在Karma和Jasmine中调用setTimeout和clearTimeout

时间:2016-01-29 08:47:39

标签: javascript unit-testing jasmine karma-jasmine

我似乎无法在我的Jasmine测试中窥探setTimeoutclearTimeout,这些测试正在通过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已调用clearTimeoutrunMyCode,但我总是Expected spy setTimeout to have been called.

对于window,显然这是因为测试和跑步者(Karma窗口)处于不同的帧中(所以为什么我期望有什么不同)。但正因为如此,我无法确定是否有任何方法可以确认这些全局函数已被调用。

我知道我可以使用jasmine.clock()来确认已经调用了超时/间隔回调,但看起来我看不到setTimeout本身。并且确认简单地调用clearTimeout是不可能的。

此时,我唯一能想到的是添加一个单独的抽象层来包装setTimeoutclearTimeout或者将函数作为依赖项注入,我以前做过,但我觉得很奇怪。

3 个答案:

答案 0 :(得分:5)

我能够让它像这样工作:

spyOn(window, 'setTimeout');
runMyCode();
expect(setTimeout).toHaveBeenCalled();

只需从setTimeout调用中删除'window'对象。

答案 1 :(得分:3)

对于那些寻找Jest解决方案的人来说,它有专门的fake timer functions(也是可以窥探的)。

答案 2 :(得分:0)

编辑:自从提出这个问题以来,看起来Jasmine已经实现了Clock,这使得这种模拟成为可能。而且,正如Piotr Jaworski的回答所指出的那样,Facebook的基于Jasmine的Jest提供了自己的(可以说是更好的)模拟和监视定时任务的方式。

所以,答案的其余部分已经过时了......

我能找到的唯一且唯一的解决方案是使用Rewire(在我的情况下,我还需要使用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)时停止工作。如果这对你不起作用,请发表评论,以便将来的开发者知道。