【问题标题】:How to stub a promisified method using Sinon.JS?如何使用 Sinon.JS 存根承诺的方法?
【发布时间】:2018-05-18 11:38:56
【问题描述】:

给定以下代码: (请注意,访问已被承诺)

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

我的单元测试不起作用,因为调用的是真正的 access 方法而不是存根方法:

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')
  })
})

如何使测试工作,给access 是在custom.js 文件中promisified?

参考资料:

【问题讨论】:

  • 这里似乎有些混乱:通过尝试测试 Promisified fs.stat 的回调,您基本上是在尝试确保 Bluebird 的 Promisify 按预期工作。不需要这样做,Bluebird 开发人员已经做到了。您现有/不存在的文件测试也已由 node.js 人员完成,因此也无需测试。可能您想披露您尝试测试的内容/原因...
  • 感谢您的回复,我用我正在尝试测试的部分逻辑更新了问题。

标签: javascript node.js mocha.js sinon chai


【解决方案1】:

更新 问题已更新。回答底部的原始问题

您正在寻找的是Proxyquire。 问题是custom.js 需要它自己的fs,而您在测试文件中存根fsProxyquire 将需要一个文件,并替换该文件所需的任何文件/功能。

在你的例子中,它会是这样的。 (使用mocha <test file path> 运行它)

测试文件:

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}`;
};

原答案

这种情况下不用蓝鸟,诗浓可以搞定。

您需要使用sinon.stub() 函数。可以这样做:

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);
    });
});

【讨论】:

  • 感谢您的回复。正如我所说, fs.stat 被承诺了,所以我需要一个例子来说明如何存根。在您的示例中,您显示 fs.stat 将在原始版本中使用:带有回调而不是承诺。
  • 嘿恩斯莱夫。真的很好的发现!非常感谢你。看起来 proxyquire 是解决方案。我正在尝试运行您的单元测试,但我从 chai 得到 2 秒的经典超时错误。 “错误:超过 2000 毫秒的超时。对于异步测试和挂钩,请确保调用了“done()”;如果返回 Promise,请确保它已解决。”应该在不调用 done 的情况下工作,因为您将 promise 返回到 Describe 方法并且正在使用 chai-as-promised。您的示例在您的环境中运行时通过了吗?
  • 不抱歉。我没有进行那个测试。这更像是一个概念证明,为您指明正确的方向。为了完整起见,我将更新一个工作示例测试。
  • 抱歉久等了,忙于工作。我将测试修复为实际工作,并包含了我一直用于测试的custom.js 文件。如果答案对您有帮助,请记住标记为已接受:)
  • 嗨恩斯莱夫。再次感谢你的回复。我也能够使用 promisifyAll of bluebird 使其工作。这在创建 new accessAsync 方法时起作用。但我不想仅仅为了使用特定的承诺方法而承诺整个包。因此,我承诺 only 访问方法。我不想放弃并尝试找到解决方案,因为我还有许多其他用于现场承诺方法的用户案例......非常感谢您的努力。我可以接受你的答案是正确的,但我仍然有这个悬而未决的问题需要调查:-(
猜你喜欢
  • 2018-06-10
  • 1970-01-01
  • 2014-01-31
  • 1970-01-01
  • 1970-01-01
  • 2018-03-06
  • 2016-12-02
  • 2017-07-12
  • 2012-01-09
相关资源
最近更新 更多