43

angular サービスの 1 つに、一定の間隔で繰り返し呼び出したい関数があります。$timeout を使用してこれを行いたいと思います。次のようになります。

var interval = 1000; // Or something

var _tick = function () {
     $timeout(function () {
        doStuff();
        _tick();
    }, interval);
};

_tick();

現時点でジャスミンでこれを単体テストする方法に困惑しています - どうすればいいですか? 私が使用する$timeout.flush()と、関数呼び出しは無期限に発生します。ジャスミンの模擬時計を使えば$timeout影響はないようです。基本的に、これを機能させることができれば、準備は万端です。

describe("ANGULAR Manually ticking the Jasmine Mock Clock", function() {
    var timerCallback, $timeout;

    beforeEach(inject(function($injector) {
        $timeout = $injector.get('$timeout');
        timerCallback = jasmine.createSpy('timerCallback');
        jasmine.Clock.useMock();
    }));

    it("causes a timeout to be called synchronously", function() {
        $timeout(function() {
            timerCallback();
        }, 100);
        expect(timerCallback).not.toHaveBeenCalled();
        jasmine.Clock.tick(101);
        expect(timerCallback).toHaveBeenCalled();
    });
});

これらの 2 つのバリエーションは機能しますが、役に立ちません。

describe("Manually ticking the Jasmine Mock Clock", function() {
    var timerCallback;

    beforeEach(function() {
        timerCallback = jasmine.createSpy('timerCallback');
        jasmine.Clock.useMock();
    });

    it("causes a timeout to be called synchronously", function() {
        setTimeout(function() {
            timerCallback();
        }, 100);
        expect(timerCallback).not.toHaveBeenCalled();
        jasmine.Clock.tick(101);
        expect(timerCallback).toHaveBeenCalled();
    });
});

describe("ANGULAR Manually flushing $timeout", function() {
    var timerCallback, $timeout;

    beforeEach(inject(function($injector) {
        $timeout = $injector.get('$timeout');
        timerCallback = jasmine.createSpy('timerCallback');
    }));

    it("causes a timeout to be called synchronously", function() {
        $timeout(function() {
            timerCallback();
        }, 100);
        expect(timerCallback).not.toHaveBeenCalled();
        $timeout.flush();
        expect(timerCallback).toHaveBeenCalled();
    });
});

前もって感謝します!

4

2 に答える 2

52

Jasmine のクロックを使用してテストを非同期にしないでください。代わりに$timeout.flush()、テストの流れを同期的に維持するために使用します。セットアップが少し難しいかもしれませんが、一度セットアップすると、テストがより速く、より制御されます。

このアプローチを使用してそれを行うテストの例を次に示します: https://github.com/angular/angular.js/blob/master/test/ngAnimate/animateSpec.js#L618

于 2013-09-14T14:45:27.317 に答える
46

@matskoの答えは、私を正しい道に導いてくれました。答えを見つけやすくするために、「完全な」ソリューションを投稿すると思いました。

テストするもの

angular.module("app").service("MyService", function() {
    return {
        methodThatHasTimeoutAndReturnsAPromise: function($q, $timeout) {
            var deferred = $q.defer();
            $timeout(function() {
                deferred.resolve(5);
            }, 2000);
            return deferred.promise;
        }
    };
});

テスト

describe("MyService", function() {
    var target,
        $timeout;
    beforeEach(inject(function(_$timeout_, MyService) {
        $timeout = _$timeout_;
        target = MyService;
    }));
    beforeEach(function(done) {
        done();
    });
    it("equals 5", function(done) {
        target.methodThatHasTimeoutAndReturnsAPromise().then(function(value) {
            expect(value).toBe(5);
            done();
        });
        $timeout.flush();
    });
});
于 2014-08-04T17:33:12.523 に答える