Home > OS >  How to mock file I/O in sinon?
How to mock file I/O in sinon?

Time:10-20

I have a function which unzip a file form the directory. It's working fine

index.js

const unZip = async (zipFilePath, destDir) => {
  await util.promisify(fs.mkdir)(destDir);

  return new Promise((resolve, reject) => {
    fs.createReadStream(zipFilePath)
      .pipe(unzipper.Extract({ path: destDir }))
      .on("close", () => resolve(destDir))
      .on("error", (err) => {
        console.log("Error inside unzip", err);
        reject(err);
      });
  });
};

but for unit test i am using sinon and ava, where I am unable to pass the test case here is the code

index.test.js

ava.beforeEach(() => {
  // mockFs({
  //   'fakeDir/fakeFile': mockFs.load('test/helpers/file/testFile.txt'),
  //   fakeFileContent: 'content here',
  // });
  sinon.stub(mockFs, 'createReadStream').returns({
    pipe: sinon.stub().returns({
      on: sinon.stub().returns({
        on: sinon.stub().returns(),
      }),
    }),
  });
});

ava.serial('unZip test', async (t) => {
  const unzip = proxyquire('../../../src/helpers/file/unZip', {
    fs: mockFs,
    util: {},
    unzipper: { Extract: () => Buffer.from([8, 6, 7, 5, 3, 0, 9]) },
  });

  const result = await unzip('fakeFileContent', 'fakeFileContent');

  t.is(result, true);
});

it's giving me the error like this

  unZip test

  Rejected promise returned by test. Reason:

  Error {
    code: 'EEXIST',
    errno: -17,
    path: 'fakeFileContent',
    syscall: 'mkdir',
    message: 'EEXIST: file already exists, mkdir \'fakeFileContent\'',
  }

CodePudding user response:

You don't need to use proxyquire package, use sinon.stub(obj, 'method') to stub methods of object. You can stub fs.mkdir, unzipper.Extract and fs.createReadStream methods.

You use util.promisify to convert fs.mkdir into a promise form and call it, but underly is still the callback being called, so you need to use the .callsFake() method to mock implementation for fs.mkdir, and call the callback manually in the test case.

The below example uses mocha as testing framework, but ava should also be fine.

index.js:

const fs = require('fs');
const util = require('util');
const unzipper = require('unzipper');

const unZip = async (zipFilePath, destDir) => {
  await util.promisify(fs.mkdir)(destDir);

  return new Promise((resolve, reject) => {
    fs.createReadStream(zipFilePath)
      .pipe(unzipper.Extract({ path: destDir }))
      .on('close', () => resolve(destDir))
      .on('error', (err) => {
        console.log('Error inside unzip', err);
        reject(err);
      });
  });
};

module.exports = unZip;

index.test.js:

const unZip = require('./');
const fs = require('fs');
const sinon = require('sinon');
const unzipper = require('unzipper');

describe('69616649', () => {
  afterEach(() => {
    sinon.restore();
  });
  it('should pass', async () => {
    sinon.stub(fs, 'mkdir').callsFake((path, callback) => {
      callback();
    });
    const rs = {
      pipe: sinon.stub().returnsThis(),
      on: sinon.stub().callsFake(function (event, callback) {
        if (event === 'close') {
          callback();
        }
      }),
    };
    sinon.stub(fs, 'createReadStream').returns(rs);
    sinon.stub(unzipper, 'Extract');
    const actual = await unZip('fakeFileContent', 'fakeFileContent');
    sinon.assert.match(actual, 'fakeFileContent');
    sinon.assert.calledWithExactly(fs.mkdir, 'fakeFileContent', sinon.match.func);
    sinon.assert.calledWithExactly(fs.createReadStream, 'fakeFileContent');
    sinon.assert.calledWithExactly(unzipper.Extract, { path: 'fakeFileContent' });
    sinon.assert.calledOnce(rs.pipe);
    sinon.assert.calledWithExactly(rs.on, 'close', sinon.match.func);
  });
});

test result:

  69616649
    ✓ should pass


  1 passing (7ms)

----------|---------|----------|---------|---------|-------------------
File      | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s 
----------|---------|----------|---------|---------|-------------------
All files |   81.82 |      100 |      75 |   81.82 |                   
 index.js |   81.82 |      100 |      75 |   81.82 | 13-14             
----------|---------|----------|---------|---------|-------------------

CodePudding user response:

I am new to ava so may be it's wrong

proxyquire('../../../src/helpers/file/unZip')// actual function file

ava('69616649', () => {
  ava.afterEach(() => {
    sinon.restore();
  });
  ava.serial('should pass', async () => {
    sinon.stub(fs, 'mkdir').callsFake((path, callback) => {
      callback();
    });
    const rs = {
      pipe: sinon.stub().returnsThis(),
      on: sinon.stub().callsFake(function (event, callback) {
        if (event === 'close') {
          callback();
        }
      }),
    };
    sinon.stub(fs, 'createReadStream').returns(rs);
    sinon.stub(unzipper, 'Extract');
    const actual = await unZip('fakeFileContent', 'fakeFileContent');
    sinon.assert.match(actual, 'fakeFileContent');
    sinon.assert.calledWithExactly(
      fs.mkdir,
      'fakeFileContent',
      sinon.match.func
    );
    sinon.assert.calledWithExactly(fs.createReadStream, 'fakeFileContent');
    sinon.assert.calledWithExactly(unzipper.Extract, {
      path: 'fakeFileContent',
    });
    sinon.assert.calledOnce(rs.pipe);
    sinon.assert.calledWithExactly(rs.on, 'close', sinon.match.func);
  });
});
  • Related