Below I have 3 functions that do exactly the same thing. Each one uses a different way of calling setTimeout
, delay1()
uses setTimeout
directly, delay2()
uses angularjs $timeout
and delay3()
uses lodash debounce
. They all work fine.
The problems occurs when I test using Jasmine. setTimeout
works fine with the jasmine.clock().tick()
method, but $timeout
and debounce
don't
I am interested in getting debounce working with Jasmine. I know I can use $timeout.flush()
with angularjs but $timeout
and setTimeout
give me problems elsewhere in my code where I am using it with leaflet maps. debounce works nicely with leaflet.
I have created a plunker here: plnkr where you will see the $timeout
and debounce tests not passing while the setTimeout test passes.
Is there a way I can work around this problem? Thanks
JS
var app = angular.module('plunker', []);
app.controller('MainCtrl', function($scope, $timeout) {
$scope.name = 'World';
$scope.delayed1 = function(){
setTimeout(function(){
$scope.name = "Hello world by setTimeout";
},500)
}
$scope.delayed2 = function(){
$timeout(function(){
$scope.name = "Hello world by $timeout";
},500)
}
$scope.delayed3 = function(){
_.debounce(function(){
$scope.name = "Hello world by debounce";
},500)
}
});
spec
describe('Testing a Hello World controller', function() {
var $scope = null;
var ctrl = null;
//you need to indicate your module in a test
beforeEach(module('plunker'));
beforeEach(inject(function($rootScope, $controller) {
$scope = $rootScope.$new();
ctrl = $controller('MainCtrl', {
$scope: $scope
});
}));
it('should say hallo to the World', function() {
expect($scope.name).toEqual('World');
});
it('should say Hello world by setTimeout', function() {
jasmine.clock().install();
$scope.delayed1();
jasmine.clock().tick(600);
expect($scope.name).toEqual('Hello world by setTimeout');
jasmine.clock().uninstall();
});
it('should say Hello world by timeout', function() {
jasmine.clock().install();
$scope.delayed2();
jasmine.clock().tick(600);
expect($scope.name).toEqual('Hello world by timeout');
jasmine.clock().uninstall();
});
it('should say Hello world by debouce', function() {
jasmine.clock().install();
$scope.delayed3();
jasmine.clock().tick(600);
expect($scope.name).toEqual('Hello world by debouce');
jasmine.clock().uninstall();
});
});
The clock in Jasmine will only work if you're testing setInterval()
or setTimeout()
functions directly as it just mocks those functions specifically to run synchronously. I believe there's a pull request for Jasmine to mock the Date object, which would allow for testing functions like _.debounce()
without mocking it, but I don't remember if that was merged in or not.
To test _.debounce()
you'll have to mock it to run synchronously, preferably as a spy or you can just override the function. Here's what I've found to work for me:
spyOn(_, 'debounce').and.callFake(function (func) {
return function () {
func.apply(this, arguments);
};
});
Now calls to _.debounce()
will run synchronously and tests should complete successfully. Of course you'll still have to use $timeout.flush()
.
I updated your plunker with these changes: http://plnkr.co/edit/KXmwcf1faUNf8nlqPeyd
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