Methods & Tools Software Development Magazine

Software Development Magazine - Project Management, Programming, Software Testing

 

Mox: utility library for unit testing your Angular app

Frank van Wijk, @fevanwijk, Xebia Netherlands, http://www.frankvanwijk.nl

Mox is an open source mocking utility library for AngularJS apps. When it comes to JavaScript unit tests, a lot of boilerplate code is written to set up the mocks. Mox provides utility functions to set up mocks very fast and have total control of the scope of your tests.

Website: www.github.com/fvanwijk/mox
Version tested: 0.6
System requirements: NodeJS
Support: www.github.com/fvanwijk/mox/issues

Everybody loves writing tests for software, right? Let’s see what is involved when testing an AngularJS application. The developers of AngularJS [1] call their framework a “superheroic JavaScript MVW framework”. MVW stands for Model View Whatever, since the ‘controller’ (view business logic) is spread across the app: in services, controllers, directives and even in templates.

AngularJS came with testability in mind. However, still a lot of developers struggle with testing their AngularJS app. This is partly caused by the poor Angular docs with bad or trivial testing examples. Another reason is that you need a lot of setup code to write the perfect test.

Mox evolved from a file called testUtils.js into a library that helps you to write Jasmine unit tests for your AngularJS app without a lot of boilerplate code.

Testing your AngularJS app

During the early stages of AngularJS, one of the developers, Vojta Jína, created Testacular, a test runner that uses dependency injection (DI). The test runner was renamed Karma [2], which is now the most popular JavaScript test runner. The DI system was later implemented into Angular. One of the advantages of dependency injection is that it is really easy to test your stuff.

For example, you want to test a trivial UserService:

angular.module('myApp')
 .value('Users',[{ id: 1,name: 'Frank' },{ id: 2,name: 'Misko' }])
 .service('UserService', function (Users)
    this.getUsers= function (){
      return Users;
    };
  });

In this code example, a User value and a UserService service is registered on the injector of the myApp module, with the provided constructor function that injects Users.

In case I want to inject the UserService in another service or controller, this constructor function is called with the User value as parameters. The result is a UserService singleton instance.

Now it is a piece of cake to test this UserService: injecting the UserService into your test, call the getUsers method and assert the result.

describe('the getUsers method on the UserService', function (){
  beforeEach(function() {  
angular.mock.module('myApp'); // Instantiate the injector for my app  });

 it('should return the Users', inject(function(UserService) {
    var usersResult = UserService.getUsers();
expect(usersResult[0].name).toBe('Frank');
 }));
});

What if I change the implementation of the Users value?

The test in the UserService spec fails! This is not what I want. It seems that the test is not only testing the getUsers method, but also implicitly tests the injected Users value. We have to isolate our test by stubbing the dependencies. This is done by replacing them with spies, which can be used to mock as well. See Martin Fowlers article for more information about stubs and mocks [3].

We have to re-register the dependency in our test to overwrite the original value.

angular.value('User', [{ name: 'Frank stub' }]);

And change the assertion to

expect(usersResult[0].name).toBe('Frank stub' );

Now our test does not depend on the real implementation of Users anymore.

Don’t Repeat Yourself

The previous example is a very simple case, but usually you have lots of stuff injected into your service, for example $scope, $http, $resource, $location and a lot of user created services.

This results in a lot of boilerplate code to stub the dependencies of the system under test (SUT).

Mox comes with a simple DSL to create all stubs. In this way you can create all setup code with minimal effort. It is not required anymore to manually re-register services.

Put this in a beforeEach block. That’s all.

mox
  .module('myApp')
  .mockServices('Users')
  .setupResults(function() {
    return{
      Users: [{ name: 'Frank stub' }]
   };
  }).run();

I can image that you have several services or controllers that inject Users. You don’t want to define the Users mock over and over, so let’s configure this mock one time so that the .setupResults call could be omitted.

// moxConfig.js
angular.extend(moxConfig,{
  Users: function($provide) {
   mox.save($provide, 'Users', [{ name: 'Frank' }]);
 }
});

Helper functions

There are also some services you need in your tests only:

Service

Function

Why?

$rootScope

Manually create a new $scope for your tests

To mock injected $scope

$compile

Compile HTML and link it to the $scope

To test components

$controller

Instantiate a controller

To test controllers

$q

Create promises

To mock methods that return promises such as $http

$templateCache

Fetching the template for a given template path

(component) views usually are in a HTML file

Injecting them into every test would require even more boilerplate code. (And yes, you need some of these services every test!)

This is a trivial example for testing a view without Mox.

it('should show "Hello Frank"', inject(function ($compile, $rootScope, $templateCache) {
  var$scope  = $rootScope.$new();
 $scope.name = 'Frank';
 var html=$templateCache.get('components/hello.html'); // <span class="hello">Hello {{name}}</span>
  var element =$compile(html)($scope);
 expect(element.find('.hello')).toBe('Hello Frank');
}));

Mox provides a lot of helper functions to create scope/controllers/promises and to compile HTML so that you don’t need to inject these services anymore.

With Mox, the previous test can be simplified to

it('should show "Hello Frank"', function (){
  var element =compileTemplate('components/hello.html', createScope({ name: 'Frank' }));
 expect(element.find('.hello')).toBe('Hello Frank');
});

Conclusion

The AngularJS core team is right: testing an Angular app isn’t that difficult. However, finding out how to do it correctly is a hassle. I hope that this article provides you some practical guidelines how to set up a unit test with minimal amount of boilerplate code using Mox.

A fully working example project with a vanilla test suite and a Mox test suite can be found at Github [4]. The project demonstrates how to test a component, view, controller or resource (HTTP backend) with and without using Mox. It also provides configurations for template preloading, fixtures, custom Jasmine jQuery matchers, test coverage and Karma/Wallaby test runner.

References

  1. AngularJS, http://www.angularjs.org
  2. Karma Test Runner, http://karma-runner.github.io
  3. Fowler, Martin. “Mocks aren’t stubs”, http://martinfowler.com/articles/mocksArentStubs.html
  4. Van Wijk, Frank. “Mox example project”, https://github.com/fvanwijk/mox-example

Related Resources


Click here to view the complete list of tools reviews

This article was originally published in the Fall 2016 issue of Methods & Tools