Node.js 即使正在调用被监视的函数,Sinon spy也会失败
我很难理解为什么sinon spy对我来说失败了,即使我所监视的函数在测试期间被调用(我用一些简单的控制台日志记录证明了这一点) 假设我有以下几点:Node.js 即使正在调用被监视的函数,Sinon spy也会失败,node.js,mocha.js,sinon,chai,Node.js,Mocha.js,Sinon,Chai,我很难理解为什么sinon spy对我来说失败了,即使我所监视的函数在测试期间被调用(我用一些简单的控制台日志记录证明了这一点) 假设我有以下几点: const nock = require('nock'); const sinon = require('sinon'); const MyModule = require('../index.js'); describe('When calling DoStuff succeeds in making the xhr call', () =>
const nock = require('nock');
const sinon = require('sinon');
const MyModule = require('../index.js');
describe('When calling DoStuff succeeds in making the xhr call', () => {
before(() => {
nock(apiHostName)
.post('/some-path')
.reply(200, { foo: 'bar' });
});
it('should call doOtherStuff', async () => {
const spy = sinon.spy(MyModule, 'doOtherStuff');
await MyModule.DoStuff();
sinon.assert.calledOnce(spy);
});
});
index.js
let MyModule = require('./src/index.js');
MyModule = new MyModule();
module.exports = {
DoStuff: MyModule.DoStuff,
doOtherStuff: MyModule.doOtherStuff,
};
const MyModule = function MyModule() {
const self = this;
self.doOtherStuff = function doOtherStuff() {
console.log('doOtherStuff called!!!')
}
self.DoStuff = async function DoStuff() {
const xhr = self.axiosInstance();
await xhr.post()
.then((res) => {
self.doOtherStuff(res.data);
})
.catch((_err) => {
console.log(_err);
});
};
}
module.exports = MyModule;
src/index.js
let MyModule = require('./src/index.js');
MyModule = new MyModule();
module.exports = {
DoStuff: MyModule.DoStuff,
doOtherStuff: MyModule.doOtherStuff,
};
const MyModule = function MyModule() {
const self = this;
self.doOtherStuff = function doOtherStuff() {
console.log('doOtherStuff called!!!')
}
self.DoStuff = async function DoStuff() {
const xhr = self.axiosInstance();
await xhr.post()
.then((res) => {
self.doOtherStuff(res.data);
})
.catch((_err) => {
console.log(_err);
});
};
}
module.exports = MyModule;
我的测试如下:
const nock = require('nock');
const sinon = require('sinon');
const MyModule = require('../index.js');
describe('When calling DoStuff succeeds in making the xhr call', () => {
before(() => {
nock(apiHostName)
.post('/some-path')
.reply(200, { foo: 'bar' });
});
it('should call doOtherStuff', async () => {
const spy = sinon.spy(MyModule, 'doOtherStuff');
await MyModule.DoStuff();
sinon.assert.calledOnce(spy);
});
});
我在我的测试运行程序输出中看到了doOtherStuff函数输出中的控制台日志,但是测试失败,说spy被调用了零次
我想知道这是否与我正在测试的代码的异步性质有关,但我已经确保在测试中使用async/await来实现。我一定是做了什么蠢事,我哪里做错了
谢谢
更新
因此,我尝试将函数剥离回更基本的内容,现在有以下内容:
const MyModule = function MyModule() {
const self = this;
self.doOtherStuff = function doOtherStuff() {
console.log('doOtherStuff called!!!')
}
self.DoStuff = function DoStuff() {
self.doOtherStuff();
};
}
module.exports = MyModule;
因此,这将排除我可能遇到的任何异步/等待问题
但即使在运行以下简单测试时,间谍也不会被调用:
const MyModule = require('../index.js');
it('should call doOtherStuff', () => {
const spy = sinon.spy(MyModule, 'doOtherStuff');
MyModule.DoStuff();
sinon.assert.calledOnce(spy);
});
如果我监视console.log
,它就会通过。我一定是误解了一个非常基本的原则,但我不知道它是什么
这是否与我的模块导出的声明方式有关?因此,即使我试图在我的index.js
(thedoOtherStuff:MyModule.doOtherStuff
)中监视顶级导出,但在我的测试中调用DoStuff时,这并不是实际在内部调用的吗 发行
spy
中包装的属性不是被调用的属性
细节
sinon.spy
获取一个对象和一个属性名,并将函数包装在spy中该属性名处
在本例中,对象是index.js
的模块导出
模块导出是一个具有两个属性的对象,它们指向在index.js
中创建的内部MyModule
实例上的方法。因此,该对象的doOtherStuff
属性现在是spy
,而DoStuff
属性仍然只是内部MyModule
实例的DoStuff
属性的引用
然后,当测试调用MyModule.DoStuff()
时,它调用内部MyModule
实例的DoStuff
属性,该实例调用登录到控制台的内部MyModule
实例的doOtherStuff
属性
关键是内部MyModule
实例的doOtherStuff
属性被直接调用,而index.js
导出的对象的doOtherStuff
属性从未被调用
然后,由index.js
导出的对象的doOtherStuff
属性上的spy
正确地断言它被调用了0次
解决方案
确保在实际调用的属性上创建了spy
在这种情况下,最简单的方法是直接从index.js
导出MyModule
实例:
let MyModule = require('./src/index.js');
MyModule = new MyModule();
module.exports = MyModule;
现在,当创建spy
时,它直接在内部MyModule
实例的doOtherStuff
属性上创建,并将正确报告它被调用过一次。问题
spy
中包装的属性不是被调用的属性
细节
sinon.spy
获取一个对象和一个属性名,并将函数包装在spy中该属性名处
在本例中,对象是index.js
的模块导出
模块导出是一个具有两个属性的对象,它们指向在index.js
中创建的内部MyModule
实例上的方法。因此,该对象的doOtherStuff
属性现在是spy
,而DoStuff
属性仍然只是内部MyModule
实例的DoStuff
属性的引用
然后,当测试调用MyModule.DoStuff()
时,它调用内部MyModule
实例的DoStuff
属性,该实例调用登录到控制台的内部MyModule
实例的doOtherStuff
属性
关键是内部MyModule
实例的doOtherStuff
属性被直接调用,而index.js
导出的对象的doOtherStuff
属性从未被调用
然后,由index.js
导出的对象的doOtherStuff
属性上的spy
正确地断言它被调用了0次
解决方案
确保在实际调用的属性上创建了spy
在这种情况下,最简单的方法是直接从index.js
导出MyModule
实例:
let MyModule = require('./src/index.js');
MyModule = new MyModule();
module.exports = MyModule;
现在,当创建spy
时,它将直接在内部MyModule
实例的doOtherStuff
属性上创建,并将正确报告它被调用过一次。看起来您在监视ALClient
,而不是MyModule
?是的,对不起,当我把这个代码片段放上去时,这是一个输入错误。我已经纠正了这一点,但原来的问题仍然存在。谢谢你的意思是要先创建MyModule的实例吗?在您的示例中,MyModule
没有可以监视的DoStuff
属性,也没有doOtherStuff
属性。在显示的代码中,您应该看到Sinon引发的“试图包装未定义的属性doOtherStuff”异常:。但是,如果您创建了MyModule
的实例,它可以正常工作:在测试规范文件的开头,我确实需要模块中的require
,因此这些属性在测试运行时可用。我没有在我的测试规范代码段中显示require语句,我现在已经添加了它。我怀疑