Mocha Testing AngularJs Dependency Injection
When you test your AngularJs code, you need to explicitly inject the services that your controllers and modules require. It has its own special syntax. It requires mocking. You'll see a slightly different syntax than you may have expected.
The Solution: Mocking AngularJs Injections
Angular is simple and quick on many things. On some things, it's not as simple as we are be led to believe from simple examples. From the Angular tutorial:
Because we started using dependency injection and our controller has dependencies, constructing the controller in our tests is a bit more complicated.
And really, who doesn't use dependency injection in any of their Angular code? But don't worry, it's not much worse. And really, it makes sense that things would be this way.
The final Mocha code to test our simple controller should look something like this:
var assert = chai.assert,
expect = chai.expect,
should = chai.should();
it('should be available', inject(function($rootScope, $controller) {
var scope = $rootScope.$new();
var ctrl = $controller(MyController, {
$scope: scope
});
expect(ctrl).to.not.be.undefined;
}));
A couple of points:
chai
is an assertion library that makes a great bdd/should-style assertion available in browser tests.inject()
is made available through theangular-mocks.js
file. This is available automagically in Jasmine, but in Mocha, you have to include this extra file to get the function.$rootScope
is a scope available to all controllers, so it's not dependent onng-controller
references which are in your src, but not your test environment. From this scope, we create a new scope.- Initializing
MyController
with the$controller
function allows us to mock the value of$scope
in the controller.
Potential Errors
If you look at the solution above, it should give you the working test of DI that you want. Here are a few things I worked through when testing my Angular controller...
TypeError: 'undefined' is not an object
My controller looked somewhat like this:
function MyController($scope) {
$scope.$on('$viewContentLoaded', function () {
// ... stuff when dom in the controller is ready
});
}
And this was the start of my test:
it('should be available', function() {
var ctrl = new MyController();
// ... assertions
});
Ths $scope.$on()
line couldn't run because $scope
was simply not injected and undefined.
blah - scope is not there
ReferenceError: Can't find variable: expect
In Mocha, you'll need to import an assertion library of your choice. Otherwise, expect()
and other assertions will not be available to use. I prefer Chai for its should-style assertions. They read as a sentence really well:
expect(ctrl).to.not.be.undefined;