Testing in Angular seems to be a sticking point for a lot of developers and rightly so. I haven’t been able to find a real solid reference or comprehensive guide for testing in Angular with Jasmine in conjunction with the John Papa Angular Style Guide, so I decided to start creating one. This guide assumes no previous experience with unit testing, but it does assume familiarity/experience with Angular JS.
Background
Wikipedia defines a unit test as follows:
In computer programming, unit testing is a software testing method by which individual units of source code, sets of one or more computer program modules together with associated control data, usage procedures, and operating procedures, are tested to determine whether they are fit for use.
We essentially test a block of code (e.g., a module, controller, service, etc.) in a vacuum. We set the stage by mocking dependencies and simulating the conditions we’d like to test, then we make assertions about how our code should behave. Let’s take a look at a simple example. Here is a simple multiplication module:
1 | // multiplication.js |
and here is a corresponding Jasmine test file:
1 | // multiplication.spec.js |
http://plnkr.co/edit/FO47r4yXfzM5lWQGWoEG?
Check out the plunk to see how it’s set up. We have two different test cases, marked by the ‘it’ statement. You can nest describe and it statements, and the descriptions will concatenate. For example, the first test case would read “multiplication multiplyBy2 should multiply its argument by 2.”
Testing in Angular is similar to the example above, except that we’ll need to use the https://docs.angularjs.org/api/ngMock module so that we can inject and mock angular services and modules. When I write Angular code, I mostly follow the John Papa Style Guide, which says you should use controllerAs syntax (with bindToController, if needed).
Controllers
Testing controllers is probably the most straight-forward in Angular. We load the module that our controller is associated with, then we mock any dependencies our controller has, and finally we instantiate the controller. I’ve created another plunk (here: http://plnkr.co/edit/RpPAl5oRaXFB96XNB4KD) that I’ll be using examples from. Here’s the controller and test file:
1 | // myCtrl.controller.js |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27// myCtrl.controller.spec.js
describe("myCtrl", function() {
var myCtrl, mockService;
// Load the module
beforeEach(module('myApp'));
beforeEach(inject(function ($controller) {
// Create a mocked version of myService
mockService = {
// Using a spy here to track calls to mockService.hello()
hello: jasmine.createSpy('myService')
};
// Instantiate the controller using $controller service,
// stubbing in mockService for myService
myCtrl = $controller('MyCtrl', { myService: mockService });
}));
it('should set the hello property', function () {
expect(myCtrl.hello).toEqual('Hello World');
});
it('should call myService.hello', function () {
expect(mockService.hello).toHaveBeenCalled();
});
});
Services
Services are similar to controllers, except there’s no helper function for instantiating them, which means we have to mock dependencies a little differently. Here’s my simple example service with test file:
1 | // myService.service.js |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22// myService.service.spec.js
describe('myService', function () {
var myService, mockMyOtherService = {};
beforeEach(module('myApp'));
beforeEach(module(function($provide) {
$provide.value('myOtherService', mockMyOtherService);
}));
beforeEach(inject(function(_myService_) {
// Use injector to get the service
myService = _myService_;
}));
describe('hello', function () {
it('should return hello', function () {
expect(myService.hello()).toEqual("Hello");
});
});
});
Here we’re using $provide to “register” a provider in Angular world. We tell it the name of the dependency we are providing (myOtherService in this case) along with the mocked service (in this case just an empty object). It’s important to understand here that we’re just registering the provider so Angular knows about it. We can even change it after the fact like this:
1 | describe('myService', function () { |
Directives
Directives are a little trickier. Since directives are essentially reusable components with custom behavior, they inherently mix presentation (template) with display logic (controller or linking function). Things get interesting when you’re using controllerAs syntax, where the controller is a property of the directive/element scope. Here’s my directive, using both a post-link function and controller (http://plnkr.co/edit/IeVdEwmNWcfF8wnnx9i2):
1 | // myDirective.directive.js |
The controller is accessible in the linking function as a scope property as shown above. Likewise, $scope could be accessible in the controller by injecting it.
1 | // myDirective.directive.spec.js |