How can I track the controller method using Jasmine?

I use the controller-like syntax to create my controller. I have a private initialization function that calls a function to load default data.

var app = angular.module('plunker', []); app.controller('MainCtrl', function($scope) { var mc = this; mc.dataLoaded = false; function init() { mc.loadData(); } mc.loadData = function(){ mc.dataLoaded = true; } init(); }); 

In my test, I create a spy to check if the loadData function has been loadData . Although I can verify that the function was called by testing for the mc.dataLoaded flag, my spy does not seem to write the called function. How can I get a spy to correctly record a function call?

 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 as mc', { $scope: $scope }); spyOn($scope.mc, 'loadData').and.callThrough(); })); it('should call load data', function() { expect($scope.mc.loadData).toHaveBeenCalled(); //expect($scope.mc.dataLoaded).toEqual(false); }); }); 

Link to Plunker

+8
angularjs jasmine
source share
2 answers

This sequence of lines:

 ctrl = $controller('MainCtrl as mc', { $scope: $scope }); spyOn($scope.mc, 'loadData').and.callThrough(); 

Means that a Jasmine spy is created after the controller has already been created using $ controller. The init function has already been completed before creating the spy.

You also cannot switch between lines, because MainCtrl must exist before you can track a method on it.

If the init function calls another service, then look in that service method and confirm that the service is being called correctly. If MainCtrl just does something internally, then check the result of this, for example, by stating that the controller data / properties are being updated. It is not even worth checking if it is enough enough.

In addition, since you are using the controller as syntax, you can refer to the controller through the return value of the call to $ controller, and not directly access the scope:

 ctrl = $controller('MainCtrl as mc', { $scope: $scope }); ctrl.loadData === $scope.mc.loadData; // true 
+9
source share

I found a solution that allowed me to avoid changing my controller. I turned on the $state mock service in the beforeEach test suite beforeEach and gave it the reload mock method:

 beforeEach(inject(function ($controller, $rootScope) { stateMock = { reload: function() { myCtrl = $controller('MyCtrl'); } }; ... 

Then in jasmine tests, I can simply call stateMock.reload() to reinitialize my controller, keeping my spies declared in another beforeEach block.

+1
source share

All Articles