Tag Archives: angularjs

Unit Testing AngularJS Controllers, Views, and More: Part Six, Injecting Indirect Dependencies

This is a follow-up to the series of posts on using Duck-Angular for unit testing controller, views, and bindings (Part 1, Part 2, Part 3, Part 4, and Part 5), Duck-Angular and AngularJS-RequireJS-Seed (which is what the code in this series of posts is based on) now support AngularJS 1.2.9.

What do I mean by Indirect Dependencies? It’s those dependencies that are associated with your controller/service through at least one level of indirection. Simply put, if X depends upon Y, and Y depends upon Z, X depends upon Z. Because all objects are built from the base up, they are transitive, regardless of the complexity of the hierarchy. So far, we’ve seen how to inject dependencies directly into the object(s) under test. So, what about indirect dependencies? Well, it was always possible using Duck-Angular, just slightly unwieldy to set up. No more.

Let’s start with a simple service with no dependencies:

define([], function(){
    return function() {
        this.getSomeData = function() {
            return "True Data from Svc 3";
        };
    };
});

We’ll create another service which depends upon service3. Let’s imaginatively call this service2.

define([], function(){
    return function($q, svc3) {
        var dataFromService3 = svc3.getSomeData();
        this.get = function() {
            var d = $q.defer();
            d.resolve({fromService2: "Some Data", fromService3: dataFromService3});
            return d.promise;
        };
    };
});

We intend to write a test for controller2, as a Duck-Angular test. Note that we are not mocking out service2; we want to preserve it’s original behaviour, but mock out it’s dependency service3.

it("can mock out indirect dependencies", function () {
  var service3Mock = { getSomeData: function() {
    return "Mock Service 3 Data";
  }};
  return mother.createMvc("route2Controller", "../templates/route2.html", {}, null, {service3: service3Mock}).then(function (mvc) {
    var dom = new DuckDOM(mvc.view, mvc.scope);
    expect(dom.element("#data")[0].innerText).to.eql("Some Data, Mock Service 3 Data");
  });
});

The API from spec-helper.js has the mvc() function, which looks like this:

 mother.createMvc = function createMvc(controllerName, templateUrl, dependencies, options, appDependencies) {
 ...
 };

The dependencies parameter serves for injecting direct dependencies. The appDependencies parameter is of interest here. Anything that you pass in here as a map, will be registered with AngularJS during the bootstrap process under the name corresponding to the key. This is exactly what happens when we inject service3Mock in our test.

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

I’ll speak of testing directives using Duck-Angular in this post. This is a follow-up to the series of posts on using Duck-Angular for unit testing controller, views, and bindings (Part 1, Part 2, Part 3, and Part 4), Duck-Angular and the AngularJS-RequireJS-Seed now support AngularJS 1.2.9.

Our Example Directive

Let’s use a simple directive to illustrate our unit test. directive1.js has some code which formats US phone numbers, as below:

define([], function() {
    return function () {
        return {
            link: function (scope, element, attrs) {
                    attrs.$observe("phone", function(value) {
                        var rawPhoneNumber = value.replace(/[^0-9]/g, "");
                        rawPhoneNumber = rawPhoneNumber.slice(rawPhoneNumber.length - 10, 10);

                        var areaCode = rawPhoneNumber.substr(0,3);
                        var phonePrefix = rawPhoneNumber.substr(3,3);
                        var phoneSuffix = rawPhoneNumber.substr(6,4);

                        if (rawPhoneNumber.length === 10) {
                            var prettyPrintedNumber = "(" + areaCode + ")" + " " + phonePrefix + "-" + phoneSuffix;
                            element.text(prettyPrintedNumber);
                        }
                        else {
                            element.text(rawPhoneNumber);
                        }
                });
            }
        };
    };
});

The code is pretty simple, really. It observes the value of the phone attribute on an element (which is resolved from the injected scope variable phoneNumber), and modifies the resultant text of the associated element to the formatted phone number.

The Unit Test

The test, taken from directive1-test.js, is below:

define(["spec_helper"], function(mother) {
    describe("Phone Directive", function() {
        it("can format phone number", function() {
            var template = "<span phone='{{phoneNumber}}'>{{phoneNumber}}</span>";
            var scope = {phoneNumber: "1234567890"};
            return mother.compileDirective(template, scope).spread(function(s, element) {
                expect(element.text()).to.eql("(123) 456-7890");
            });
        });
    });
});

If you’ve followed along in the previous posts, this test will be very familiar to you. The only difference is that, instead of calling mother.createController(…) or mother.createMvc(…), we call mother.compileDirective(…). This is useful for when you are just interested in testing the directive in isolation. If you’re testing a complete partial view, mother.createMvc(…) should serve just as well.

One Caveat

There is one thing to keep in mind: if you modify the scope after the initial compilation, do remember to call s.$apply() before making any assertions. Here’s an example:

define(["spec_helper"], function(mother) {
    describe("Phone Directive", function() {
        it("can format phone number", function() {
            var template = "<span phone='{{phoneNumber}}'>{{phoneNumber}}</span>";
            var scope = {phoneNumber: "1234567890"};
            return mother.compileDirective(template, scope).spread(function(s, element) {
                expect(element.text()).to.eql("(123) 456-7890");
                s.phoneNumber = "5671234400";
                s.$apply();
                expect(element.text()).to.eql("(567) 123-4400");
            });
        });
    });
});

As to what compileDirective() does, most of it is pretty similar to binding views to scopes. The function is more or less similar to createMvc() with some of the fat stripped out.

Duck-Angular updated for AngularJS 1.2.9, plus a quick recap

Update

Since publishing the series of posts on using Duck-Angular for unit testing controller, views, and bindings (Part 1, Part 2, Part 3, and Part 4), I’ve upgraded Duck-Angular and the AngularJS-RequireJS-Seed to support AngularJS 1.2.9. Below is just a quick recap of some of the things I’ve talked about already. There is more detail in the links above.

Unit Testing and Application Patterns

  • Using a module loader lets you test a controller, service, etc. in isolation without having to bootstrap AngularJS app.
  • Using a module loader allows for loading third-party dependencies without having to declare them as AngularJS objects (best of both worlds).
  • Using RequireJS makes minification a no-brainer.
  • Don’t pollute the Javascript global namespace (anything like window.*). You can isolate your entire application from external pollution.
  • Simplify testing promises using Mocha-as-Promised, Chai-as-Promised, etc. Jasmine-as-Promised also exists.
  • If you’re testing on anything below IE9, don’t have console.log() in your code.
  • Referring to router paths directly like <a href="\#/route1"/> may lead to infinite $digest loop errors.
    • Replace that with ng-click which does the actual navigation.
    • Or, add target=”_self” attribute to anchor link.
  • If you’re still using AngularJS 1.0.x and it is not too late, move to AngularJS 1.2.x now. There are quite a few things that are better, for example:
    • AngularJS 1.2.x has request interceptors (1.0.x had only response interceptors).
    • Many bug fixes for IE. Plus extra incentive to not use IE8 any more, since 1.3 and up will no longer necessarily work on IE8.
  • Don’t manipulate the DOM in Javascript.
    • Bind DOM states to $scope variables (ng-show, etc.)
    • Isolate DOM-manipulating functions that you must have in their own modules.

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();
    d.resolve({});
    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.

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

Link to Part One of this Series on Angular Unit Testing using Duck-Angular

When we last left off, I’d discussed the structure of the application, and why I was using RequireJS (and Q). In this post, I’ll speak of the way the tests are set up. For the most part, it will parallel the application folder structure, but there are a few things that will be worth noting. For reference, I’m still using the AngularJS-RequireJS-Seed project to talk you through the details. The Duck-Angular project is here.

The files pertinent to the initial part of this post are:

  • test-main.html
  • test-main.js
  • test-setup.js
  • test.config.js

It might be easiest to explain these files by drawing parallels between them and the production bootstrap code.

