监视 Karma 和 Jasmine 中的 setTimeout 和 clearTimeout
Spy on setTimeout and clearTimeout in Karma and Jasmine
我似乎无法在我的 Jasmine 测试中监视 setTimeout
和 clearTimeout
,这些测试正在通过 Karma 运行。
我已经尝试了所有这些的变体
spyOn(window, 'setTimeout').and.callFake(()=>{});
spyOn(global, 'setTimeout').and.callFake(()=>{});
spyOn(window, 'clearTimeout').and.callThrough();
clock = jasmine.clock();
clock.install();
spyOn(clock, 'setTimeout').and.callThrough();
runMyCode();
expect(window.setTimeout).toHaveBeenCalled(); // no
expect(global.setTimeout).toHaveBeenCalled(); // nope
expect(window.clearTimeout).toHaveBeenCalled(); // no again
expect(clock.setTimeout).toHaveBeenCalled(); // and no
在每种情况下,我都可以确认 setTimeout
和 clearTimeout
已在 runMyCode
中被调用,但我总是得到 Expected spy setTimeout to have been called.
对于 window
,显然这是因为测试和 运行ner(业力 window)在不同的框架中(所以我为什么要期待任何不同)。但是正因为如此,我看不到任何方法来确认这些全局函数已经被调用。
我知道我可以使用jasmine.clock()
来确认timeout/interval 回调已被调用,但看起来我无法观看setTimeout
本身。并确认 clearTimeout
已被调用是不可能的。
在这一点上,我唯一能想到的就是添加一个单独的抽象层来包装 setTimeout
和 clearTimeout
或者将函数作为依赖项注入,我已经做到了之前,但我觉得很奇怪。
编辑:自问这个问题以来,Jasmine 似乎已经实现了 Clock,这使得这种模拟成为可能。而且,正如 Piotr Jaworski 的回答所指出的,Facebook 基于 Jasmine 的 Jest 提供了自己的(可以说是更好的)模拟和监视定时任务的方式。
因此,其余答案已过时....
我能找到的唯一 - 也是唯一 - 解决方案是使用 Rewire (in my case, I am required to also use Rewire-Webpack).
Rewire 确实允许您替换全局方法 -- 但是一旦替换了方法,就无法对其进行监视。因此,要真正成功地使用 toHaveBeenCalledWith
,您必须包装并代理模拟函数。
var rewire = require('rewire'),
myModule = rewire('./path/to/module');
describe(function () {
var mocks = {
setTimeout: function () { return 99: },
clearTimeout: function () {}
};
beforeEach(function () {
// This will work
myModule.__set__('setTimeout', function () {
mocks.setTimeout.apply(null, arguments)
})
// This will NOT work
myModule.__set__('clearTimeout', mocks.clearTimeout)
});
it('calls setTimeout', function () {
spyOn(mocks, 'setTimeout').and.callThrough();
spyOn(mocks, 'clearTimeout').and.callThrough();
myModule.doSomething(); // this will invoke setTimeout locally
expect(mocks.setTimeout).toHaveBeenCalledWith(jasmine.any(Function), 1000);
expect(mocks.clearTimeout).toHaveBeenCalledWith(99); // Won't work (see above)
});
});
当然,下次 Jasmine、Rewire、Karma、Webpack ......或天气......变化时,这肯定会停止工作(grrr)。如果这对您不起作用,请发表评论,以便未来的开发人员知道。
我能够让它像这样工作:
spyOn(window, 'setTimeout');
runMyCode();
expect(setTimeout).toHaveBeenCalled();
只需从 setTimeout 调用中删除 'window' 对象。
对于那些寻找 Jest 解决方案的人,它专门提供了 fake timer functions(这也是可监视的)。
我似乎无法在我的 Jasmine 测试中监视 setTimeout
和 clearTimeout
,这些测试正在通过 Karma 运行。
我已经尝试了所有这些的变体
spyOn(window, 'setTimeout').and.callFake(()=>{});
spyOn(global, 'setTimeout').and.callFake(()=>{});
spyOn(window, 'clearTimeout').and.callThrough();
clock = jasmine.clock();
clock.install();
spyOn(clock, 'setTimeout').and.callThrough();
runMyCode();
expect(window.setTimeout).toHaveBeenCalled(); // no
expect(global.setTimeout).toHaveBeenCalled(); // nope
expect(window.clearTimeout).toHaveBeenCalled(); // no again
expect(clock.setTimeout).toHaveBeenCalled(); // and no
在每种情况下,我都可以确认 setTimeout
和 clearTimeout
已在 runMyCode
中被调用,但我总是得到 Expected spy setTimeout to have been called.
对于 window
,显然这是因为测试和 运行ner(业力 window)在不同的框架中(所以我为什么要期待任何不同)。但是正因为如此,我看不到任何方法来确认这些全局函数已经被调用。
我知道我可以使用jasmine.clock()
来确认timeout/interval 回调已被调用,但看起来我无法观看setTimeout
本身。并确认 clearTimeout
已被调用是不可能的。
在这一点上,我唯一能想到的就是添加一个单独的抽象层来包装 setTimeout
和 clearTimeout
或者将函数作为依赖项注入,我已经做到了之前,但我觉得很奇怪。
编辑:自问这个问题以来,Jasmine 似乎已经实现了 Clock,这使得这种模拟成为可能。而且,正如 Piotr Jaworski 的回答所指出的,Facebook 基于 Jasmine 的 Jest 提供了自己的(可以说是更好的)模拟和监视定时任务的方式。
因此,其余答案已过时....
我能找到的唯一 - 也是唯一 - 解决方案是使用 Rewire (in my case, I am required to also use Rewire-Webpack).
Rewire 确实允许您替换全局方法 -- 但是一旦替换了方法,就无法对其进行监视。因此,要真正成功地使用 toHaveBeenCalledWith
,您必须包装并代理模拟函数。
var rewire = require('rewire'),
myModule = rewire('./path/to/module');
describe(function () {
var mocks = {
setTimeout: function () { return 99: },
clearTimeout: function () {}
};
beforeEach(function () {
// This will work
myModule.__set__('setTimeout', function () {
mocks.setTimeout.apply(null, arguments)
})
// This will NOT work
myModule.__set__('clearTimeout', mocks.clearTimeout)
});
it('calls setTimeout', function () {
spyOn(mocks, 'setTimeout').and.callThrough();
spyOn(mocks, 'clearTimeout').and.callThrough();
myModule.doSomething(); // this will invoke setTimeout locally
expect(mocks.setTimeout).toHaveBeenCalledWith(jasmine.any(Function), 1000);
expect(mocks.clearTimeout).toHaveBeenCalledWith(99); // Won't work (see above)
});
});
当然,下次 Jasmine、Rewire、Karma、Webpack ......或天气......变化时,这肯定会停止工作(grrr)。如果这对您不起作用,请发表评论,以便未来的开发人员知道。
我能够让它像这样工作:
spyOn(window, 'setTimeout');
runMyCode();
expect(setTimeout).toHaveBeenCalled();
只需从 setTimeout 调用中删除 'window' 对象。
对于那些寻找 Jest 解决方案的人,它专门提供了 fake timer functions(这也是可监视的)。