简体   繁体   中英

How to stub a promisified method using Sinon.JS?

Given the following code: (Note that access was promisified)

const fs = require('fs')
const util = require('util')
const access = util.promisify(fs.access)

const custom = () => {
  return access('myfile', fs.constants.R_OK)
    .then(() => {
      return true
    }).catch(err => {
      if (err.code !== 'ENOENT') throw err
      return false
    }).then(fileExists => {
      return `File exists: ${fileExists}`
    })
}

module.exports = custom

My unit test is not working because the real access method is being called instead of the stubbed one:

const chai = require('chai')
const chaiAsPromised = require('chai-as-promised')
const custom = require('./custom')
const fs = require('fs')
const sinon = require('sinon')

chai.use(chaiAsPromised)
const expect = chai.expect

describe('custom', () => {
  let sandbox = sinon.createSandbox()

  afterEach(function () {
    sandbox.restore()
  })

  it('should return file exists true', () => {
    sandbox.stub(fs, 'access').yields(null)
    return expect(custom()).to.eventually.equal('File exists: true')
  })

  it('should return file exists false', () => {
    const error = new Error('No such file or directory')
    error.code = 'ENOENT'
    sandbox.stub(fs, 'access').yields(error)
    return expect(custom()).to.eventually.equal('File exists: false')
  })
})

How to make the test works, givem access is promisified in the custom.js file?

References:

Update Question has been updated. Answer to original question at the bottom

What you are looking for is Proxyquire . The problem is that custom.js requires its own fs , and you are stubbing the fs in your test file. Proxyquire will require a file, and replace any files/functions you want, which that file requires.

In your example, it would go something like this. (run it with mocha <test file path> )

test file:

const chai = require('chai');
const chaiAsPromised = require('chai-as-promised');
const proxyquire = require('proxyquire');

chai.use(chaiAsPromised);
const expect = chai.expect;

describe('custom', () => {
    let custom;

    const stubs = {
        fs: {
            accessAsync: () => {
                // Since you mentioned that fs was promisified, we will
                // make this a promise as well.
                return Promise.resolve();
            }
        },
        bluebird: {
            // Stop bluebird from promisifying, since we handle it above.
            promisifyAll: (req => req),
        }
    };

    before(async function () {
        custom = proxyquire('./custom', stubs);
    });

    it('should return file exists true', () => {
        return expect(custom()).to.eventually.equal('File exists: true');
    });
});

custom.js

'use strict';

const bluebird = require('bluebird');
const fs = bluebird.promisifyAll(require('fs'));

module.exports = async function() {
    let exists = true;
    await fs.accessAsync('nonimportant.path', fs.constants.F_OK)
        .catch( () => { exists = false; });
    return `File exists: ${exists}`;
};

Original answer

No need to use bluebird in this case, Sinon can handle that.

You need to use the sinon.stub() function. It can be done like this:

const chai = require('chai');
const chaiAsPromised = require('chai-as-promised');
const sinon = require('sinon');
const fs = require('fs');

chai.use(chaiAsPromised);
const expect = chai.expect;

describe('fs.stat', function(){
    before(function() {
        sinon.stub(fs, 'stat').resolves('Your desired value');
    });

    it('should should be called', function(done){
        expect(fs.stat('./some.file'))
            .to.eventually.equal('Your desired value')
            .notify(done);
    });
});

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM