42

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.

4
  • is neither of the 2 good enough? Commented Jun 17, 2013 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?' Commented Dec 10, 2013 at 13:36
  • 1
    @fwielstra, Yes, this kind of test feels like double entry. Then again, I find most tests feel that way. On the other hand, I find double entry useful. Commented May 29, 2014 at 18:29
  • the answer here worked for me. mock the $httpBackend stackoverflow.com/questions/17963717/… Commented Jun 18, 2015 at 16:48

3 Answers 3

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')
  });
});
Sign up to request clarification or add additional context in comments.

4 Comments

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')"
This looks like a pretty useless test to me. Not to say the style proposed by others is better.
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"?
@AndreschSerj, the OP asked how do I unit test routes. Your comment would make a great system test.
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);

  });
}); 

4 Comments

This is perfect. $rootScope.$digest() was exactly what I needed.
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!
Just need to add $httpBackend with proper expectGET. Here is my example plnkr.co/edit/j1o0iu?p=preview
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'));
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);
});

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.