Tag Archives: requirejs

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.

When your RequireJS-minified code misbehaves…

This is more or less directly taken from a note I dropped to my team after investigating some weird behaviour which was occurring only in the minified version of our code. Now I do need to point out that in many cases, the issue with minified code will more or less identify itself if you’re using AngularJS and have not written minification-proof code. But, but…if you’re going in blind, or are simply not sure what is wrong, hopefully this technique will help.

Preliminaries

When faced with minified code which misbehaves, you may attribute it to one (or both) of the following stages:

  • Concatenation: This is where RequireJS will trace dependencies and put everything in one single file, in the correct order. Any file may be excluded from this concatenation by using the excludeShallow option. The relevant link is here.
  • Obfuscation: This is when whitespace is stripped, names are shortened, and statements are mangled while maintaining syntactic and semantic correctness. This is a global option and can be turned off by setting optimize to none. RequireJS uses the UglifyJS library by default. This implies that if you use the excludeShallow option for all files, and set optimize to none, your resulting minified source will be the same as your original, unminified, source.

Steps to debug

  • Use excludeShallow for all files, and set optimize to none, initially.
  • Minify your source and check to see if it is causing any problems.
  • If not, start removing excludeShallow from files, until you can reproduce the issue.
  • Narrow it down to the file/files which are responsible for this misbehaviour. Fix the issue, or exclude it from the minification step.
  • Turn optimize back on (set it to uglify), and verify that nothing else is broken.
  • Enjoy!

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.

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.