How do I spy on a controller method using Jasmine?

angularjs, jasmine

Solution

This sequence of lines:

ctrl = $controller('MainCtrl as mc', {
  $scope: $scope
});

spyOn($scope.mc, 'loadData').and.callThrough();

Means that the Jasmine spy is created after the controller has already been instantiated by $controller. Before the spy is created, the `init` function has already executed.

You can't switch the lines around either, because MainCtrl needs to exist before you can spy on a method on it.

If the `init` function calls another service, then spy on that service's method and assert that the service is called correctly. If MainCtrl is just doing something internally, then test the result of that, for example, by asserting that controller's data/properties are updated. It may not even be worth testing if it's trivial enough.

Also, since you're using the controller as syntax, you can reference the controller through the return value of calling $controller, rather than accessing the scope directly:

ctrl = $controller('MainCtrl as mc', {
  $scope: $scope
});

ctrl.loadData === $scope.mc.loadData; // true

Problem

I'm using the "controller as" syntax to create my controller. I have a private initialization function that calls a function to load the 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'm creating a spy to check whether the `loadData` function has been called. Although I can verify that the function has been called by testing for the `mc.dataLoaded` flag, my spy doesn't seem to record the function being called. How can I get the spy to correctly record the 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); }); }); ``` Plunker link

Original source