在我的一个单元测试中,我需要监视一个函数,这个函数被另一个函数用作构造函数,它的代码是 Sinon
库。根据他们的文件
...
sinon.spy(object, "method")
创建一个封装现有函数 object.method 的 spy。这个窥视将与原始方法的行为完全相同(包括作为构造函数使用时)... 但到目前为止,我还没能让它工作,即使是在测试函数中调用的构造函数上进行监视,更不用说被其他函数调用了。
单元测试。
it('constructor was called.', () => {
const Foo = require('../app/foo');
const fooModule = module.children.find(m => m.id.includes('foo.js'));
const fooSpy = sinon.spy(fooModule, 'exports');
const f = new Foo(5);
expect(fooSpy).calledOnce;
});
要实例化的函数:
const Foo = function(param) {
console.log('Foo called with: ' + param);
};
Foo.prototype.bar = function(x) {
console.log(`Foo.prototype.bar() called with x: ` + x);
};
module.exports = Foo;
当我使用调试器时,我可以看到函数在... const fooSpy = sinon.spy(fooModule, 'exports');
被间谍取代(已全部全部)。sinon
属性添加如 calledOnce
诸如此类......)然而,当在 new Foo(5);
看来 Foo
是一个非间谍对象。
我想这可能是一个范围或引用错误,但我似乎找不到其他的地方了。Foo
将会被定义为以外的 module.children
. 它不在 global
既不是其上 window
因为其运行在 node
.
目前当然测试失败与。
Foo called with: 5
AssertionError: expected exports to have been called exactly once, but it was called 0 times
at Context.it (test/fooTest.js:18:23)
先谢谢你的帮助
你的问题不是真正的 sinon.spy
API,但与Node模块如何导入函数。当调用 sinon.spy
除非我们正在测试一个回调函数,否则我们通常需要一个对象作为我们想要监视某个特定方法的上下文。这就是为什么你的例子试图访问的是 exports
目标 foo.js
模块。我不知道Node是否允许我们访问这样一个对象。
然而,为了让你的例子有效,我们不需要访问Foo模块的 exports
,我们可以简单地创建一个自己的上下文。比如说,我们可以自己创建一个上下文。
const chai = require("chai");
const sinon = require("sinon");
const sinonChai = require("sinon-chai");
const expect = chai.expect;
chai.use(sinonChai);
describe("foo", function () {
it('constructor was called.', function () {
const context = {
Foo: require("../app/foo"),
};
const fooSpy = sinon.spy(context, "Foo");
new context.Foo(5);
expect(fooSpy).to.be.calledOnceWith(5);
});
});
当然,上面的测试是解决你的例子中所提供的问题的有效方法 但是,作为一个测试,它并不是很有用 因为这个断言只是验证了上面的一行。
当间谍的作用比较大的时候,他们是 依赖性 的测试系统(SUT)。换句话说,如果我们有一些模块应该构建一个 Foo
,我们要使 Foo
构造函数一个Spy,这样它就可以向我们的测试报告模块确实调用了它。
比如说,我们有 fooFactory.js
模块。
const Foo = require("./foo");
module.exports = {
createFoo(num) {
return new Foo(num);
},
};
现在,我们想创建一个单元测试,以确认在调用 createFoo
的作用 fooFactory.js
模块调用 Foo
的构造函数。我们需要覆盖 fooFactory.js
's Foo
依赖关系的Spy。
这就回到了我们最初的问题,即当一个导入的(构造函数)Function不是上下文对象上的方法时,我们如何将它变成一个spy,所以我们不能用 sinon.spy(context, 'method')
.
幸运的是,我们不是第一个遇到这个问题的人。NPM模块的存在允许覆盖所需模块的依赖关系。Sinon.js提供了一个 操作方法 他们使用了一个叫做 代理查询.
proxyquire将允许我们导入 fooFactory.js
模块到我们的单元测试中,但也(更重要的是)到 凌驾于 Foo
它取决于. 这将使我们的单元测试能够使 fooFactory.js
用 sinon.spy
代替 Foo
构造函数。
测试文件变成。
const chai = require("chai");
const proxyquire = require("proxyquire");
const sinon = require("sinon");
const sinonChai = require("sinon-chai");
const expect = chai.expect;
chai.use(sinonChai);
describe("fooFactory", function () {
it("calls Foo constructor", function () {
const fooSpy = sinon.spy();
const { createFoo } = proxyquire("../app/fooFactory", {
"./foo": fooSpy,
});
createFoo(5);
expect(fooSpy).to.be.calledOnceWith(5);
});
});