Menu Search
Jump to the content X X
Smashing Conf New York

We use ad-blockers as well, you know. We gotta keep those servers running though. Did you know that we publish useful books and run friendly conferences — crafted for pros like yourself? E.g. upcoming SmashingConf Barcelona, dedicated to smart front-end techniques and design patterns.

An Introduction To Unit Testing In AngularJS Applications

AngularJS1 has grown to become one of the most popular single-page application frameworks. Developed by a dedicated team at Google, the outcome is substantial and widely used in both community and industry projects.

One of the reasons for AngularJS’ success is its outstanding ability to be tested. It’s strongly supported by Karma102 (the spectacular test runner written by Vojta Jína) and its multiple plugins. Karma, combined with its fellows Mocha, Chai163 and Sinon184, offers a complete toolset to produce quality code that is easy to maintain, bug-free and well documented.

“Well, I’ll just launch the app and see if everything works. We’ve never had any problem doing that.”

– No one ever

The main factor that made me switch from “Well, I just launch the app and see if everything works” to “I’ve got unit tests!” was that, for the first time, I could focus on what matters and on what I enjoy in programming: creating smart algorithms and nice UIs.

I remember a component that was supposed to manage the right-click menu in an application. Trust me, it was a complex component. Depending on dozens of mixed conditions, it could show or hide buttons, submenus, etc. One day, we updated the application in production. I can remember how I felt when I launched the app, opened something, right-clicked and saw no contextual menu — just an empty ugly box that was definitive proof that something had gone really wrong. After having fixed it, re-updated the application and apologized to customer service, I decided to entirely rewrite this component in test-driven development style. The test file ended up being twice as long as the component file. It has been improved a lot since, especially its poor performance, but it never failed again in production. Rock-solid code.

A Word About Unit Testing Link

Unit testing has become a standard in most software companies. Customer expectations have reached a new high, and no one accepts getting two free regressions for the price of one update anymore.

If you are familiar with unit testing, then you’ll already know how confident a developer feels when refactoring tested code. If you are not familiar, then imagine getting rid of deployment stress, a “code-and-pray” coding style and never-ending feature development. The best part of? It’s automatic.

Unit testing improves code’s orthogonality. Fundamentally, code is called “orthogonal” when it’s easy to change. Fixing a bug or adding a feature entails nothing but changing the code’s behavior, as explained in The Pragmatic Programmer: From Journeyman to Master5. Unit tests greatly improve code’s orthogonality by forcing you to write modular logic units, instead of large code chunks.

Unit testing also provides you with documentation that is always up to date and that informs you about the code’s intentions and functional behavior. Even if a method has a cryptic name — which is bad, but we won’t get into that here — you’ll instantly know what it does by reading its test.

Unit testing has another major advantage. It forces you to actually use your code and detect design flaws and bad smells. Take functions. What better way to make sure that functions are uncoupled from the rest of your code than by being able to test them without any boilerplate code?

Furthermore, unit testing opens the door to test-driven development. While it’s not this article’s topic, I can’t stress enough that test-driven development is a wonderful and productive way to write code.

What and What Not to Test Link

Tests must define the code’s API. This is the one principle that will guide us through this journey. An AngularJS application is, by definition, composed of modules. The elementary bricks are materialized by different concepts related to the granularity at which you look at them. At the application level, these bricks are AngularJS’ modules. At the module level, they are directives, controllers, services, filters and factories. Each one of them is able to communicate with another through its external interface.

Everything is bricks, regardless of the level you are at.6
Everything is bricks, regardless of the level you are at. (View large version7)

All of these bricks share a common attribute. They behave as black boxes, which means that they have a inner behavior and an outer interface materialized by inputs and outputs. This is precisely what unit tests are for: to test bricks’ outer interfaces.

Black box model (well, this one is gray, but you get the idea).8
Black box model (well, this one is gray, but you get the idea) (View large version9)

Ignoring the internals as much as possible is considered good practice. Unit testing — and testing in general — is a mix of stimuli and reactions.

Bootstrapping A Test Environment For AngularJS Link

To set up a decent testing environment for your AngularJS application, you will need several npm modules. Let’s take a quick glance at them.

Karma: The Spectacular Test Runner Link

Karma102 is an engine that runs tests against code. Although it has been written for AngularJS, it’s not specifically tied to it and can be used for any JavaScript application. It’s highly configurable through a JSON file and the use of various plugins.

If you don’t see this at some point in this process, then you might have missed something.11
If you don’t see this at some point in the process, then you might have missed something. (View large version12)

All of the examples in this article can be found in the dedicated GitHub project13, along with the following configuration file for Karma.

// Karma configuration
// Generated on Mon Jul 21 2014 11:48:34 GMT+0200 (CEST)
module.exports = function(config) {
  config.set({

    // base path used to resolve all patterns (e.g. files, exclude)
    basePath: '',

    // frameworks to use
    frameworks: ['mocha', 'sinon-chai'],

    // list of files / patterns to load in the browser
    files: [
      'bower_components/angular/angular.js',
      'bower_components/angular-mocks/angular-mocks.js',
      'src/*.js',
      'test/*.mocha.js'
    ],

    // list of files to exclude
    exclude: [],

    // preprocess matching files before serving them to the browser
    preprocessors: {
      'src/*.js': ['coverage']
    },

    coverageReporter: {
      type: 'text-summary',
      dir: 'coverage/'
    },

    // test results reporter to use
    reporters: ['progress', 'coverage'],

    // web server port
    port: 9876,

    // enable / disable colors in the output (reporters and logs)
    colors: true,

    // level of logging
    logLevel: config.LOG_INFO,

    // enable / disable watching file and executing tests on file changes
    autoWatch: true,

    // start these browsers
    browsers: ['PhantomJS'],

    // Continuous Integration mode
    // if true, Karma captures browsers, runs the tests and exits
    singleRun: false
  });
};

This file can be automagically generated by typing karma init in a terminal window. The available keys are described in Karma’s documentation14.

Notice how sources and test files are declared. There is also a newcomer: ngMock15 (i.e. angular-mocks.js). ngMock is an AngularJS module that provides several testing utilities (more on that at the end of this article).

Mocha Link

Mocha is a testing framework for JavaScript. It handles test suites and test cases, and it offers nice reporting features. It uses a declarative syntax to nest expectations into cases and suites. Let’s look at the following example (shamelessly stolen from Mocha’s home page):

describe('Array', function() {
  describe('#indexOf()', function() {
    it('should return -1 when the value is not present', function() {
      assert.equal(-1, [1,2,3].indexOf(5));
      assert.equal(-1, [1,2,3].indexOf(0));
    });
  });
});

You can see that the whole test is contained in a describe call. What is interesting about nesting function calls in this way is that the tests follow the code’s structure. Here, the Array suite is composed of only one subsuite, #indexOf. Others could be added, of course. This subsuite is composed of one case, which itself contains two assertions and expectations. Organizing test suites into a coherent whole is essential. It ensures that test errors will be reported with meaningful messages, thus easing the debugging process.

Chai Link

We have seen how Mocha provides test-suite and test-case capabilities for JavaScript. Chai163, for its part, offers various ways of checking things in test cases. These checks are performed through what are called “assertions” and basically mark a test case as failed or passed. Chai’s documentation has more17 on the different assertions styles.

Sinon Link

Sinon184 describes itself as “standalone test spies, stubs and mocks for JavaScript.” Spies, stubs and mocks all answer the same question: How do you efficiently replace one thing with another when running a test? Suppose you have a function that takes another one in a parameter and calls it. Sinon provides a smart and concise way to monitor whether the function is called and much more (with which arguments, how many times, etc.).

Unit Testing At The Application Level Link

The point of the external interface of a module in an AngularJS application is its ability to be injected into another module — that it exists and has a valid definition.

beforeEach(module('myAwesomeModule'));

This is enough and will throw an error if myAwesomeModule is nowhere to be found.

Unit Testing At The Module Level Link

An AngularJS module can declare several types of objects. Some are services, while others are more specialized. We will go over each of them to see how they can be bootstrapped in a controlled environment and then tested.

Filters, Services and Factories: A Story of Dependency Injection Link

Filters, services and factories (we will refer to these as services in general) can be compared to static objects or singletons in a traditional object-oriented framework. They are easy to test because they need very few things to be ready, and these things are usually other services.

AngularJS links services to other services or objects using a very expressive dependency-injection model, which basically means asking for something in a method’s arguments.

What is great about AngularJS’ way of injecting dependencies is that mocking a piece of code’s dependencies and injecting things into test cases are super-easy. In fact, I am not even sure it could be any simpler. Let’s consider this quite useful factory:

angular.module('factories', [])
.factory('chimp', ['$log', function($log) {
  return {
    ook: function() {
      $log.warn('Ook.');
    }
  };
}]);

See how $log is injected, instead of the standard console.warn? While AngularJS will not print $log statements in Karma’s console, avoid side effects in unit tests as much as possible. I once reduced by half the duration of an application’s unit tests by mocking the tracking HTTP requests — which were all silently failing in a local environment, obviously.

describe('factories', function() {

  beforeEach(module('factories'));

  var chimp;
  var $log;

  beforeEach(inject(function(_chimp_, _$log_) {
    chimp = _chimp_;
    $log = _$log_;
    sinon.stub($log, 'warn', function() {});
  }));

  describe('when invoked', function() {

    beforeEach(function() {
      chimp.ook();
    });

    it('should say Ook', function() {
      expect($log.warn.callCount).to.equal(1);
      expect($log.warn.args[0][0]).to.equal('Ook.');
    });
  });
});

The pattern for testing filters, services or other injectables is the same. Controllers can be a bit trickier to test, though, as we will see now.

Controllers Link

Testing a controller could lead to some confusion. What do we test? Let’s focus on what a controller is supposed to do. You should be used to considering any tested element as a black box by now. Remember that AngularJS is a model-view-whatever (MVW) framework, which is kind of ironic because one of the few ways to define something in an AngularJS application is to use the keyword controller. Still, any kind of decent controller usually acts as a proxy between the model and the view, through objects in one way and callbacks in the other.

The controller usually configures the view using some state objects, such as the following (for a hypothetical text-editing application):

angular.module('textEditor', [])

.controller('EditionCtrl', ['$scope', function($scope) {
  $scope.state = {toolbarVisible: true, documentSaved: true};
  $scope.document = {text: 'Some text'};

  $scope.$watch('document.text', function(value) {
    $scope.state.documentSaved = false;
  }, true);

  $scope.saveDocument = function() {
    $scope.sendHTTP($scope.document.text);
    $scope.state.documentSaved = true;
  };

  $scope.sendHTTP = function(content) {
    // payload creation, HTTP request, etc.
  };
}]);

Chances are that the state will be modified by both the view and the controller. The toolbarVisible attribute will be toggled by, say, a button and a keyboard shortcut. Unit tests are not supposed to test interactions between the view and the rest of the universe; that is what end-to-end tests are for.

The documentSaved value will be mostly handled by the controller, though. Let’s test it.

describe('saving a document', function() {

  var scope;
  var ctrl;

  beforeEach(module('textEditor'));

  beforeEach(inject(function($rootScope, $controller) {
    scope = $rootScope.$new();
    ctrl = $controller('EditionCtrl', {$scope: scope});
  }));

  it('should have an initial documentSaved state', function(){
    expect(scope.state.documentSaved).to.equal(true);
  });

  describe('documentSaved property', function() {
    beforeEach(function() {
      // We don't want extra HTTP requests to be sent
      // and that's not what we're testing here.
      sinon.stub(scope, 'sendHTTP', function() {});

      // A call to $apply() must be performed, otherwise the
      // scope's watchers won't be run through.
      scope.$apply(function () {
        scope.document.text += ' And some more text';
      });
    });

    it('should watch for document.text changes', function() {
      expect(scope.state.documentSaved).to.equal(false);
    });

    describe('when calling the saveDocument function', function() {
      beforeEach(function() {
        scope.saveDocument();
      });

      it('should be set to true again', function() {
        expect(scope.state.documentSaved).to.equal(true);
      });

      afterEach(function() {
        expect(scope.sendHTTP.callCount).to.equal(1);
        expect(scope.sendHTTP.args[0][0]).to.equal(scope.document.text);
      });
    });
  });
});

An interesting side effect of this code chunk is that it not only tests changes on the documentSaved property, but also checks that the sendHTTP method actually gets called and with the proper arguments (we will see later how to test HTTP requests). This is why it’s a separated method published on the controller’s scope. Decoupling and avoiding pseudo-global states (i.e. passing the text to the method, instead of letting it read the text on the scope) always eases the process of writing tests.

Directives Link

A directive is AngularJS’ way of teaching HTML new tricks and of encapsulating the logic behind those tricks. This encapsulation has several contact points with the outside that are defined in the returned object’s scope attribute. The main difference with unit testing a controller is that directives usually have an isolated scope, but they both act as a black box and, therefore, will be tested in roughly the same manner. The test’s configuration is a bit different, though.

Let’s imagine a directive that displays a div with some string inside of it and a button next to it. It could be implemented as follows:

angular.module('myDirectives', [])
.directive('superButton', function() {
  return {
    scope: {label: '=', callback: '&onClick'},
    replace: true,
    restrict: 'E',
    link: function(scope, element, attrs) {

    },
    template: '<div>' +
      '<div>{{label}}</div>' +
      '<button ng-click="callback()">Click me!</button>' +
      '</div>'
  };
});

We want to test two things here. The first thing to test is that the label gets properly passed to the first div’s content, and the second is that something happens when the button gets clicked. It’s worth saying that the actual rendering of the directive belongs slightly more to end-to-end and functional testing, but we want to include it as much as possible in our unit tests simply for the sake of failing fast. Besides, working with test-driven development is easier with unit tests than with higher-level tests, such as functional, integration and end-to-end tests.

describe('directives', function() {

  beforeEach(module('myDirectives'));

  var element;
  var outerScope;
  var innerScope;

  beforeEach(inject(function($rootScope, $compile) {
    element = angular.element('<super-button label="myLabel" on-click="myCallback()"></super-button>');

    outerScope = $rootScope;
    $compile(element)(outerScope);

    innerScope = element.isolateScope();

    outerScope.$digest();
  }));

  describe('label', function() {
    beforeEach(function() {
      outerScope.$apply(function() {
        outerScope.myLabel = "Hello world.";
      });
    })

    it('should be rendered', function() {
      expect(element[0].children[0].innerHTML).to.equal('Hello world.');
    });
  });

  describe('click callback', function() {
    var mySpy;

    beforeEach(function() {
      mySpy = sinon.spy();
      outerScope.$apply(function() {
        outerScope.myCallback = mySpy;
      });
    });

    describe('when the directive is clicked', function() {
      beforeEach(function() {
        var event = document.createEvent("MouseEvent");
        event.initMouseEvent("click", true, true);
        element[0].children[1].dispatchEvent(event);
      });

      it('should be called', function() {
        expect(mySpy.callCount).to.equal(1);
      });
    });
  });
});

This example has something important. We saw that unit tests make refactoring easy as pie, but we didn’t see how exactly. Here, we are testing that when a click happens on the button, the function passed as the on-click attribute is called. If we take a closer look at the directive’s code, we will see that this function gets locally renamed to callback. It’s published under this name on the directive’s isolated scope. We could write the following test, then:

describe('click callback', function() {
  var mySpy;

  beforeEach(function() {
    mySpy = sinon.spy();
    innerScope.callback = mySpy;
  });

  describe('when the directive is clicked', function() {
    beforeEach(function() {
      var event = document.createEvent("MouseEvent");
      event.initMouseEvent("click", true, true);
      element[0].children[1].dispatchEvent(event);
    });

    it('should be called', function() {
      expect(mySpy.callCount).to.equal(1);
    });
  });
});

And it would work, too. But then we wouldn’t be testing the external aspect of our directive. If we were to forget to add the proper key to the directive’s scope definition, then no test would stop us. Besides, we actually don’t care whether the directive renames the callback or calls it through another method (and if we do, then it will have to be tested elsewhere anyway).

Providers Link

This is the toughest of our little series. What is a provider exactly? It’s AngularJS’ own way of wiring things together before the application starts. A provider also has a factory facet — in fact, you probably know the $routeProvider and its little brother, the $route factory. Let’s write our own provider and its factory and then test them!

angular.module('myProviders', [])

.provider('coffeeMaker', function() {
  var useFrenchPress = false;
  this.useFrenchPress = function(value) {
    if (value !== undefined) {
      useFrenchPress  = !!value;
    }

    return useFrenchPress;
  };

  this.$get = function () {
    return {
      brew: function() {
        return useFrenchPress ? 'Le café.': 'A coffee.';
      }
    };
  };
});

There’s nothing fancy in this super-useful provider, which defines a flag and its accessor method. We can see the config part and the factory part (which is returned by the $get method). I won’t go over the provider’s whole implementation and use cases, but I encourage you to look at AngularJS’ official documentation about providers19.

To test this provider, we could test the config part on the one hand and the factory part on the other. This wouldn’t be representative of the way a provider is generally used, though. Let’s think about the way that we use providers. First, we do some configuration; then, we use the provider’s factory in some other objects or services. We can see in our coffeeMaker that its behavior depends on the useFrenchPress flag. This is how we will proceed. First, we will set this flag, and then we’ll play with the factory to see whether it behaves accordingly.

describe('coffee maker provider', function() {
  var coffeeProvider = undefined;

  beforeEach(function() {
    // Here we create a fake module just to intercept and store the provider
    // when it's injected, i.e. during the config phase.
    angular.module('dummyModule', function() {})
      .config(['coffeeMakerProvider', function(coffeeMakerProvider) {
        coffeeProvider = coffeeMakerProvider;
      }]);

    module('myProviders', 'dummyModule');

    // This actually triggers the injection into dummyModule
    inject(function(){});
  });

  describe('with french press', function() {
    beforeEach(function() {
      coffeeProvider.useFrenchPress(true);
    });

    it('should remember the value', function() {
      expect(coffeeProvider.useFrenchPress()).to.equal(true);
    });

    it('should make some coffee', inject(function(coffeeMaker) {
      expect(coffeeMaker.brew()).to.equal('Le café.');
    }));
  });

  describe('without french press', function() {
    beforeEach(function() {
      coffeeProvider.useFrenchPress(false);
    });

    it('should remember the value', function() {
      expect(coffeeProvider.useFrenchPress()).to.equal(false);
    });

    it('should make some coffee', inject(function(coffeeMaker) {
      expect(coffeeMaker.brew()).to.equal('A coffee.');
    }));
  });
});

HTTP Requests Link

HTTP requests are not exactly on the same level as providers or controllers. They are still an essential part of unit testing, though. If you do not have a single HTTP request in your entire app, then you can skip this section, you lucky fellow.

Roughly, HTTP requests act like inputs and outputs at any of your application’s level. In a RESTfully designed system, GET requests give data to the app, and PUT, POST and DELETE methods take some. That is what we want to test, and luckily AngularJS makes that easy.

Let’s take our factory example and add a POST request to it:

angular.module('factories_2', [])
.factory('chimp', ['$http', function($http) {
  return {
    sendMessage: function() {
      $http.post('http://chimps.org/messages', {message: 'Ook.'});
    }
  };
}]);

We obviously do not want to test this on the actual server, nor do we want to monkey-patch the XMLHttpRequest constructor. That is where $httpBackend enters the game.

describe('http', function() {

  beforeEach(module('factories_2'));

  var chimp;
  var $httpBackend;

  beforeEach(inject(function(_chimp_, _$httpBackend_) {
    chimp = _chimp_;
    $httpBackend = _$httpBackend_;
  }));

  describe('when sending a message', function() {
    beforeEach(function() {
      $httpBackend.expectPOST('http://chimps.org/messages', {message: 'Ook.'})
      .respond(200, {message: 'Ook.', id: 0});

      chimp.sendMessage();
      $httpBackend.flush();
    });

    it('should send an HTTP POST request', function() {
      $httpBackend.verifyNoOutstandingExpectation();
      $httpBackend.verifyNoOutstandingRequest();
    });
  });
});

You can see that we’ve defined which calls should be issued to the fake server and how to respond to them before doing anything else. This is useful and enables us to test our app’s response to different requests’ responses (for example, how does the application behave when the login request returns a 404?). This particular example simulates a standard POST response.

The two other lines of the beforeEach block are the function call and a newcomer, $httpBackend.flush(). The fake server does not immediately answer each request; instead, it lets you check any intermediary state that you may have configured. It waits for you to explicitly tell it to respond to any pending request it might have received.

The test itself has two methods calls on the fake server (verifyNoOutstandingExpectation and verifyNoOutstandingRequest). AngularJS’ $httpBackend does not enforce strict equality between what it expects and what it actually receives unless you’ve told it to do so. You can regard these lines as two expectations, one of the number of pending requests and the other of the number of pending expectations.

ngMock Module Link

The ngMock module20 contains various utilities to help you smooth over JavaScript and AngularJS’ specifics.

$timeout, $log and the Others Link

Using AngularJS’ injectable dependencies is better than accessing global objects such as console or window. Let’s consider console calls. They are outputs just like HTTP requests and might actually matter if you are implementing an API for which some errors must be logged. To test them, you can either monkey-patch a global object — yikes! — or use AngularJS’ nice injectable.

The $timeout dependency also provides a very convenient flush() method, just like $httpBackend. If we create a factory that provides a way to briefly set a flag to true and then restore it to its original value, then the proper way to test it’s to use $timeout.

angular.module('timeouts', [])

.factory('waiter', ['$timeout', function($timeout) {
  return {
    brieflySetSomethingToTrue: function(target, property) {
      var oldValue = target[property];

      target[property] = true;

      $timeout(function() {
        target[property] = oldValue;
      }, 100);
    }
  };
}]);

