M.K. Safi
M.K. Safi

Reputation: 7019

Unit testing the config phase in AngularJS

I'm trying to learn how to write unit tests for AngularJS. I started at the beginning, with

angular.module( ... ).config( ... )

I wanna test what's inside config. Here's how the relevant portions look like:

angular.module('ogApp', ['ngCookies','ui.router','ogControllers','ogServices','ogDirectives','ogMetricsData'])

.config([
  '$stateProvider', '$locationProvider',
  function ($stateProvider, $locationProvider) {
    $stateProvider.
      state('login', {
        templateUrl: 'connect.html'
      }).state('addViews', {
        templateUrl: 'add-views.html'
      }).state('dashboard', {
        templateUrl: 'dashboard.html'
      });
    $locationProvider.
      html5Mode(true).
      hashPrefix('!');
  }
]);

I'm thinking the easiest way to test this code is to inject mocks for $stateProvider and $locationProvider. Then execute the config phase. After that, assert how $stateProvider and $locationProvider should look like.

If my thinking is right, my problem then is, I have no idea how to inject those mocks into the module and execute its config phase from a test.

Could you show me how to test this code?

Upvotes: 25

Views: 10026

Answers (3)

dprothero
dprothero

Reputation: 2722

I would create a factory that points to a function... that function is then also called within the config function. That way you can unit test the factory:

angular.module('ogApp', ['ngCookies','ui.router','ogControllers','ogServices','ogDirectives','ogMetricsData']);

// Configuration factory for unit testing
angular.module('ogApp')
.factory('configuration', configuration);

configuration.$inject = ['$stateProvider', '$locationProvider'];

function configuration($stateProvider, $locationProvider) {
  return {
    applyConfig: function () {
      $stateProvider.
        state('login', {
          templateUrl: 'connect.html'
        }).state('addViews', {
          templateUrl: 'add-views.html'
        }).state('dashboard', {
          templateUrl: 'dashboard.html'
        });
      $locationProvider.
        html5Mode(true).
        hashPrefix('!');
    };
}

// Call above configuration function from Angular's config phase
angular.module('ogApp')
.config([
  '$stateProvider', '$locationProvider',
  function ($stateProvider, $locationProvider) {
    var config = configuration($stateProvider, $locationProvider);
    config.applyConfig();
  }
]);

You can unit test the configuration factory and inject mocks just like you would with any other factory.

Upvotes: -1

fidian
fidian

Reputation: 853

Here's how to access your provider for unit testing:

describe('yourProvider', function () {
    var provider;

    // Get the provider
    beforeEach(module('app', function (yourProvider) {
        // This callback is only called during instantiation
        provider = yourProvider;
    });

    // Kick off the above function
    beforeEach(inject(function () {}));

    it('does its thing', function () {
        expect(provider.someMethod()).toEqual('your results');
    });
});

I have not yet figured out a really simple way to inject a mock, but you can easily spy on methods and that's close enough. If you need a mock returned from the dependency provider's .$get() method you can do that with another spy as well. This example illustrates returning a mock and setting up an additional spy.

describe('yourProvider', function () {
    var dependency, mock, provider;

    beforeEach(module('app', function (dependencyProvider) {
        dependency = dependencyProvider;
        mock = jasmine.createSpyObj('dependency', [
            'methodsGoHere'
        ]);
        spyOn(dependency, 'methodName');
        spyOn(dependency, '$get').andReturn(mock);
    }, function (yourProvider) {
        provider = yourProvider;
    });

    beforeEach(inject(function () {}));

    it('does its thing', function () {
        expect(provider.someMethod()).toEqual('your results');
        expect(dependency.methodName).toHaveBeenCalled();
    });

    it('returns the mock from $get', function () {
        expect(dependency.$get).toBe(mock);
    });
});

Upvotes: 11

Jason
Jason

Reputation: 15931

You can use Jasmine's createSpy and createSpyObj to create the mock services, and angular-mocks.js to inject them.

More instructions on injecting mocks here: Injecting a mock into an AngularJS service

In this test I wrote for a directive of mine you can see the following:

  • Line 9 Include angular-mock from the google cdn
  • Line 19 & 20 Create a fake rootScope object
  • Line 21 & 22 Create a fake q service
  • Line 42 Setup the provider to inject the fakes into the service
  • Line 48 Instantiate the service that has the fakes (this service is injected into the directive under test)
  • Line 53 Call the method being tested
  • Line 55 - 59 Assert on the state of the fakes

Upvotes: 0

Related Questions