Issue
I've gone through a few tutorials and basic examples but I'm having a hard time writing unit tests for my controller. I've seen code snippets instantiating controllers and letting angular inject the $rootScope
object which in turn is used to create a new scope
object for the controller. But I can't figure out why ctrl.$scope
? is undefined:
describe('EmployeeCtrl', function () {
var scope, ctrl, $httpBackend;
beforeEach(inject(function (_$httpBackend_, $rootScope, $controller, $filter) {
$httpBackend = _$httpBackend_;
scope = $rootScope.$new();
ctrl = $controller('EmployeeCtrl', { $scope: scope});
expect(ctrl).not.toBeUndefined();
expect(scope).not.toBeUndefined(); //<-- PASS!
expect(ctrl.$scope).not.toBeUndefined(); //<-- FAIL!
}));
});
I ended up using the scope
variable instead of ctrl.$scope
but then on my first test I couldn't figure out how to unit test a function variable inside my controller:
Controller:
function EmployeeCtrl($scope, $http, $filter, Employee) {
var searchMatch = function (haystack, needle) {
return false;
}
}
Broken unit test:
it('should search ', function () {
expect(ctrl.searchMatch('numbers','one')).toBe(false);
});
This is what I get
TypeError: Object # has no method 'searchMatch'
How do you test that function? As a workaround I moved my method to $scope so I could test for scope.searchMatch
but I was wondering if this is the only way.
Finally, on my tests is appears $filter
is undefined too, how do you inject it? I tried this but didn't work:
ctrl = $controller('EmployeeCtrl', { $scope: scope, $filter: $filter });
Thanks
Update: The method mentioned above to inject $filter works just fine.
Solution
My understanding is that, in Angularjs, you pass a scope object to the controller when the application is starting and the controller modify the scope. The controller is not called anymore.
What a controller is really doing, in Angularjs, is initializing the scope: the controller only runs one time. If you understand this, you realize that asking to the controller for the scope like this:
currentScope = myController.scope;
doesn't makes sense.
(Incidentally, one of the things I don't like in Angular is the names that they have choosen. If what a 'controller' is doing is initializing the scope then it's not really a controller. There are a lot of this in the Angular API).
I think that the 'proper' way for testing a 'controller' is creating a new blank scope from scratch in a Jasmine beforeEach clause and using the 'controller' for initializing a new blank scope like this::
var ctrl, myScope;
beforeEach(inject(function($controller, $rootScope) {
myScope = $rootScope.$new();
ctrl = $controller('myController', {
$scope: myScope
});
}));
And then testing the new created scope has the expected properties:
it('In the scope, the initial value for a=2', function() {
expect(myScope.a).toBe(2);
});
In other words, you don’t test the controller; you test the scope that the controller has created.
So, you're doing it right.
Answered By - Robert
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.