Unit Test Setup

  • test-main.html is analogous to index.html. This is the file that kicks everything off. This may be run inside an actual browser like Chrome, or it may be run from the command line using libraries like Mocha-PhantomJS. Perusing this file indicates that it loads up test-main.js.
  • test-main.js does not have any analog in the production code because it does a bunch of test-specific things. Among them:
    • It lists the unit test files which will be run.
    • It sets up the parameters for Mocha, like timeout and the assertion style. It also enables the Mocha-as-Promised extension.
    • It sets up Chai’s assertion style (“bdd” in this case), and also enables Chai-as-Promised.
    • Finally, it runs the unit tests.
    • As part of test-main.js, the file test-setup.js is also loaded. test-setup.js is simply the combined unit test analog of bootstrap.js and app.js, but crucially, even though it has all these functions to setup/bootstrap the app, it does not actually execute them. Think of these functions as helper functions, which can be used by the unit test to set up the AngularJS environment. The important thing to note here is, if you’re using Duck-Angular, you’ll never need to all these methods directly. In this example, I’m using spec-helper.js a lot, and that’s the module which actually uses these bootstrapping functions defined in test-setup.js.
  • Finally, test.config.js maps the test-specific modules to the correct file paths.

The First Controller unit test

We are (mostly) set to write our first unit test against a controller. This one will probably be a little anticlimactic, because at the end of all this setup, all you will really do is load a RequireJS module, and use the resulting object in your test. But this does illustrate how easily you can test AngularJS objects if you don’t start them off…as AngularJS objects. But fret not, we’ll be writing more involved tests (views and controllers and services and whatnot) very soon 🙂

Let’s look at the controller that we want to unit test. This is from controller2.js

define([], function() {
  return function ($scope, $location, $q, service2) {
    var deferredLoaded = $q.defer();
    this.loaded = deferredLoaded.promise;
    $scope.go = function() {
        console.log("Was called");
        $location.path("/route1");
    };

    console.log("In controller2");
    service2.get().then(function(data) {
        $scope.data = data;
        console.log("Service2 returned");
        deferredLoaded.resolve();
    });
  };
});

The controller defines a method go() on its scope, which navigates to /route1. The other thing it does, on initialisation, is call service2.get(), presumably to get some data, which it then sets back on to the scope. This is an asynchronous operation. Having done this, it fulfills a promise which is available to anyone who might want to act on it. The variable ‘deferredLoaded’ is used for this purpose.

The Loaded Promise Convention for Controllers

In most cases, it makes sense to have the controller fulfill a promise to signal that it is done “loading”. Loading implies completing any and all asynchronous operations. This is very useful for predictable unit tests, because if there’s a promise that has not been fulfilled in time, and an assertion depends upon that promise having been fulfilled, you have unpredictable tests which sometimes pass, and sometimes fail. Duck-Angular expects your controller to have a loaded promise. Even if your controller does not perform any asynchronous operation, having a pre-resolved promise is simple boilerplate that can be abstracted away.

The first test that we’ll write simply asserts that the scope is set up as intended. We’ll mock out service2 so that we are only testing controller behaviour. Here is a snippet from controller2-test.js.

it("can set data on scope", function () {
  var service2 = { get: sinon.stub() };
  service2.get.returns(Q("Some Mock Data"));
  var location = { path: sinon.stub()};
  var scope = {};
  var controller = new Controller2Ctor(scope, location, Q, service2);
  return controller.loaded.then(function() {
    expect(scope.data).to.eql("Some Mock Data");
  });
});

So, let’s dissect the dependencies.

  • service2 has been stubbed to return “Some Mock Data“. Note that it returns a promise, instead of the value directly. This maintains the illusion that the client of service2 is still dealing with asynchronous code.
  • scope is just an empty object, no surprises here.
  • location is an object with a single stub method get(). Strictly speaking, this is not needed here; an empty object would have sufficed.
  • The $q dependency has been satisfied with Q. There is a reason why AngularJS’ $q library will not work (in many cases) inside unit tests. I’ll write a simple test to demonstrate this later, and explain why.

Anyway, the rest of the code is more or less easy to follow. We make an assertion about the state of scope after the controller.loaded promise is fulfilled, because, of course, that’s when we can be sure that the state of the scope has been altered. The other interesting thing to note is that we are actually returning a promise from the test. If we were using the raw Mocha library, we’d have to invoke done() to signal to Mocha that the test has completed. However, because we are using Mocha-as-Promised, we return a promise at the end of a test. Whether the promise is fulfilled or rejected, determines the success (or failure) of the unit test.

