ngMock Helpers

Let’s take a controller test straight out of the Angular JS Docs:

describe('PasswordController', function() {  
  beforeEach(module('app'));

  var $controller;

  beforeEach(inject(function(_$controller_){
    // The injector unwraps the underscores (_) from around the parameter names when matching
    $controller = _$controller_;
  }));

  describe('$scope.grade', function() {
    it('sets the strength to "strong" if the password length is >8 chars', function() {
      var $scope = {};
      var controller = $controller('PasswordController', { $scope: $scope });
      $scope.password = 'longerthaneightchars';
      $scope.grade();
      expect($scope.strength).toEqual('strong');
    });
  });
});

If you are familiar with the syntax of behavior driven development testing frameworks, such as Mocha and Jasmine, you already know what describe() and it() are used for.

There are three peculiar things, though that an Angular JS novice would spot right away: module(‘app’), _$controller_, and inject(…). These helpers are not part of the testing framework itself, but are included in the optional Angular Mocks (ngMock) module. ngMock offers many additional niceties, in particular the $httpBackend interface for mocking XHR requests. In this post though, I will look at the three ones, mentioned above.

Where do these come from?

When setting up the testing environment for you Angular JS app, you are advised to make sure and install and add the Angular Mocks module:

bower install angular-mocks —save-dev

Depending on whether you are using an automated test executor or not, you should make sure to include the Angular Mocks before the start of your tests. For example, in case you are using the popular Karma runner, the <BOWER_COMPONENTS>/angular-mocks/angular-mocks.js file must be present in your Karma configuration file:

...

// list of files / patterns to load in the browser
// Make sure to add the angular-mocks.js file
files: [  
  '...',
  '../www/lib/angular-mocks/angular-mocks.js', 
],

...

The module() and inject() helper methods are both part of the angular.mock module. When Angular Mocks is included, it looks for whether Jasmine or Mocha are present, and attaches these two methods to window. This way they are easily accessible across all your unit tests:

// angular-mocks.js

if (window.jasmine || window.mocha) {  
    ...
    window.module = angular.mock.module = function() { ... }
    ...
    window.inject = angular.mock.inject = function() { ... }
}

These two do exactly what they are supposed to. module(...) loads a particular module, whereas inject(...) manually injects a given component from that module.

What about the underscores?

Look at the following example:

// In testing, we'd often want to inject a particular component
// and reuse it across tests

var myService;

beforeEach(inject(myService) {  
    // This is ambiguous ...
    // it will make use of the local myService variable
    myService = myService;
});

it('uses my service', function() {  
    //myService is undefined!!!
    myService.serviceMethod(); 
});

Adding an underscore before and after a desired parameter helps distinguish the local variable from the global one we want to set to:

var myService;

beforeEach(inject(_myService_) {  
    // It is clear now what we want to do ...
    myService = _myService_;
});

it('uses my service', function() {  
    // The service method call will work
    myService.serviceMethod(); 
});

This so called “underscore wrapping” is also made possible by Angular Mocks module. Whenever you wrap an expected component name with underscores, Angular Mocks will make sure to “unwrap” it and inject the proper dependency.

Preslav Rachev

I build search engines, CRMs, and other useful stuff. I'm a professional Java engineer and an enthusiast frontend hacker, who loves writing and sharing ideas.

Munich, Germany http://intro.preslav.me

Subscribe to Preslav's Thoughts and Ramblings

Get the latest posts delivered right to your inbox.

or subscribe via RSS with Feedly!