Unit Testing Full Stack Part 2 – AngularJS Part 1 of 3

July 16, 2015

This is part 2 of my multi part guide for setting up and testing a full stack JavaScript application. Next up is AngularJS. I’ve broken this up into 3 separate parts, as this is a bit more complex for testing the front end, since we have both unit tests and end to end tests. The parts will be broken up as follows:

Part 2A – Installation, Routes, Controllers

Part 2B – Services, Directives, Filters

Part 2C – Coverage Reports, End to End (e2e)

Installation & Configuration

To get started, install the following:

Karma (test runner) in conjunction with Jasmine (unit testing framework). You can use other unit testing frameworks, if you so desire, just check here, under ‘Framework’, to see which ones are compatible.

So let’s start with our AngularJS project, with npm, it will install both karma, the jasmine plugin and the chrome launcher plugin, as well as any required dependencies for those packages.

Something to be aware of, is that because Angular runs in the browser, it requires the chrome browser to launch when running tests, so that they can execute. This will launch and destroy a chrome browser for you automatically, but if you’re not wanting to have a browser launch, you can use PhantomJS for everything to be just run in a headless browser (A browser engine without any graphical interface, it runs in memory only).

Once these are complete, you’re next goal is configure karma to run through the tests for us. To do this, we need to create a configuration for Karma. Navigate to your /test directory and we’ll type (where karma-unit.conf.js is the name of the configuration file):

You’ll be asked a series of question (see here), once done, you will inevitably go into the Karma configuration itself and setup the correct order for loading your project files under the ‘files’ property array. This will probably take looking at your own projects index.html file and making sure that the files are all there and in proper order. Everything source file that you use in your project will need to be loaded this way, and again, in proper order.

To do the unit tests, you will also need to load the angular-mock file. This allows us to mock the backend requests over http, as jasmine doesn’t actually process async calls to an api properly. So be sure to add that file to your files array. Here’s a link you can add to the array for the angular-mock.js file form the google api CDN, for v1.2.26:

My files array looks like this:

As you can tell, it has some wildcard symbols there, where ** means all sub-folders and * means all files, and in my file, *.js means all files ending in .js. So these are all of the files included in the testing.

Next up are the plugins. My plugins looks like:

I have a few extra karma packages installed, if you’d like to use them, you can npm install them. karma-ng-html2js-preprocessor makes it possible to inject a directive template into the tests for testing. karma-mocha-reporter is the type of report that gets generated that I like, and finally karma-coverage is a coverage reporting tool built on istanbul.

Finally, here are the preprocessors, reports, and coverageReporter configs:

I need to configure the preprocessor details for both ng-html2js and the coverage plugin, and configure the coverage reporter output details.

Finally, we’re ready to start writing some tests.

Once we write tests, we can run the tests by typing:

Routes

I have my route test spec file in /test/unit-angular/routeSpecs/routeSpec.js.

I need to create the file just like any typical mocha test, with the describe, beforeEach, it, etc. functions. So a very basic route spec file will look like this:

Going through this, is pretty straight forward. we need to inject the different services location, route and rootScope. The inject function also ignores the surrounding ‘_’ for the service names. This is just to help readability within the test file. Now we can use these services in the tests themselves.

Any request made to any server requires us to use the $httpBackend to intercept the expected call and return what response the that should be expected.

In order to navigate, you need to use location.path(…) and follow it up with a manual call to $digest().

This is pretty much the entire pattern for testing your different routes. If your application requires you to login in order to test routes, you will need to create the objects, etc. to simulate a user logging. Remember, since it’s not actually making any calls to a server, you don’t need to worry about a token being authenticated, etc. Those kinds of tests should be run as unit tests against the API and/or end to end tests.

Controllers

I place my controllers in /test/unit-angular/controllerSpecs/<ctrlName>ControllerSpec.js.

Testing controllers is also fairly straight forward, but requires understanding a few important concepts.

1) We have to manually create and setup the scopes for the controllers we want to test.

2) You need to perform $httpBackend.flush() after we want to perform the $httpBackend calls in controllers.

Here’s a sample controller spec file:

The beforeEach is a bit more complex for the controller:

As you can see, I create the new controller and bind the scope to it by first creating the new scope with $rootScope.$new() and then setting it for the manual dependency injection with the $controller service {$scope: scope}.

Following this, I prefer to test all of the functions in a controller to be sure that they’re defined before I start testing them.

If the application is refactored, just realize if the function changes name or is removed, you’ll need to keep your controller test files current.

Finally, testing the actual scope functions is required. You’ll need to setup any $httpBackend expected calls first before you test the scope function, otherwise you’ll get errors for unexpected $http calls.

 

Any required objects for the scope function should be passed in at this time as well so that the function runs properly.

You can see how in the one test I try and use an empty object, and what I expect the api to respond with.

The rest comes down to just writing test coverage for all of your controllers functions based on different scenarios. Time to write your test plans! 🙂

Next part in this series we’ll cover the Services, Directives and Filters. We’ll go in-depth with testing Directives, importantly, for how to test their isolate scopes and functions.

I hope you enjoyed the read, and let me know if you find any issues with the above so that I can edit any mistakes.

Leave a Reply