Sinon to the Fore

On without pause to the next controller test. This one will be similar in structure to the previous test, but will assert that triggering the go() method in the controller causes a navigation to /route1. Of course, in this restricted AngularJS-free sandbox, we won’t really perform this navigation; we will simply verify that $location.path() method is called with the appropriate parameter (“/route1” in this case).

Let’s look at the test. This is from controller2-test.js.

it("can navigate to another route", function () {
  var service2 = { get: sinon.stub() };
  service2.get.returns(Q("Some Mock Data"));
  var location = { path: sinon.stub()};
  var scope = {};
  var controller = new Controller2Ctor(scope, location, Q, service2);
  return controller.loaded.then(function() {
    scope.go();
    expect(location.path.calledWith("/route1")).to.be.ok;
  });
});

Mostly the same, except the part inside the fulfillment handler. The scope.go() method is fired, and we expect location.path() to be called with “/route1“. This kind of test is very useful when firming up logic inside controllers.

Inject what you Want, let AngularJS do the Rest

Many times, you may want to mock out only some of the dependencies, but need the rest to be fulfilled by the production dependencies. To demonstrate this, let’s look at controller1.js.

    define([], function() {
      return function ($scope, service1, $q) {
        var deferredLoaded = $q.defer();
        this.loaded = deferredLoaded.promise;

        console.log("In controller1");
        service1.get().then(function(data) {
            $scope.x = data;
            deferredLoaded.resolve();
        });
      };
    });

This is very similar to controller2.js, and simpler. All it does is get some data from service1, and set it on the $scope as x. Let’s assume that we want to unit test this controller, but we do not want to mock out service1‘s dependency. How do we go about doing this? Here’s the short answer, from controller1-test.js.

    define(["spec_helper", "Q"], function (mother, Q) {
      describe("Controller1 Test", function () {
        it("sets up real registered service if dependency is not explicitly provided", function () {
          var testScopeMock = {};
          return mother.createController("route1Controller", { $scope: testScopeMock, $q: Q}).then(function(controller) {
            expect(testScopeMock.x).to.eql("Real Service1 Data");
          });
        });
      });
    });

If you look at service1.js, it returns a promise with the value “Real Service1 Data“, and that is the expected value we are testing against. In the test above, we inject $scope and $q, but we do not inject the service1 dependency. Yet, that dependency is fulfilled. What sort of magic is happening inside mother.createController()?

A Gander inside Duck-Angular

As it turns out, not a lot. We’ll have more than one occasion to examine the guts of Duck-Angular, and this is one of them. Before I dive into the code inside mother.createController(), it will be instructive to review how you can retrieve a controller which has been registered with AngularJS.

The easiest way is through the $controller service. The $controller service itself may be accessed through the application’s injector. After that it is mostly a matter of asking for the controller by its registered name. However, to get the injector, we need to have bootstrapped the application. This is precisely what happens inside the createController() function. Here’s the relevant code from spec-helper.js.

mother.createController = function createController(controllerName, dependencies) {
  return initApp().spread(function (injector, app) {
    mother.injector = injector;
    var container = new Container(injector, app);
    var resourceBundleFactory = container.injector.get("ngI18nResourceBundle");

    return resourceBundleFactory.get("en").then(function (resourceBundle) {
      var controller = container.controller(controllerName, dependencies);
      dependencies.$scope.resourceBundle = resourceBundle.data;
      mother.resourceBundle = resourceBundle.data;
      return controller;
    });
  });
};

You can safely ignore all the resourceBundle calls. For now, suffice it to say that the initApp() function is called (remember, initApp() was defined in test-setup.js). After this, the Container is set up with the injector and the app. When the time comes to get the controller, we simply ask the Container to give it to us. This is the relevant code from duck-angular.js.

self.controllerProvider = self.injector.get("$controller");
....
this.controller = function (controllerName, dependencies) {
  var deferred = Q.defer();
  var controller = self.controllerProvider(controllerName, dependencies);
  controller.loaded.then(function () {
    deferred.resolve(controller);
  });
  return deferred.promise;
};

