41

As we see here in http://docs.angularjs.org/tutorial/step_07,

angular.module('phonecat', []).
  config(['$routeProvider', function($routeProvider) {
  $routeProvider.
      when('/phones', {templateUrl: 'partials/phone-list.html',   controller: PhoneListCtrl}).
      when('/phones/:phoneId', {templateUrl: 'partials/phone-detail.html', controller: PhoneDetailCtrl}).
      otherwise({redirectTo: '/phones'});
}]);

routing test is suggested to be done with e2e test,

  it('should redirect index.html to index.html#/phones', function() {
    browser().navigateTo('../../app/index.html');
    expect(browser().location().url()).toBe('/phones');
  });

However, I think the '$routeProvider' config is done with a single function, function($routeProvider), and we should be able to unit test without involvement of browser since I think routing function does not require browser DOM.

For example,
when url is /foo, templateUrl must be /partials/foo.html and controller is FooCtrl
when url is /bar, templateUrl must be /partials/bar.html and controller is BarCtrl

It is a simple function IMO, and it should also be tested in a simple test, a unit test.

I googled and searched for this $routeProvider unit test, but no luck yet.

I think I may borrow some code from here but couldn't make it yet, https://github.com/angular/angular.js/blob/master/test/ng/routeSpec.js.

allenhwkim
  • 27,270
  • 18
  • 89
  • 122
  • is neither of the 2 good enough? – Maarten Jun 17 '13 at 11:50
  • One does have to wonder whether this would produce a unit test that provides actual value, though; most route configuration is just that, configuration, and the unit test would boil down to 'Is /some/route equal to /some/route?' – cthulhu Dec 10 '13 at 13:36
  • 1
    @fwielstra, Yes, this kind of test feels like [double entry](http://en.wikipedia.org/wiki/Double-entry_bookkeeping_system). Then again, I find most tests feel that way. On the other hand, I find double entry useful. – zhon May 29 '14 at 18:29
  • the answer here worked for me. mock the $httpBackend http://stackoverflow.com/questions/17963717/can-we-write-unit-test-for-angularjs-routeprovider – ColacX Jun 18 '15 at 16:48

3 Answers3

63

Why not just assert the route object is configured correctly?

it('should map routes to controllers', function() {
  module('phonecat');

  inject(function($route) {

    expect($route.routes['/phones'].controller).toBe('PhoneListCtrl');
    expect($route.routes['/phones'].templateUrl).
      toEqual('partials/phone-list.html');

    expect($route.routes['/phones/:phoneId'].templateUrl).
      toEqual('partials/phone-detail.html');
    expect($route.routes['/phones/:phoneId'].controller).
      toEqual('PhoneDetailCtrl');

    // otherwise redirect to
    expect($route.routes[null].redirectTo).toEqual('/phones')
  });
});
zhon
  • 1,610
  • 1
  • 22
  • 31
  • 1
    When i try to test the router i'm getting "'undefined' is not an object (evaluating '$route.routes[null].redirectTo')" for the last scenario "expect($route.routes[null].redirectTo).toEqual('/phones')" – Brune Jul 24 '14 at 13:24
  • 13
    This looks like a pretty useless test to me. Not to say the style proposed by others is better. – Duarte Cunha Leão Sep 26 '14 at 15:08
  • 2
    This won't test if based on states routes are available/callabe. For instance, how does this cover the case "I am not logged in and try to access a restricted recource"? – Andresch Serj Feb 12 '15 at 09:16
  • @AndreschSerj, the OP asked how do I unit test routes. Your comment would make a great system test. – zhon Dec 08 '16 at 19:51
45

I think you should be able to test the $routeProvider like this:

angular.module('phonecat', []).
  config(['$routeProvider', function($routeProvider) {
  $routeProvider.
      when('/phones', {templateUrl: 'partials/phone-list.html',   controller: PhoneListCtrl}).
      when('/phones/:phoneId', {templateUrl: 'partials/phone-detail.html', controller: PhoneDetailCtrl}).
      otherwise({redirectTo: '/phones'});
}]);


it('should test routeProvider', function() {
  module('phonecat');

  inject(function($route, $location, $rootScope) {

    expect($route.current).toBeUndefined();
    $location.path('/phones/1');
    $rootScope.$digest();

    expect($route.current.templateUrl).toBe('partials/phone-detail.html');
    expect($route.current.controller).toBe(PhoneDetailCtrl);

    $location.path('/otherwise');
    $rootScope.$digest();

    expect($location.path()).toBe('/phones/');
    expect($route.current.templateUrl).toEqual('partials/phone-list.html');
    expect($route.current.controller).toBe(PhoneListCtrl);

  });
}); 
Juho Salmio
  • 571
  • 4
  • 5
  • 2
    This is perfect. `$rootScope.$digest()` was exactly what I needed. – marcoseu May 17 '13 at 19:43
  • 1
    This does not work as Angular wants to trigger a HttpBackend call to retrieve the template through Ajax... solution above this one (from zhon) works though! – alchemication Jan 23 '14 at 21:14
  • 5
    Just need to add $httpBackend with proper expectGET. Here is my example http://plnkr.co/edit/j1o0iu?p=preview – ghiden Mar 19 '14 at 09:46
  • 1
    I think this solution is better as it can test route parameter resolution as well. instead of using $httpBackend, you can load your template using something like: beforeEach(module('partials/browse-view.html')); – Olivier Amblet Apr 23 '14 at 12:23
5

Combining the two previous answers, if you want to test the router as a black box that is successfully routing where it should (not the controller ets configs in themselves), whatever the routes might be:

// assuming the usual inject beforeEach for $route etc.
var expected = {};
it('should call the right controller for /phones route', function () { 
    expected.controller = $route.routes['/phones'].controller;
    $location.path('/phones');
    $rootScope.$digest();
    expect($route.current.controller).toBe(expected.controller);
});

it('should redirect to redirectUrl from any other route', function () {
    expected.path = $route.routes[null].redirectTo;
    $location.path('/wherever-wrong');
    $rootScope.$digest();
    expect($location.path()).toBe(expected.path);
});
pansay
  • 687
  • 8
  • 11