Angular ui router unit testing (states to urls)

Been having this issue as well, and finally figured out how to do it.

Here is a sample state:

angular.module('myApp', ['ui.router'])
.config(['$stateProvider', function($stateProvider) {
    $stateProvider.state('myState', {
        url: '/state/:id',
        templateUrl: 'template.html',
        controller: 'MyCtrl',
        resolve: {
            data: ['myService', function(service) {
                return service.findAll();
            }]
        }
    });
}]);

The unit test below will cover testing the URL w/ params, and executing the resolves which inject its own dependencies:

describe('myApp/myState', function() {

  var $rootScope, $state, $injector, myServiceMock, state = 'myState';

  beforeEach(function() {

    module('myApp', function($provide) {
      $provide.value('myService', myServiceMock = {});
    });

    inject(function(_$rootScope_, _$state_, _$injector_, $templateCache) {
      $rootScope = _$rootScope_;
      $state = _$state_;
      $injector = _$injector_;

      // We need add the template entry into the templateCache if we ever
      // specify a templateUrl
      $templateCache.put('template.html', '');
    })
  });

  it('should respond to URL', function() {
    expect($state.href(state, { id: 1 })).toEqual('#/state/1');
  });

  it('should resolve data', function() {
    myServiceMock.findAll = jasmine.createSpy('findAll').and.returnValue('findAll');
    // earlier than jasmine 2.0, replace "and.returnValue" with "andReturn"

    $state.go(state);
    $rootScope.$digest();
    expect($state.current.name).toBe(state);

    // Call invoke to inject dependencies and run function
    expect($injector.invoke($state.current.resolve.data)).toBe('findAll');
  });
});

If you want to check only the current state's name it's easier to use $state.transitionTo('splash')

it('should transition to splash', inject(function($state,$rootScope){
  $state.transitionTo('splash');
  $rootScope.$apply();
  expect($state.current.name).toBe('splash');
}));

I realize this is slightly off topic, but I came here from Google looking for a simple way to test a route's template, controller, and URL.

$state.get('stateName')

will give you

{
  url: '...',
  templateUrl: '...',
  controller: '...',
  name: 'stateName',
  resolve: {
    foo: function () {}
  }
}

in your tests.

So your tests could look something like this:

var state;
beforeEach(inject(function ($state) {
  state = $state.get('otherwise');
}));

it('matches a wild card', function () {
  expect(state.url).toEqual('/path/to/page');
});

it('renders the 404 page', function () {
  expect(state.templateUrl).toEqual('views/errors/404.html');
});

it('uses the right controller', function () {
  expect(state.controller).toEqual(...);
});

it('resolves the right thing', function () {
  expect(state.resolve.foo()).toEqual(...);
});

// etc

For a state that without resolve:

// TEST DESCRIPTION
describe('UI ROUTER', function () {
    // TEST SPECIFICATION
    it('should go to the state', function () {
        module('app');
        inject(function ($rootScope, $state, $templateCache) {
            // When you transition to the state with $state, UI-ROUTER
            // will look for the 'templateUrl' mentioned in the state's
            // configuration, so supply those templateUrls with templateCache
            $templateCache.put('app/templates/someTemplate.html');
            // Now GO to the state.
            $state.go('someState');
            // Run a digest cycle to update the $state object
            // you can also run it with $state.$digest();
            $state.$apply();

            // TEST EXPECTATION
            expect($state.current.name)
                .toBe('someState');
        });
    });
});

NOTE:-

For a nested state we may need to supply more than one template. For ex. if we have a nested state core.public.home and each state, i.e. core, core.public and core.public.home has a templateUrl defined, we will have to add $templateCache.put() for each state's templateUrl key:-

$templateCache.put('app/templates/template1.html'); $templateCache.put('app/templates/template2.html'); $templateCache.put('app/templates/template3.html');

Hope this helps. Good Luck.