It basically translates to a simple call the the $controller service. The $controller service is responsible for accommodating our injected dependencies, as well as autowiring those which have not been explicitly injected.

In the next part of this series, I’ll talk of the nuances of using Q instead of $q, a nice way to mock $http‘s success()/failure() clauses, and start wiring up views to controllers in our unit tests, while digging more inside Duck-Angular’s internals.

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

AngularJS makes a lot of things easy, but one thing you still have to come to grips to, is deciding:

  • What to unit test.
  • How to unit test it.
  • How to unit test it easily.

I’m not saying it’s hard, but it can sometimes be convoluted. Let’s answer the first question.

What to unit test

Normally, we want to be unit testing logic. This would usually translate to unit testing controllers. However, because AngularJS allows us to embed expressions inside our templates, it’d be nice to be able to unit test template logic too. To this, people might object because, of course, Selenium (for example) would be the choice to perform end-to-end testing. Which I agree to completely, except that end-to-end testing is not synonymous with controller-view integration testing.

In a past life, when I was working with Silverlight, using MVVM (Model, View, View-Model), my prevailing philosophy was to move most (if not all) the view related logic to the view model, so that we could unit test the view model thoroughly. The only remaining bugs would be a faulty binding. As it turns out, using this philosophy is quite useful in AngularJS as well. However, I do want to go a step further to be able to unit test these bindings as well. I also would want to be able to test the state of HTML elements. Normally, a higher-level acceptance test would do this, but the higher up the testing abstraction one moves to, the harder it is to pinpoint low-level issues when they arise.

Let’s go one step further. I’d also like to be able to unit-test interactions. This basically means verifying what would happen in a controller/view if the user interacted with a certain UI element.

So, in short, what I want to be able to test are:

  • Controllers
  • Template-Controller binding
  • View interactions

In this series, I shall start with a simple project structure, and use Duck-Angular, a framework I’ve developed to show how you can do all of the above. I’ll walk you through the code of Duck; hopefully, this will serve to illustrate the design thinking behind AngularJS too.

What is Duck-Angular?

Duck-Angular is a container for bootstrapping and testing AngularJS views and controllers in memory. No browser or external process is needed. Duck-angular is available as a Bower package. Install it using bower ‘install duck-angular’.

Include it using RequireJS’ define(). Your controller/service/object initialisation scripts need to have run before you use Duck-Angular. Put them in script tags, or load them using a script loader like RequireJS or Inject.

The Github repository is here. As we go on, we’ll see how Duck-Angular works, and how you can use it in your unit tests.

How to unit test it

Say whatever you will about AngularJS, it is very hackable. Some of that can be attributable to the Javascript language itself, but there are some very good design decisions that the development team has taken. The two points which come to mind are:

  • Copious event publishing
  • Unintrusive dependency injection model

As we shall see, each of these points make it possible to write unit tests for almost every part of your application code.

Which Libraries?

Before beginning, I shall list out the libraries I’ve used for this example project:

  • Duck-Angular (A container for bootstrapping and testing AngularJS views and controllers in memory: no browser or external process needed)
  • RequireJS (Isolates libraries, including AngularJS for your specific context)
  • Q (Used for bootstrapping the Angular application, and mocking $q)
  • Mocha and Chai (Unit testing framework, and assertion framework, respectively)
  • Sinon (Mocking/Stubbing framework)
  • Mocha-as-Promised and Chai-as-Promised (Extensions to Mocha and Chai to ease handling of promises)

The example project to follow along is AngularJS-RequireJS-Seed Project. It is in a bit of a flux at the moment, but we’ll fix that soon.

Project Structure

With the imminent advent of the ES6 Module System, it only makes sense to structure Javascript code as proper, first-class application code, instead of a bunch of script tags. To this end, I’ve used RequireJS to structure the project. There are several other beneficial effects of using RequireJS apart from this isolation, which we’ll soon see. The folder structure of the app is as below:

