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
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
I found a solution that allowed me to avoid changing my controller. I included a $state
mock service in the test suite's beforeEach
method, and gave it a reload
mock method:
beforeEach(inject(function ($controller, $rootScope) {
stateMock = {
reload: function() {
myCtrl = $controller('MyCtrl');
}
};
...
Then within the jasmine tests, I can simply call stateMock.reload()
to re-initialize my controller while preserving my spies I declared in another beforeEach
block.
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