web-dev-qa-db-ja.com

KarmaとJasmineのsetTimeoutとclearTimeoutをスパイする

Karmaで実行されているJasmineテストでは、setTimeoutclearTimeoutをスパイできないようです。

私はこれらすべてのバリエーションを試しました

_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
_

いずれの場合も、setTimeoutclearTimeoutrunMyCodeで呼び出されたことを確認できますが、代わりに常に_Expected spy setTimeout to have been called._を取得します。

windowの場合、これは明らかに、テストとランナー(Karmaウィンドウ)が異なるフレームにあるためです(したがって、なぜ異なるものを期待する必要があるのですか)。しかし、このため、これらのグローバル関数が呼び出されたことを確認する方法がわかりません。

jasmine.clock()を使用して、タイムアウト/間隔のコールバックが呼び出されたことを確認できることは知っていますが、setTimeout自体を監視できないようです。そして、clearTimeoutが呼び出されたことを確認することは単に不可能です。

この時点で、私が考えることができる唯一のことは、setTimeoutclearTimeoutをラップするために別の抽象化レイヤーを追加するか、以前に行った依存関係として関数を挿入することですが、変だと思います。

11
Andrew

編集:この質問をしたので、Jasmineが Clock を実装したように見えます。これにより、この種のモックが可能になります。そして、Piotr Jaworskiの回答が指摘しているように、Facebookのジャスミンベースの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)。これがうまくいかない場合は、コメントを残して、将来の開発者がわかるようにしてください。

0
Andrew

私はそれをこのように動作させることができました:

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

SetTimeout呼び出しから「window」オブジェクトを削除するだけです。

8
Bobby

Jestソリューションをお探しの方のために、専用の 偽のタイマー関数 (これもスパイ可能です)があります。

3
Piotr Jaworski