Donnie D'Amato
Donnie D'Amato

Reputation: 3940

Unit Testing a nodejs function using `fs` wrapped in `promisify`

In the following code I have a function that is looking for a file in the file system based on a provided configuration.

const fs = require('fs');
const { promisify } = require('util');

const lstat = promisify(fs.lstat);

async function doSomething(someFilePath) {
    try {
       const stats = await lstat(someFilePath);
    } catch (err) {
       throw err;
    }

    // do something with the file stats
}

module.exports = doSomething;

From here I'm trying to test the doSomething function but it fails because the file paths I'm providing don't actually exist. The below code was working when I was using lstatSync without promisify.

const fs = require('fs');
const sinon = require('sinon');

const doSomething = require('./doSomething');

describe('The test', function() {

    let lstatStub;

    beforeEach(function() {
        lstatStub = sinon.stub(fs, 'lstatSync');
    });

    afterEach(function() { sinon.restore() });

    it('should pass', async function() {
        lstatStub.withArgs('image.jpg').returns({
            isFile: () => true,
            isDirectory: () => false,
        });

        assert(await doSomething('image.jpg')).ok();
    });

})

It now fails because Error: ENOENT: no such file or directory, lstat 'image.jpg'. I've tried wrapping the stub in promisify or exporting the promisifyed functions into the test to stub. Both didn't work.

How do I stub a promisifyed fs method?

Upvotes: 0

Views: 686

Answers (1)

Lin Du
Lin Du

Reputation: 102527

You can use Link Seams and proxyquire package to stub promisify function.

E.g.

main.js:

const fs = require('fs');
const { promisify } = require('util');

const lstat = promisify(fs.lstat);

async function doSomething(someFilePath) {
  return await lstat(someFilePath);
}

module.exports = doSomething;

main.test.js:

const sinon = require('sinon');
const proxyquire = require('proxyquire');
const { expect } = require('chai');

describe('61412987', () => {
  it('should pass', async () => {
    const lstatStub = sinon.stub().resolves('fake data');
    const utilStub = { promisify: sinon.stub().callsFake(() => lstatStub) };
    const doSomethng = proxyquire('./main', {
      util: utilStub,
    });

    const actual = await doSomethng('/root/avatar.jpg');
    expect(actual).to.be.equal('fake data');
    sinon.assert.calledOnce(utilStub.promisify);
    sinon.assert.calledWithExactly(lstatStub, '/root/avatar.jpg');
  });
});

unit test results with 100% coverage:

  61412987
    ✓ should pass (1894ms)


  1 passing (2s)

----------|---------|----------|---------|---------|-------------------
File      | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s 
----------|---------|----------|---------|---------|-------------------
All files |     100 |      100 |     100 |     100 |                   
 main.js  |     100 |      100 |     100 |     100 |                   
----------|---------|----------|---------|---------|-------------------

source code: https://github.com/mrdulin/expressjs-research/tree/master/src/stackoverflow/61412987

Upvotes: 0

Related Questions