Unit Testing AngularJS Controllers, Views, and More: Part Four

Link to Part One of this series

Link to Part Two of this series

Link to Part Three of this series

The AngularJS-RequireJS-Seed used for reference is here.

Duck-Angular is here.

This post contains a grab bag of miscellaneous topics to afford a deeper understanding of Duck-Angular, as well as some nuances/pitfalls of unit testing in AngularJS.

Mocking HTTP the way you want

Yeah, you can mock $httpBackend. However, most of the time, you can get away by just mocking $http itself. Here is an example from service1-test.js, which shows you how to do this. The primary reason to use the success() and error() API’s of $http is that these methods provide wrappers over the usual single-argument then() clause, to expand the parameters into the familiar (data, status, headers, config) format. We don’t need to unpack this information ourselves. Thus, to mock out $http directly, the thing to mock out are these functions.

Also note, these failure handlers can be chained, so that every mocked function (success() and error()) must return a promise.

define(["service1", "Q"], function (Service1Ctor, Q) {
  describe("Service1", function () {
    it("should work, even if the HTTP call succeeds", function (){
      var httpResponse = {successful: false};
      var promise = Q.reject(httpResponse);
      promise.success = function(onSuccess) {
        promise.then(function(data) { return onSuccess(data); });
        return promise;
      promise.error = function(onError) {
        promise.fail(function(err) { return onError(err); });
        return promise;
      var getStub = sinon.stub().returns(promise);
      var httpMock = {get: getStub};
      var service1 = new Service1Ctor(Q, httpMock);
      var run = service1.getHttp("http://google.com");
      return expect(run).to.be.rejected;

A bit verbose, but this can be abstracted into a mock builder function. I find it easier to use this approach when I’m mocking out an $http dependency, instead of having to register Angular modules, and go through the whole bootstrap process.

Why $q won’t always work

Here is a simple unit test from controller1-view-test.js. There’s nothing special about this unit test: all it does is returns a pre-resolved promise.

it("can prove that $q won't work in a plain unit test", function () {
  return mother.createMvc("route2Controller", "../templates/route2.html", {}).then(function (mvc) {
    var injector = mvc.injector;
    var $q = injector.get("$q");
    var d = $q.defer();
    return d.promise;

When you run this test, you will be greeted with a Mocha timeout error, like so:

Error: timeout of 10000ms exceeded
    at http://localhost/example/static/js/test/lib/mocha/mocha.js?bust=1388996292503:3993:14

The reason using $q in place of Q in a simple unit test will not work is that *$q‘s mechanics are intimately tied to AngularJS’ scope lifecycle. Indeed, if you take a look at the code for $q (inside QProvider), you’ll see a call to $evalAsync(), which makes no guarantees about when the callback will be invoked, except for the following:

  • It will execute in the current script execution context (before any DOM rendering).
  • At least one $digest cycle will be performed expression execution.

The simple fix for all these situations is to use Q instead of $q. Should you have to fulfil the $q dependency for a service or a controller in a simple unit test (one that does not bootstrap AngularJS modules), inject Q.

Resolving child templates

A template may have multiple partials, each of which might contain further nested partials. Normally, when you’re unit testing templates, this loading/binding of the nested partials is handled for you entirely by AngularJS. But there is a caveat: we do not really know when all the partials have loaded.

In older versions of Duck-Angular, trying to unit test templates which had nested partials, could sometimes result in errors, because the test had started before AngularJS had resolved and bound all the nested partials. In most cases, the expansion of these partials would not happen, and any assertions on DOM elements expected to be inside these partials would fail.

Currently, Duck-Angular fixes this by making use of the copious event publishing mechanisms that AngularJS provides. The event that we will focus on for this discussion is the $includeContentLoaded event. This event is published whenever a partial is loaded using the ng-include directive. How does this help us? Well, if we know how many partials exist in a template (regardless of the level of nesting), we can set up a counter that ticks every time the $includeContentLoaded event is fired, and only begin our test when the counter reaches the number of partials that we detected.

That sounds very well in theory, except for one tiny wrinkle: we need to actually load up all the partials to actually find out how many partials there are, to begin with. That is exactly what Duck-Angular does. It creates a chain of promises. Each promise is responsible for loading a separate partial. Once this partial is loaded, this promise is chained with other promises which, in turn, load up child partials. The result of each promise fulfilment is one plus the fulfilment value of the child promises.

This is simply a recursive chain of promises, each of which aggregate the counters of its child promises, and passes it up the tree. The result is the total number of promises spawned, which has a one-to-one correspondence with the number of partials loaded. The code below from duck-angular.js illustrates this.

  var includes = element.find("[ng-include]");
  if (includes.length === 0) {
    return Q.fcall(function () {
      return 1;

  var promises = _.map(includes, function (include) {
    var includeSource = angular.element(include).attr("src").replace("'", "").replace("'", "");
    var includePromise = requireQ(["text!" + includeSource]);
    return includePromise.spread(function (sourceText) {
      var child = self.removeElementsBelongingToDifferentScope(self.createElement(sourceText));
      return num(child);
  return Q.all(promises).then(function (counts) {
    return 1 + _.reduce(counts, function (sum, count) {
      return sum + count;
    }, 0);

Unit Testing Pitfalls

  • If you’re using Mocha-as-Promised, make sure to always explicitly return a promise. If you do not do this, the test will most likely appear to pass (because Mocha has no way of deciding when the test has finished), but would have probably failed.

  • A good practice while using Chai-as-Promised is to use the should.be.fulfilled() and the should.be.rejected() clauses, instead of using the simple then() clause. If your promise fails, any assertions/steps in the then() clause will never be executed, and your test will appear to have succeeded. Using Chai-as-Promised’s explicit fulfillment/failure verification prevents this from happening.

This (kind of) concludes this series on unit testing AngularJS controllers and views, using Duck-Angular. I’ll probably update the code to use AngularJS 1.2.x, since that is the latest production release branch. If there are any corrections/errata arising from that, I’ll update in a separate post.