dimsuz
dimsuz

Reputation: 9207

Unit-test of ui-router: after $state.transitionTo() unable to get $state.current to contain current state

I am trying to write unit-test for ui-router's state and can not get it to work so that $state.current would contain a correct state inside the controller after I call $state.transitionTo() inside test.

This works in browser (where transitionTo is called by ui-router internally), but does not in unit-tests (using Karma) - where I call it explicitly.

Here's the code for the controller:

angular.module( 'ngBoilerplate.about', [
  'ui.router'
])

.config(function config( $stateProvider ) {
  $stateProvider.state( 'about', {
    url: '/about',
    views: {
      "main": {
        controller: 'AboutCtrl',
        templateUrl: 'about/about.tpl.html'
      }
    },
    data: {
      title: "About"
    }
  });
})

.controller( 'AboutCtrl', function AboutCtrl( $scope, $state ) {
  console.log($state.get('about'));
  console.log($state.current);
  $scope.title = $state.current.data.title;
})

;

And here is the test code (Jasmine) in about.spec.js:

describe( 'AboutCtrl', function() {
  var $scope, createController;

  beforeEach( module( 'ngBoilerplate.about' ) );

  beforeEach( inject( function( $controller, $rootScope, $state, $stateParams) {
    $scope = $rootScope.$new();
    createController = function() {
      $state.transitionTo('about', {});
      $rootScope.$digest();
      return $controller( 'AboutCtrl', { $scope: $scope, $state: $state });
    };
  }));

  it( 'should have title for about set', inject( function() {
    var controller = createController();
    expect($scope.title).toEqual('About');
  }));

});

This produces the results:

LOG: Object{url: '/about', views: Object{main: Object{controller: ..., templateUrl: ...}}, data: Object{title: 'About1'}, name: 'about'}
LOG: Object{name: '', url: '^', views: null, abstract: true}
Chrome 29.0.1547 (Linux) AboutCtrl should have title for about set FAILED
    TypeError: Cannot read property 'title' of undefined

Which shows that state is being inited OK, but it does not change for some reason, stays in a roughly initialized state. Again: this code when invoked in a real app environment shows the correctly filled $state.current which has all the data etc.

I have looked at ui-router's tests and it looks like they do exactly the same thing ($q.flush() is a decorated $rootScope.$digest() there), so I guess this should work.

Any hints? (I'm a bit noob with everything angular so far :)

Upvotes: 4

Views: 3845

Answers (2)

ailveen
ailveen

Reputation: 583

Remember, the ui router will initialize your controller and will inject the scope object into it, while in this test, you instantiated your own controller passing your own scope object. Your controller is not the same with the one instantiated by the ui router, and hence their scope objects are different references too.
I guess if you need to "unit test" a ui-router then test only the state parameters, but not scoped objects inside the controller. If you need to test scoped objects, create a unit test for the controller.

Upvotes: 0

codebreach
codebreach

Reputation: 2220

Have you considered using $rootScope.$apply() instead of $rootScope.$digest() in the createController method?

Upvotes: 2

Related Questions