Reputation: 13
We recently started incorporating promises in our controllers to handle multiple requests at the same time and it works in the app, but unit testing these has been proven to be more than difficult and I will I have a hard time grasping exactly what it is I'm missing. Following are two snippets that are very simplified on what I'm exactly trying to test.
Controller:
angular.module('testengine').controller('testController', [
'$scope',
'$q',
'Api',
function($scope, $q, Api) {
$scope.getTest = function (deferred) {
// do stuff...
Api.test.get(params, function(response) {
// do stuff with response
if (deferred) {
deferred.resolve(); // if success
}
});
};
$scope.runTest = function () {
// do stuff...
var promises = [];
var deferred = $q.defer();
promises.push(deferred.promise);
$scope.getTest(deferred);
$q.all(promises).then(function () {
$scope.navigate();
}, function () {
// do stuff with rejected
});
};
$scope.navigate = function () {
// do stuff
};
}]);
Test:
describe('Controller:', function () {
var root, scope, controllerFactory, $q;
function createController () {
return controllerFactory('testController', {
$scope: scope
});
}
beforeEach(function () {
module('testengine');
});
beforeEach(inject(function ($rootScope, $controller, _$q_) {
root = $rootScope;
scope = $rootScope.new();
controllerFactory = $controller;
$q = _$q_;
});
it('should run test', function () {
createController();
var deferred = $q.defer();
spyOn(scope, 'getTest').andReturn(deferred.resolve());
spyOn(scope, 'navigate');
$scope.runTest();
root.$apply();
expect(scope.navigate).toHaveBeenCalled();
});
});
According to all examples and documentation I've read on promises and $q, this should work, but it doesn't, instead I get:
Expected spy navigate to have been called but it was never called.
I'm guessing because it's not the same deferred object I'm resolving in my mocked spy, but how am I suppose to mock that? Do I have to bind the deferred object on the controller or the scope or what?
The reason for the structure you see is because there are different methods using getTest() (and some do not only use that request but also others, hence promises). Also, getTest() is tested separately in another test, which is why I want to mock that function and any requests made in it.
Any help is appreciated and if I've made some blatant errors, I'm happy to educate myself being still fairly new to Angular.
Upvotes: 1
Views: 799
Reputation: 6066
Yes, you are not resolving the right promise. You need to intercept the function arguments.
spyOn(scope, 'getTest');
expect(scope.getTest).toHaveBeenCalled();
scope.getTest.mostRecentCall.args[0].resolve();
This is a good concise Jasmine reference: http://tobyho.com/2011/12/15/jasmine-spy-cheatsheet/
Upvotes: 1