I have code that is something like this:
function doThing() {
if (invalidInput) {
console.error(\'Invalid input.\');
return;
}
$timeout(function()
No uncertainty is welcome in unit tests, it should be predictable whether there is something to flush for $timeout
or not.
In this piece of code two cases should be tested, in both $timeout
shouldn't be a blackbox, it should be a stub instead (optionally a spy that wraps around the real service).
beforeEach(module('app', ($provide) => {
$provide.decorator('$timeout', ($delegate) => {
var timeoutSpy = jasmine.createSpy().and.returnValue($delegate);
// methods aren't copied automatically to spy
return angular.extend(timeoutSpy, $delegate);
});
}));
The first is falsey invalidInput
:
...
MyService.doThing();
expect($timeout).not.toHaveBeenCalled();
And the second one is truthy invalidInput
:
...
MyService.doThing();
expect($timeout).toHaveBeenCalledWith(jasmine.any(Function), 1000);
$timeout.flush();
expect(MyService.doThing).toHaveBeenCalledTimes(2);
Disregarding this case, it is generally a good thing to return promises from promise-powered functions:
function doThing() {
if (invalidInput) {
console.error('Invalid input.');
return;
}
return $timeout(function() {
return MyService.doThing();
}, 1000);
}
This way the callers (most likely specs) may have some control on function's asynchronous behaviour.
Answering the question directly, the expected way to do 'flushIfFlushable()' is
try {
$timeout.verifyNoPendingTasks(); // just for semantics, not really necessary
$timeout.flush();
} catch (e) {}
Which should be avoided for the reasons listed above.
I suggest define two separate unit tests to verify the behavior of your doThing
function. Try following code:
(function () {
'use strict';
angular.module('myApp', [])
.controller('MainCtrl', function ($timeout, MyService) {
var vm = this;
vm.invalidInput = true;
vm.doThing = doThing;
function doThing() {
if (vm.invalidInput) {
return;
}
$timeout(function () {
MyService.doThing();
}, 1000);
}
});
})();
(function () {
'use strict';
angular.module('myApp').service('MyService', MyService);
function MyService() {
this.doThing = function () {
// doThing code
};
}
})();
'use strict';
describe('Controller: MainCtrl', function () {
beforeEach(module('myApp'));
var vm,
$timeout,
MyService;
beforeEach(inject(function (_$controller_, _$timeout_, _MyService_) {
$timeout = _$timeout_;
MyService = _MyService_;
vm = _$controller_('MainCtrl', {
$timeout: $timeout,
MyService: MyService
});
}));
it('should call doThing for valid inputs', function () {
spyOn(MyService, 'doThing').andCallThrough();
vm.invalidInput = false;
vm.doThing();
$timeout.flush();
expect(MyService.doThing).toHaveBeenCalled();
});
it('should not call doThing for invalid inputs', function () {
spyOn(MyService, 'doThing').andCallThrough();
vm.invalidInput = true;
vm.doThing();
expect(MyService.doThing).not.toHaveBeenCalled();
});
});
With the first test we expect to call MyService.doThing()
function.
On other hand, if you have invalidInput
as true
, the previous function should not be called.
I hope It helps.