And the test will look like this:

describe('timeouts', function() {

  beforeEach(module('timeouts'));

  var waiter;
  var $timeout;

  beforeEach(inject(function(_waiter_, _$timeout_) {
    waiter = _waiter_;
    $timeout = _$timeout_;
  }));

  describe('brieflySetSomethingToTrue method', function() {
    var anyObject;

    beforeEach(function() {
      anyObject = {foo: 42};
      waiter.brieflySetSomethingToTrue(anyObject, 'foo');
    });

    it('should briefly set something to true', function() {
      expect(anyObject.foo).to.equal(true);
      $timeout.flush();
      expect(anyObject.foo).to.equal(42);
    });
  });
});

Notice how we’re checking the intermediary state and then flush()’ing the timeout.

module() and inject() Link

The module()21 and inject()22 functions help to retrieve modules and dependencies during tests. The former enables you to retrieve a module, while the latter creates an instance of $injector, which will resolve references.

it('should say Ook.', inject(function($log) {
  sinon.stub($log, 'warn', function() {});

  chimp.ook();

  expect($log.warn.callCount).to.equal(1);
  expect($log.warn.args[0][0]).to.equal('Ook.');
}));

In this test case, we’re wrapping our test case function in an inject call. This call will create an $injector instance and resolve any dependencies declared in the test case function’s arguments.

Dependency Injection Made Easy Link

One last trick is to ask for dependencies using underscores around the name of what we are asking for. The point of this is to assign a local variable that has the same name as the dependencies. Indeed, the $injector used in our tests will remove surrounding underscores if any are found. StackOverflow has a comment on this.

Conclusion Link

Unit testing in AngularJS applications follows a fractal design. It tests units of code. It freezes a unit’s behavior by providing a way to automatically check its response to a given input. Note that unit tests do not replace good coding. AngularJS’ documentation is pretty clear on this point: “Angular is written with testability in mind, but it still requires that you do the right thing.”

Getting started with writing unit tests — and coding in test-driven development — is hard. However, the benefits will soon show up if you’re willing to fully test your application, especially during refactoring operations.

Tests also work well with agile methods. User stories are almost tests; they’re just not actual code (although some approaches, such as “design by contract23,” minimize this difference).

Further Resources Link

(al, ml)

Footnotes Link

  1. 1 https://angularjs.org
  2. 2 http://karma-runner.github.io
  3. 3 http://chaijs.com
  4. 4 http://sinonjs.org
  5. 5 https://pragprog.com/the-pragmatic-programmer
  6. 6 https://www.smashingmagazine.com/wp-content/uploads/2014/09/01-bricks-opt.png
  7. 7 https://www.smashingmagazine.com/wp-content/uploads/2014/09/01-bricks-opt.png
  8. 8 https://www.smashingmagazine.com/wp-content/uploads/2014/09/02-blackbox-opt.png
  9. 9 https://www.smashingmagazine.com/wp-content/uploads/2014/09/02-blackbox-opt.png
  10. 10 http://karma-runner.github.io
  11. 11 https://www.smashingmagazine.com/wp-content/uploads/2014/09/03-karma-success-opt.png
  12. 12 https://www.smashingmagazine.com/wp-content/uploads/2014/09/03-karma-success-opt.png
  13. 13 https://github.com/lorem--ipsum/smashing-article
  14. 14 http://karma-runner.github.io/0.8/config/configuration-file.html
  15. 15 https://docs.angularjs.org/api/ngMock
  16. 16 http://chaijs.com
  17. 17 http://chaijs.com/guide/styles/
  18. 18 http://sinonjs.org
  19. 19 https://docs.angularjs.org/guide/providers
  20. 20 https://docs.angularjs.org/api/ngMock
  21. 21 https://docs.angularjs.org/api/ngMock/function/angular.mock.module
  22. 22 https://docs.angularjs.org/api/ngMock/function/angular.mock.inject
  23. 23 http://en.wikipedia.org/wiki/Design_by_contract
  24. 24 https://pragprog.com/the-pragmatic-programmer
  25. 25 https://docs.angularjs.org/guide/unit-testing
  26. 26 https://github.com/lorem--ipsum/smashing-article
SmashingConf New York

Hold on, Tiger! Thank you for reading the article. Did you know that we also publish printed books and run friendly conferences – crafted for pros like you? Like SmashingConf Barcelona, on October 25–26, with smart design patterns and front-end techniques.

↑ Back to top Tweet itShare on Facebook

