Javascript 监视用作构造函数的函数
在我的一个单元测试中,我需要监视一个函数,该函数被另一个具有Javascript 监视用作构造函数的函数,javascript,node.js,unit-testing,sinon,Javascript,Node.js,Unit Testing,Sinon,在我的一个单元测试中,我需要监视一个函数,该函数被另一个具有Sinonlibrary的函数用作构造函数。根据他们的文件 …sinon.spy(object,“method”)创建一个封装现有函数object.method的spy。spy的行为与原始方法完全相同(包括用作构造函数时)。。。 但到目前为止,即使我试图监视在测试函数中调用的构造函数,更不用说由另一个函数调用的构造函数,我也无法使它工作 单元测试: it('constructor was called.', () => {
Sinon
library的函数用作构造函数。根据他们的文件
…sinon.spy(object,“method”)
创建一个封装现有函数object.method的spy。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;
当我使用调试器时,我可以看到函数位于constfoospy=sinon.spy(fooModule,'exports')
被spy替换(添加了所有的sinon
属性,如calledOnce
等…),但是当启用new Foo(5)时代码>似乎Foo
是一个非间谍对象
我认为这可能是一个作用域或引用错误,但我似乎找不到除了在模块中定义Foo
以外的其他地方。children
。它既不在全局
上,也不在窗口
上,因为它在节点
上运行
目前,测试当然失败,原因是:
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,而是关于节点模块如何导入函数。调用sinon.spy
时,除非我们正在测试回调函数,否则我们通常需要一个对象作为我们想要监视特定方法的上下文。这就是为什么您的示例尝试访问foo.js
模块的exports
对象。我不知道节点允许我们访问这样的对象
然而,为了让您的示例发挥作用,我们不需要访问Foo模块的导出
,我们只需创建自己的上下文即可。例如:
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);
});
});
当然,上面的测试是示例中提供的问题的有效解决方案,但是,作为测试,它不是很有用,因为断言只是验证上面的行
当SPIE是被测系统(SUT)的依赖项时,它们更有用。换句话说,如果我们有一个模块应该构造一个Foo
,我们想让Foo
构造函数成为一个间谍,这样它就可以向我们的测试报告该模块确实调用了它
例如,假设我们有foodfactory.js
模块:
const Foo = require("./foo");
module.exports = {
createFoo(num) {
return new Foo(num);
},
};
现在,我们想创建一个单元测试,以确认调用fooFactory.js
模块的createFoo
函数调用带有指定参数的Foo
构造函数。我们需要用间谍覆盖fooFactory.js
的Foo
依赖关系
这让我们回到了最初的问题,即当导入的(构造函数)函数不是上下文对象上的方法,因此无法用sinon.spy(context,'method')
覆盖它时,如何将其转换为spy
幸运的是,我们不是第一个遇到这个问题的人。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);
});
});
我认为你做不到。需要/导入的模块与正在测试的代码处于不同的级别。测试代码运行时,模块已加载。您对模块没有相同的访问权限。导出
与您对当前执行上下文中引用的对象所做的操作相同,例如Foo
,f
。非常感谢<代码>proxyquire
正是我所需要的。