即使正在调用被监视的函数,Sinon 间谍也会失败
Sinon spy fails even though function spied on is being called
我无法理解为什么 sinon 间谍对我来说失败了,即使我正在监视的函数在我的测试期间确实被调用了(我用一些简单的控制台日志记录证明了这一点)。
所以说我有如下内容:
index.js
let MyModule = require('./src/index.js');
MyModule = new MyModule();
module.exports = {
DoStuff: MyModule.DoStuff,
doOtherStuff: MyModule.doOtherStuff,
};
src/index.js
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 函数输出中的控制台日志,但测试失败说间谍被调用零次。
我想知道这是否归因于我正在测试的代码的异步性质,但我已确保在我的测试中使用 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;
所以这将排除我可能遇到的任何 async/await 问题。
但即使 运行 下面的简单测试,间谍也永远不会被调用:
const MyModule = require('../index.js');
it('should call doOtherStuff', () => {
const spy = sinon.spy(MyModule, 'doOtherStuff');
MyModule.DoStuff();
sinon.assert.calledOnce(spy);
});
如果我监视 console.log
然而它就会通过。我一定是误解了这里的一个非常基本的原则,但我不知道它是什么!
这与我的 module.exports
的申报方式有关吗?因此,即使我试图监视 index.js
(doOtherStuff: MyModule.doOtherStuff
)中的顶级导出,这不是在我的测试中调用 DoStuff 时内部实际调用的内容吗?
问题
包裹在 spy
中的 属性 不是被调用的 属性。
详情
sinon.spy
获取一个对象和一个 属性 名称并将函数包装在该 属性 名称处的间谍中。
在这种情况下,对象是 index.js
的模块导出。
模块导出是一个具有两个属性的对象,这些属性指向在 index.js
中创建的内部 MyModule
实例上的方法。所以那个对象的 doOtherStuff
属性 现在是 spy
而 DoStuff
属性 仍然只是对 DoStuff
[=65 的引用=] 内部 MyModule
实例。
当测试随后调用 MyModule.DoStuff()
时,它调用内部 MyModule
实例的 DoStuff
属性,后者调用 doOtherStuff
属性记录到控制台的内部 MyModule
实例。
关键是直接调用了内部MyModule
实例的doOtherStuff
属性和导出对象的doOtherStuff
属性 index.js
从未调用过。
doOtherStuff
属性 上的 doOtherStuff
index.js
导出的对象的 spy
然后正确断言它被调用了 0 次。
解决方案
确保 spy
是在实际调用的 属性 上创建的。
在这种情况下,最简单的方法是直接从 index.js
:
导出 MyModule
实例
let MyModule = require('./src/index.js');
MyModule = new MyModule();
module.exports = MyModule;
现在,当创建 spy
时,它是直接在内部 MyModule
实例的 doOtherStuff
属性 上创建的,并且会正确地报告它被调用了一次。
我无法理解为什么 sinon 间谍对我来说失败了,即使我正在监视的函数在我的测试期间确实被调用了(我用一些简单的控制台日志记录证明了这一点)。
所以说我有如下内容:
index.js
let MyModule = require('./src/index.js');
MyModule = new MyModule();
module.exports = {
DoStuff: MyModule.DoStuff,
doOtherStuff: MyModule.doOtherStuff,
};
src/index.js
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 函数输出中的控制台日志,但测试失败说间谍被调用零次。
我想知道这是否归因于我正在测试的代码的异步性质,但我已确保在我的测试中使用 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;
所以这将排除我可能遇到的任何 async/await 问题。
但即使 运行 下面的简单测试,间谍也永远不会被调用:
const MyModule = require('../index.js');
it('should call doOtherStuff', () => {
const spy = sinon.spy(MyModule, 'doOtherStuff');
MyModule.DoStuff();
sinon.assert.calledOnce(spy);
});
如果我监视 console.log
然而它就会通过。我一定是误解了这里的一个非常基本的原则,但我不知道它是什么!
这与我的 module.exports
的申报方式有关吗?因此,即使我试图监视 index.js
(doOtherStuff: MyModule.doOtherStuff
)中的顶级导出,这不是在我的测试中调用 DoStuff 时内部实际调用的内容吗?
问题
包裹在 spy
中的 属性 不是被调用的 属性。
详情
sinon.spy
获取一个对象和一个 属性 名称并将函数包装在该 属性 名称处的间谍中。
在这种情况下,对象是 index.js
的模块导出。
模块导出是一个具有两个属性的对象,这些属性指向在 index.js
中创建的内部 MyModule
实例上的方法。所以那个对象的 doOtherStuff
属性 现在是 spy
而 DoStuff
属性 仍然只是对 DoStuff
[=65 的引用=] 内部 MyModule
实例。
当测试随后调用 MyModule.DoStuff()
时,它调用内部 MyModule
实例的 DoStuff
属性,后者调用 doOtherStuff
属性记录到控制台的内部 MyModule
实例。
关键是直接调用了内部MyModule
实例的doOtherStuff
属性和导出对象的doOtherStuff
属性 index.js
从未调用过。
doOtherStuff
属性 上的 doOtherStuff
index.js
导出的对象的 spy
然后正确断言它被调用了 0 次。
解决方案
确保 spy
是在实际调用的 属性 上创建的。
在这种情况下,最简单的方法是直接从 index.js
:
MyModule
实例
let MyModule = require('./src/index.js');
MyModule = new MyModule();
module.exports = MyModule;
现在,当创建 spy
时,它是直接在内部 MyModule
实例的 doOtherStuff
属性 上创建的,并且会正确地报告它被调用了一次。