Sébastien Fragnaud is a front-end engineer at Metamarkets, a company specialized in real-time analytics for the advertising market. He loves JavaScript and is an active contributor to the AngularJS open-source ecosystem.

  1. 1

    Antonio Laguna

    October 8, 2014 10:05 am

    Given the fact that Karma ships with Jasmine and that Jasmine is in more active development than Mocha or Sinon, I don’t get why you chose to use Mocha + Sinon with Jasmine.

    I get it’s a matter of preference but Jasmine contains all Mocha and Sinon features and it works Out of the Box.

    8
    • 2

      You’re right. You can use Jasmine out of the box and that’s probably easier for beginners or small projects. I guess Jasmine and Mocha have their own pros and cons, however I tend to consider Mocha to be slightly more up-to-date and active than Jasmine. Besides, you might have noticed how I try to break things into modular, autonomous bricks, and I like how separated yet compatible Mocha/Chai/Sinon are :-)

      Thanks for your comment !

      10
      • 3

        I have to side with Antonio here. Jasmine does everything in one product that you have used three products for. I work on a sizeable project and we use Jasmine with no troubles at all, so I don’t understand your comment about small projects.

        These things being said, you covered the topic very well and the article is good reading.

        2
        • 4

          For me it was the asynchronous headaches that made me prefer mocha/chai/sinon over Jasmine. It’s nice that Jasmine has a more active development community, but the out of the box enforcing that you can let asynchronous functions execute synchronously during a test run makes testing easier and more readable to me.

          3
    • 5

      Mocha seems to work out better in JavaScript than Jasmine if you have a lot of asynchronous processing you need to test. Jasmine has a method for doing this, but the method with Mocha + Chai + Sinon is less noisy.

      0
  2. 6

    Awesome article. Thanks!

    3
  3. 7

    I love the quote at the top, but you have the attribution is inside the quotes, which was confusing. It makes it look like “no one ever” is part of what was said, not who said it (no one).

    5
  4. 9

    Most tricky thing is testing $q and setting up config for testing directives with templateUrl.

    0
    • 10

      Isn’t the trick with $q just calling a $rootScope.$digest() in your test and triggering the “done” function in the result functions? For me testing promises comes down to:

      Service.promiseFunction().then(function(result) {
      expect(result).to.be.something;
      done();
      });
      $rootScope.$digest();

      1
  5. 11

    Or you could just say, start with this repo: https://github.com/yearofmoo/angularjs-seed-repo

    Unit tests and E2E testing. Live reload. Selenium. Good stuff. I suggest forking it and making a skeleton for all your AngularJS apps.

    1
  6. 13

    Great article. To the people who say Jasmine does everything in one package, I used to think like that 3 years ago. I always thought Jasmine and qUnit were enough until I decided to give the Mocha/Chai/Sinon stack a try. A whole new world of possibilities opened up. I don’t know how many lines of code and headaches it has saved me. I highly recommend to give it a try and then decide if you are still happy with Jasmine alone.

    3
    • 14

      This is very true. The only way to make sure you pick the right tool for one task is to test as many others as possible. This will also broaden your knowledge of these other tools, and who knows ? They might be well-suited for other tasks.

      0
  7. 15

    Fire-Dragon-DoL

    October 18, 2014 7:18 pm

    Guys what about stubbing web requests? I used jasmine and $httpBackend but that doesn’t allow me to match GET params on my request (except with regexp, which is terrible considering params order). And i would like to also match PARTIAL get params (for example match message=3 but ignore fake=1), anyone find a solution to this? Also, what about factories for some object creation with fake data (to emulate server response)? I usually write tests on ruby where I have rspec (jasmine), webmock ($httpBackend), FactoryGirl (to create objects), but I definitely didn’t find all utilities I’m used to :(

    0
  8. 18

    Fantastic article! Thanks Sebastian!

    0
  9. 19

    “The elementary bricks are materialized by different concepts related to the granularity at which you look at them.”

    I might be a po-dunk coder from yesteryear, but it is sentences like the above which make me stop reading articles I otherwise found interesting. Is it always necessary for JavaScript articles to talk about something in esoteric and cumbersome language? I remember back in “the day” when it was cool to sound like you had a grasp on it all. That was also when nobody wanted to deal with JavaScript because we didn’t have good libraries and it was … cumbersome.

    If we can make JavaScript less cumbersome, why can’t we make JavaScript articles less cumbersome?

    3
  10. 20

    This is a very useful article and demonstrates code patterns I have now locked at dozens of times. My problem is that I can’t seem to make Mocha aware of angular-mocks. When run the line of code beforeEach(module(‘myModule’)); I get an error that module is not a function. I need some kind of configuration file or something to make Mocha aware of angular. No one ever seems to mention it and it doesn’t appear to be a very common problem since a web search doesn’t return much information

    1
  11. 21

    Jamshid Asadzadeh

    November 29, 2014 8:40 pm

    This was very clear and nicely written, thank you.

    1
  12. 22

    It has very usefull article for us. I will use future projects on the AngularJs.

    0
  13. 23

    Thanks for article. It’s a pity that example of directive test doesn’t mention about testing directive with templateUrl. Let’s face it: in real project, most of the time you use templateUrl, instead of template and inline code. It can be a pain to make it work. For anybody who struggle with this issue, I recommend: http://www.bluesphereinc.com/blog/unit-testing-angularjs-directives

    3
  14. 24

    Patrik Gallik

    January 17, 2015 1:13 am

    This is sooo useful and well explained. Wish I have read this couple months ago :) I’ve been using Jasmine to test my angular code, but I will definitely try Mocha for next tests. Thank you!

    0
  15. 25

    This is a bit misleading. Why not already use Jasmine? Misko Hevery uses it… and rest of the angularjs team

    1
  16. 26

    Need help.

    1. If my unit test is calling function which is in controller and that function is calling a service to fetch the details. Will it call service(StationService)?
    2. My Karma unit test is not able to inject StationService and not able to call service.

    My Code.
    /// controller
    var policyControllers = angular.module(‘policyControllers’, []);
    policyControllers.controller(‘StationListController’, [‘$translate’, ‘$scope’,’$rootScope’,’$state’, ‘StationService’, ‘StationListExportService’, function ($translate, $scope, $rootScope, $state, StationService, StationListExportService) {

    $scope.getFilterDetails = function(StationService, filterDetails ){

    StationService.get(filterDetails).$promise.then(function (filteredDetails) {
    console.log(” Web services Result – “, JSON.stringify(filteredDetails));
    },function(error) {
    console.log(” Error “);
    });
    };

    ///Service
    var policyServices = angular.module(‘policyServices’, [‘ngResource’]);
    policyServices.factory(‘StationService’, [‘$resource’, function($resource) {
    return $resource(policyConfig.mock? ‘modules/policymanager/services/mock/stations.json’: ‘http://10.132.240.25:7640/policy/api/v1/stationpolicy/stations’,{},{
    get:{method: ‘POST’,isArray: false, url:’modules/policymanager/services/mock/stations.json’}
    });
    }]);

    /// Unit test
    describe(‘station filter’, function(){

    var scope;
    var ctrl;
    var translate, scope, rootScope, state;
    var StationService, StationListExportService;

    beforeEach(module(‘policyServices’));
    beforeEach(module(‘policyControllers’));

    beforeEach(inject(function(_StationService_, _StationListExportService_, $rootScope, $controller, $translate, $state) {

    StationService = _StationService_;
    StationListExportService = _StationListExportService_;
    translate = $translate;
    rootScope = $rootScope;
    state = $state;
    scope = $rootScope.$new();
    ctrl = $controller(‘StationListController’, {$scope: scope});

    }));

    it(‘Stations Inject test case’, inject([‘StationService’,function(StationService){

    var data = {“recency”:””,”countries”:[],”policies”:[],”stations”:[{“stationName”:”Test”}],”status”:”ready”,”regions”:[]};

    scope.getFilterDetails(StationService, data);

    /// Getting StationService is undifiened

    }]));

    0
  17. 27

    i have a interest to test angularjs application, you have given a worthy information, thank you for sharing keep your updates regularly…

    0
  18. 28

    $httpBackend.expectPOST(‘http://chimps.org/messages’, {message: ‘Ook.’})
    .respond(200, {message: ‘Ook.’, id: 0});

    I need to store the message reponse into a json file. How can I do that? Thanks!

    0
  19. 29

    your approach is not good for beginners, I figured out I’m lost at your explanation. You didn’t explained how to run those things.
    your sample is just too complex for any beginner understand.

    0
  20. 30

    Thank you for the article. I recently wrote some notes regarding configuration of unit testing of Angular apps within WebStorm IDE, to enhance writing unit tests process, and I would like to share them with others. http://best-web-creation.com/articles/view/id/angular-js-app-part-3-unit-test?lang=en

    0

↑ Back to top