js
 └───public
     ├───js
     │   ├───app
     │   │   ├───controller
     │   │   ├───directive
     │   │   ├───factory
     │   │   └───services
     │   ├───lib
     │   └───test
     │       ├───lib
     │       └───unit
     │           ├───controllers
     │           ├───services
     │           └───ui
     └───templates

Some quick notes about the folder structure.

  • The test/lib directory contains test-specific libraries like Mocha, Chai, etc.
  • This folder structure is not an absolute must; it just makes it easier to explain the application organisation.

Apache Server Configuration

The choice of server to serve these static assets is up to you, but just in case you’re using Apache, here’s the snippet from httpd.conf that you might find useful. Of course, you’ll have to modify the local directory path to your JS project.

    ProxyPreserveHost On
    ProxyRequests Off
    ProxyPass /example/static !
    Alias /example/static/ "C:/projects/AngularJS-RequireJS-Seed/example-app/src/main/resources/public/"

    Header set Cache-control "no-cache,must-revalidate"
    Header set pragma "no-cache"
    Header set Expires "Sat, 1 Jan 2000 00:00:00 GMT"

    <Directory "C:/projects/AngularJS-RequireJS-Seed/example-app/src/main/resources/public">
       Options Indexes FollowSymLinks MultiViews ExecCGI
       AllowOverride All
       Order allow,deny
       Allow from all
    </Directory>

Bootstrapping the application

The relevant snippet which starts off the app is in index.html:

<head>
  <title>RequireJS+Angular</title>
    <script src="/example/static/js/lib/require.js" data-main="/example/static/js/app/bootstrap.js"></script>
</head>

Looking at the code above, we see that the first Javascript file to be loaded and run, is bootstrap.js. The bootstrap.js code is very short, and is shown below.

    require(["app.config"], function(config) {
    require(["app/app"], function(app) {
    app.bootstrap(app.init());
    });
});

This code loads up two files, app.config.js, and app.js in sequence (They should not be loaded parallely, hence the nested require’s). Once the app module has been loaded, it is initialised (init()), and bootstrapped (bootstrap()). The two phases are separate so as to allow the developer to add extra test-specific initialisation after init(), but before bootstrap().

Now, the question is: what do bootstrap() and init() do? Here is the init() function from the app.js module:

var init = function() {
  var app = angular.module('ExampleApp', []);
  services.init(app);
  libs.init(app);
  directives.init(app);
  controllers.init(app);
  factories.init(app);
  app.config(['$routeProvider', function ($routeProvider) {
    $routeProvider.
      when('/navigation', {templateUrl: '/example/static/templates/navigation.html', controller: 'navigationController'}).
      when('/route1', {templateUrl: '/example/static/templates/route1.html', controller: 'route1Controller'}).
      when('/route2', {templateUrl: '/example/static/templates/route2.html', controller: 'route2Controller'}).
      otherwise({redirectTo: '/navigation'});
  }]);
  return app;
};

Parts of this may be familiar to you, for example, the wiring up of the routes. This is where we manually bootstrap the application, calling it “ExampleApp”. To set up controllers, services, factories, etc., I follow a process which might seem a little unfamiliar. Not to worry, it’s pretty simple. The core idea here, which will resurface when we actually get down to testing the entities, is that every Angular controller, factory, service, etc. starts off as a RequireJS module.

Make every Angular entity start off as a RequireJS module. Why?

