Jasmine controller testing, expected spy to have been called
Solution 1
You are spying on the mock after it has been passed into the instantiated controller.
Try this:
describe('usersAddUserControllerUnitTest', function () {
var scope, deferred, objectUnderTest, mockedAddUserService, $controller;
beforeEach(module('app'));
beforeEach(inject(function ($rootScope, $q, _$controller_) {
scope = $rootScope.$new();
function emptyPromise() {
deferred = $q.defer();
return deferred.promise;
}
mockedAddUserService = {
getCountryPhoneCodes: emptyPromise
};
$controller = _$controller_;
}));
function makeController() {
objectUnderTest = $controller('usersAddUserController', {
$scope: scope,
usersAddUserService: mockedAddUserService
});
}
it('should call getCountryPhoneCodes method on init', function () {
//when
spyOn(mockedAddUserService, 'getCountryPhoneCodes').and.callThrough();
makeController();
deferred.resolve();
scope.$root.$digest();
//then
expect(mockedAddUserService.getCountryPhoneCodes).toHaveBeenCalled();
});
});
EDIT Thanks @juunas for noticing the bug in my solution
Solution 2
You can provide the mock like this:
mockedAddUserService = {
getCountryPhoneCodes: emptyPromise
};
beforeEach(function () {
module(function ($provide) {
$provide.value('usersAddUserService', mockedAddUserService);
});
});
EDIT:
The code should look (as i cannot test it) like this:
(function () {
'use strict';
describe('usersAddUserControllerUnitTest', function () {
beforeEach(module('app'));
var emptyPromise = function() {
var deferred = $q.defer();
return deferred.promise;
}
var mockedAddUserService = {
getCountryPhoneCodes: emptyPromise
};
beforeEach(function () {
module(function ($provide) {
$provide.value('usersAddUserService', mockedAddUserService);
});
});
var scope;
beforeEach(inject(function ($rootScope, $q, $controller) {
scope = $rootScope.$new();
$controller('usersAddUserController', {
$scope: scope
});
}));
it('should call getCountryPhoneCodes method on init', function () {
spyOn(mockedAddUserService, 'getCountryPhoneCodes').and.callThrough();
scope.$root.$digest();
expect(mockedAddUserService.getCountryPhoneCodes).toHaveBeenCalled();
});
});
}());
Comments
-
Thomas Weglinski almost 2 years
I have a method defined in AngularJS controller which is called on initialization. I want to test it using Jasmine (
"jasmine-core": "^2.3.4", "karma": "^0.12.37"
). I follow some tutorials on the Internet and StackOverflow questions, but I cannot find the right answer. Please take a look at this code:Controller
usersAddUserController
:(function () { 'use strict'; angular.module('app.users.addUser') .controller('usersAddUserController', ['$scope', 'usersAddUserService', function ($scope, usersAddUserService) { usersAddUserService.getCountryPhoneCodes().then(function (phoneCodes) { $scope.phoneCodes = phoneCodes; }); }]); }());
Jasmine test:
(function () { 'use strict'; describe('usersAddUserControllerUnitTest', function () { var scope, deferred, objectUnderTest, mockedAddUserService; beforeEach(module('app')); beforeEach(inject(function ($rootScope, $q, $controller) { scope = $rootScope.$new(); function emptyPromise() { deferred = $q.defer(); return deferred.promise; } mockedAddUserService = { getCountryPhoneCodes: emptyPromise }; objectUnderTest = $controller('usersAddUserController', { $scope: scope, usersAddUserService: mockedAddUserService }); })); it('should call getCountryPhoneCodes method on init', function () { //when spyOn(mockedAddUserService, 'getCountryPhoneCodes').and.callThrough(); deferred.resolve(); scope.$root.$digest(); //then expect(mockedAddUserService.getCountryPhoneCodes).toHaveBeenCalled(); }); }); }());
After running the tests, the error message is:
PhantomJS 1.9.8 (Windows 7 0.0.0) usersAddUserControllerUnitTest should call getCountryPhoneCodes method on init FAILED
Expected spy getCountryPhoneCodes to have been called.
I obviously missing something, but I cannot figure out what it is. Any help will be appreciated.
-
Thomas Weglinski almost 9 yearsThank you for the answer, but could you provide complete code? When I include your solution in my code I get the: "TypeError: 'undefined' is not an object (evaluating 'deferred.resolve')".
-
Thomas Weglinski almost 9 yearsThank you for the answer, but unfortunately still gets the same error with your code. Is it working for you?
-
Martin almost 9 yearsI didn't actually try it, it just looked like the obvious issue. Can you put a breakpoint on your controller constructor and see if it is actually the spy it is calling?
-
Thomas Weglinski almost 9 yearsThank you very much guys! Indeed @juunas was right, I got the SUCCESS now. If juunas agree, I will accept Martin answer anyway, it helps me a lot :)
-
timtos almost 9 yearsTwo reasons why I don't like the solution that much: 1. You have to call makeController(); on every test (this could be put in a beforeEach) 2. The code does not look as straight forwared as the "provide approach" but ok, this is just a question of taste. :) And well, it seems that you have to be careful about the order of things. I don't think you have this problem with provide...
-
Martin almost 9 years@timtos your points are valid. This solution is the shortest path from the original code and a working test.
-
Thomas Weglinski almost 9 yearsThank you for the code, I will try your solution as well and give a feedback what is need to be done more.