单元测试角度服务,将$timeout与Jasmine的模拟时钟一起使用



我的一个角度服务中有一个函数,我希望它能定期被重复调用。我想使用$timeout来完成此操作。它看起来像这样:

var interval = 1000; // Or something
var _tick = function () {
     $timeout(function () {
        doStuff();
        _tick();
    }, interval);
};
_tick();

我现在不知道如何用Jasmine进行单元测试——我该怎么做?如果我使用$timeout.flush(),那么函数调用将无限期地发生。如果我使用Jasmine的模拟时钟,$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();
    });
});

这两种变体有效,但对我没有帮助:

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

提前感谢!

不要使用Jasmine的时钟使测试异步。相反,使用$timeout.flush()来同步维护测试的流程。设置起来可能有点棘手,但一旦你得到了它,你的测试就会更快、更可控。

下面是一个使用这种方法进行测试的示例:https://github.com/angular/angular.js/blob/master/test/ngAnimate/animateSpec.js#L618

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

最新更新