Why is this important? Think of any AngularJS controller. You declare it like so:

    angular.controller("SomeController", function(...) {
       // Controller code
}

Now, let’s pause for a second, and think of how we can get a hold of the instance of that controller if we ever wanted to unit test it? Well, it is an AngularJS controller, so you’d need to be able to ask AngularJS to instantiate it, using AngularJS’ dependency injection framework (in this case, we’d use $injector.get(), or the $controller service).

This implies that you will need to at least initialise your AngularJS app before you can use a controller object. Kind of sucks, huh? But, if the controller’s constructor function started out as a RequireJS module, you’ll not need to bootstrap your app to create a controller. All you really do is call a constructor function, and you’re done. Not only that, you have full control over what dependencies you inject inside your controller, because you’re invoking the constructor function yourself. Later on, we’ll see how to specify only a subset of a controller’s dependencies, while the rest are populated by their default (production) dependencies.

So, to register controllers with AngularJS, controllers.js does this:

  define(["route1-controller",
  "route2-controller",
  "navigation-controller"
], function (route1Ctrl, route2Ctrl, navigationCtrl) {
    var init = function (app) {
      app.controller('route1Controller', ['$scope', 'service1', '$q', route1Ctrl]);
      app.controller('route2Controller', ['$scope', '$location', '$q', 'service2', route2Ctrl]);
      app.controller('navigationController', ['$scope', '$location', navigationCtrl]);
  };
  return {init: init};
});

All it does is return an object with an init() function. This function get the controller constructor functions (route1Ctrl, route2Ctrl, navigationCtrl), and register them with AngularJS like normal. The init() function is triggered in app.js, like so:

    controllers.init(app);

This is exactly identical to how the services, factories, and directives are registered with AngularJS. What does the bootstrap() method in app.js do? Let’s see.

var bootstrap = function(app) {
  var deferred = Q.defer();
  var injector = angular.bootstrap($('#ExampleApp'), ['ExampleApp']);
  deferred.resolve([injector, app]);

  return deferred.promise;

}

It simply takes the Angular app object (which has hopefully been initialised inside the init() method), and actually bootstraps it, binding it to a DOM element in the HTML. It then returns a Q promise, which contains an object with two fields.

  • The injector, which is responsible for Angular’s dependency injection. This is not so useful in the actual production code, but is handy in unit tests, when you need to get a handle to something registered with AngularJS.
  • The app, which is the app object itself. You could use it in your unit tests to perform further configuration changes.

The above two methods, used in conjunction inside bootstrap.js, starts up our app.

In the next post, I shall discuss how the test environment is set up.

RequireJS-AngularJS solutions for (almost) every constraint

Update: I’ve added a seed project which uses AngularJS and RequireJS, for option #3 below. It’s located here.

So, you like RequireJS. No, you probably adore it. Enough to want to evangelise it’s benefits to anyone who will listen. And now you have AngularJS, which is pretty neat. Or so you think. At least, you probably are using it on your project.

There’s one problem. Your team has not bought into the benefits of module loaders. They think nothing of putting a zillion script tags in your HTML. It doesn’t matter whether the opposing faction is one tech lead, or a few peer developers. Or maybe they are fine with the idea of module loading, but balk at the idea of wrapping all their Javascript code in “ugly” define()’s.

I’ll describe ways to inject RequireJS into your AngularJS projects with minimal disruption, depending upon the set of constraints you find yourself saddled with. I usually say that there’s no “best” way to use RequireJS in your project. However, there are certainly preferred ways to use it, and I’ll note them as I go along.

This post is still a work in progress; expect updates.

1) Use RequireJS piecemeal, wherever you’re writing new code.

  • You’ll use both Angular’s Dependency Injection and RequireJS.
  • This does not get rid of top-level script tags because you’re not in control of the project structure/Angular bootstrapping.
  • You’ll have RequireJS configurations inside your controllers, which might look a bit ugly.
  • You will have to set up Angular controllers/factories/services, in order to unit test them. Since the smaller building blocks can simply come from RequireJS modules, some unit tests might not need this initialisation.

2) Use RequireJS during bootstrap, using Angular’s Dependency Injection for all modularisation.

  • You won’t utilise RequireJS module dependency resolution features, with this method.
  • You will get rid of script tags.
  • You will have to necessarily bootstrap your Angular app manually, with this approach.
  • Angular controller/service/object declarations will not need to be defined inside RequireJS modules.
  • You will have to set up Angular controllers/factories/services, in order to unit test them. Since all building blocks will be Angular components, you’ll have to do this for every unit test.

3) Use RequireJS during bootstrap, using Angular’s Dependency Injection for service/factory/controller/coarse-grained objects only. All service/factory/controller declarations come from RequireJS modules.

Note: I prefer this approach.

  • This allows you to unit test controller/service logic without having to initialise Angular modules in unit tests.
  • You will get rid of script tags.
  • You will have to necessarily bootstrap your Angular app manually, with this approach.
  • You’ll invoke RequireJS inside controllers/services, etc., as needed.