Angular 1.x Unit Testing with Jasmine

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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// multiplication.js
var Multiplication = (function () {
function multiplyBy2(val) {
return val*2;
}

function multiplyBy3(val) {
return val*4; // typo here will cause this function to behave incorrectly
}

return {
multiplyBy2: multiplyBy2,
multiplyBy3: multiplyBy3
}
})();

and here is a corresponding Jasmine test file:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// multiplication.spec.js
describe("multiplication", function() {
var testNum;

beforeEach(function () {
// put setup code here
testNum = 3;
})

describe('method multiplyBy2', function() {
it('should multiply its argument by 2', function() {
expect(Multiplication.multiplyBy2(testNum)).toEqual(6);
});
})

describe('method multiplyBy3', function() {
it('should multiply its argument by 3', function() {
// fails due to typo above
expect(Multiplication.multiplyBy3(testNum)).toEqual(9);
});
});
});

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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// myCtrl.controller.js

(function() {
'use strict';

// Load Angular module and add controller
angular
.module('myApp')
.controller('MyCtrl', MyCtrl);

// Simple controller that uses myService
MyCtrl.$inject = ['myService'];
function MyCtrl(myService) {
var myCtrl = this;

// Declare bindable members here
myCtrl.hello = "Hello World";
myCtrl.anotherHello = myService.hello();
}
})();

 

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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// myService.service.js

(function() {
'use strict';

angular
.module('myApp')
.service('myService', myService);

myService.$inject = ['myOtherService'];
function myService(myOtherService) {
function hello() {
return "Hello";
}

return {
hello: hello
};
}
})();

 

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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
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 and call myOtherService.someMethod', function () {
mockMyOtherService.someMethod = jasmine.createSpy('someMethod');
expect(myService.hello()).toEqual("Hello");
expect(mockMyOtherService.someMethod).toHaveBeenCalled();
});
});
});

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
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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
// myDirective.directive.js

(function() {
'use strict';

angular
.module('myApp')
.directive('myDirective', myDirective);

myDirective.$inject = ['myService'];
function myDirective(myService) {
var directive = {
restrict: 'E',
template: '<div>Hello</div>',
controller: MyDirectiveCtrl,
controllerAs: 'myDirectiveCtrl',
scope: {},
bindToController: true,
link: linkFn
}

return directive;

function linkFn(scope) {
scope.myDirectiveCtrl.goodbye = myService.goodbye(); // Goodbye
console.log(scope.myDirectiveCtrl.hello); // Hello
}
}

MyDirectiveCtrl.$inject = ['myService'];
function MyDirectiveCtrl(myService) {
var myDirectiveCtrl = this;

// bindables go here
myDirectiveCtrl.hello = null;

activate();

function activate() {
myDirectiveCtrl.hello = myService.hello(); // Hello
}
}
})();

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
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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
// myDirective.directive.spec.js

describe("myDirective", function() {
var myDirectiveCtrl, mockService = {}, $compile, $rootScope, element;

// Load the module
beforeEach(module('myApp'));

// Register providers
beforeEach(function () {
module('myApp', function ($provide) {
$provide.value('myService', mockService);
})
})

// Add content to providers (mock the dependencies)
beforeEach(function () {
mockService = {
hello: jasmine.createSpy('myServiceHello').and.returnValue("Hello"),
goodbye: jasmine.createSpy('myServiceGoodbye').and.returnValue("Goodbye")
};
});

// instantiate the directive
beforeEach(inject(function (_$compile_, _$rootScope_) {
$compile = _$compile_;
$rootScope = _$rootScope_;

// wrap the directive in an angular element, then compile it
element = angular.element('<my-directive></my-directive>');
$compile(element)($rootScope);
$rootScope.$digest();

// retrieve the controller for the directive
// see "Extras" section here https://docs.angularjs.org/api/ng/function/angular.element
myDirectiveCtrl = element.controller('myDirective');
}));

it('consist of a div', function () {
expect(element.html()).toEqual('<div>Hello</div>');
});

describe('link function', function () {
it('should call my service goodbye', function () {
expect(mockService.goodbye).toHaveBeenCalled();
})

it('should set the goodbye property', function () {
expect(myDirectiveCtrl.goodbye).toEqual("Goodbye");
})
});

describe('myDirectiveCtrl', function () {
it('should invoke myService.hello', function () {
expect(mockService.hello).toHaveBeenCalled();
});

it('should set the hello property', function () {
expect(myDirectiveCtrl.hello).toEqual("Hello");
})
});
});

An Agile Perspective

Last year, Dave Thomas (a.k.a, Pragmatic Dave) posted a blog entry titled “Agile is Dead (Long Live Agility)” in which he argues that Agile has become a buzzword – a marketing term. The opportunistic swooped in and monetized the simple concepts suggested in the Manifesto for Agile Development. His post is a great read and the video below is a great watch.



The take-away is this: there is no cookie-cutter solution. If you rely on “experts” to provide you with the perfect methodology for developing software then you’re probably going to miss the mark.

My First Post Using Hexo

I started working as a software developer for a large company about a month ago. Up until that point, I was working in a different profession, and programming was mostly just my hobby. I’ve programmed in a lot of different areas, both for work and for fun: PLC programming, HMI programming, MATLAB, and python. My work programming has mostly been related to machine software, and my hobbyist programming has mostly been web development. Now I’m working with JavaScript, although it’s unclear how long that will be true. I am not formally trained in computer science.

I am starting this blog in part as an effort to chronicle my learning process as a software developer. As an aspiring developer, I know there are many others who are not trained in computer science or computer engineering and want to break into the field. Maybe I can help them somehow.

I also just wanted to get writing. I wanted an outlet where I can write about things that interest me. What are my interests besides programming? Technology, gadgets, science and engineering, music, health and nutrition, to name just a few. Although this blog will mostly be about programming and tech, I’m not going to necessarily narrow the scope to just that. Who knows?

Since I’m going to be working with JavaScript a lot, I decided to use a JavaScript-based blogging framework called Hexo. Hexo is powered by Node.js. It’s simple and elegant, and it uses Markdown for blog posts. Installation and setup was easy in Ubuntu:

$ npm install -g hexo
$ hexo init my-blog
$ cd my-blog
$ npm install

Now I have a blog located in the folder my-blog. Then to create a new blog post, I just do

$ hexo new my-new-blog-post

This generates a new markdown file with the title in the filename. I’ve configured the filename format for new posts so that it also includes the date, like so:

1
new_post_name: :year-:month-:day-:title.md # File name of new posts

This is located in the site configuration file, _config.yml, along with a variety of other options, such as the site title, URL, language, and server options. I really like the default theme that comes with hexo. In any case, there are a variety of other themes available at https://github.com/hexojs/hexo/wiki/themes. One thing I noticed is that the RSS feed doesn’t seem to work right out of the box. To get it working, you just need to install the feed generator plugin, like so:

$ npm install hexo-generator-feed --save

Then you can add it to your configuration file:

1
2
3
4
5
# RSS Feed
feed:
type: atom
path: atom.xml
limit: 20

That’s about it for now. I will probably update this post as I learn more about hexo in the future.