it-swarm.com.de

Unit Testing Angular Service, der $ timeout mit Jasmine's Mock Clock verwendet

Ich habe eine Funktion in einem meiner angular Dienste, die ich in regelmäßigen Abständen wiederholt aufrufen möchte. Ich würde dies gerne mit $ timeout tun. Es sieht ungefähr so ​​aus:

var interval = 1000; // Or something

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

_tick();

Ich bin ratlos, wie ich das im Moment mit Jasmine testen soll. Wie mache ich das? Wenn ich $timeout.flush() benutze, werden die Funktionsaufrufe auf unbestimmte Zeit ausgeführt. Wenn ich Jasmines Scheinuhr verwende, scheint $timeout Nicht betroffen zu sein. Grundsätzlich sollte es gut sein, wenn ich das zum Laufen bringen kann:

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();
    });
});

Diese beiden Varianten funktionieren, helfen mir aber nicht:

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();
    });
});

Danke im Voraus!

43

Testen Sie Async nicht mit Jasmines Uhr. Verwenden Sie stattdessen $timeout.flush(), um den Testfluss synchron zu halten. Die Einrichtung mag etwas schwierig sein, aber sobald Sie sie erhalten haben, sind Ihre Tests schneller und kontrollierter.

Hier ist ein Beispiel für einen Test, der diesen Ansatz verwendet: https://github.com/angular/angular.js/blob/master/test/ngAnimate/animateSpec.js#L618

52
matsko

@ Matskos Antwort führte mich auf den richtigen Weg. Ich dachte, ich würde meine "vollständige" Lösung posten, um das Finden der Antwort zu vereinfachen.

Das Ding zum testen

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

Der Test

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();
    });
});
45
Beez