I have a function inside one of my angular services that I'd like to be called repeatedly at a regular interval. I'd like to do this using $timeout. It looks something like this:
var interval = 1000; // Or something var _tick = function () { $timeout(function () { doStuff(); _tick(); }, interval); }; _tick();
I'm stumped on how to unit test this with Jasmine at the moment - How do I do this? If I use $timeout.flush()
then the function calls occur indefinitely. If I use Jasmine's mock clock, $timeout
seems to be unaffected. Basically if I can get this working, I should be good to go:
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(); }); });
These two variations work, but do not help me:
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(); }); });
Thanks in advance!
Jasmine is a behavior development testing framework. Unit tests are written using Jasmine and are run to see if individual parts of an application are working correctly. As a result, unit tests will either pass or fail depending on if the code is working correctly or has a bug.
Jasmine and Karma frameworks are used for Unit Testing of the Angular applications.
Jasmine supports testing async code. We can test async code with: describe("Using callbacks", function () { beforeEach(function (done) { setTimeout(function () { value = 0; done(); }, 1); }); it("supports sequential execution of async code", function (done) { value++; expect(value). toBeGreaterThan(0); done(); }); });
Prefer spies as they are usually the best way to mock services. These standard testing techniques are great for unit testing services in isolation. However, you almost always inject services into application classes using Angular dependency injection and you should have tests that reflect that usage pattern.
Do not make your test Async by using Jasmine's clock. Instead, use $timeout.flush()
to synchronously maintain the flow of the test. It may be a bit tricky to setup, but once you get it then your tests will be faster and more controlled.
Here's an example of a test that does it using this approach: https://github.com/angular/angular.js/blob/master/test/ngAnimate/animateSpec.js#L618
@matsko's answer led me down the right path. I thought I'd post my "complete" solution to make it simpler to find the answer.
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(); }); });
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With