user769096
user769096

Reputation:

Unit testing controller which uses $state.transitionTo

within a controller i have a function which uses $state.transitionTo to "redirect" to another state.

now i am stuck in testing this function, i get always the error Error: No such state 'state-two'. how can i test this? it its totally clear to me that the controller does not know anything about the other states, but how can i mock this state?

some code:


angular.module( 'mymodule.state-one', [
  'ui.state'
])

.config(function config($stateProvider) {
  $stateProvider.state('state-one', {
    url: '/state-one',
    views: {
      'main': {
        controller: 'MyCtrl',
        templateUrl: 'mytemplate.tpl.html'
      }
    }
  });
})

.controller('MyCtrl',
  function ($scope, $state) {
    $scope.testVar = false;
    $scope.myFunc = function () {
      $scope.testVar = true;
      $state.transitionTo('state-two');
    };

  }
);

describe('- mymodule.state-one', function () {

  var MyCtrl, scope

  beforeEach(module('mymodule.state-one'));

  beforeEach(inject(function ($rootScope, $controller) {

    scope = $rootScope.$new();

    MyCtrl = $controller('MyCtrl', {
      $scope: scope
    });

  }));

  describe('- myFunc function', function () {
    it('- should be a function', function () {
      expect(typeof scope.myFunc).toBe('function');
    });

    it('- should test scope.testVar to true', function () {
      scope.myFunc();
      expect(scope.testVar).toBe(true);
      expect(scope.testVar).not.toBe(false);
    });
  });
});

Upvotes: 14

Views: 14946

Answers (3)

Dashu
Dashu

Reputation: 347

Also if you want to expect on that the transition was made like so

expect(state.current.name).toEqual('state-two')

then you need to scope.$apply before the expect() for it to work

Upvotes: 2

Christoph
Christoph

Reputation: 27995

Disclaimer: I haven't done this myself, so I totally don't know if it will work and is what your are after.

From the top of my head, two solutions come to my mind.

1.) In your tests pre configure the $stateProvider to return a mocked state for the state-two That's also what the ui-router project itself does to test state transitions.

See: https://github.com/angular-ui/ui-router/blob/04d02d087b31091868c7fd64a33e3dfc1422d485/test/stateSpec.js#L29-L42

2.) catch and parse the exception and interpret it as fulfilled test if tries to get to state-two

The second approach seems very hackish, so I would vote for the first.

However, chances are that I totally got you wrong and should probably get some rest.

Solution code:

beforeEach(module(function ($stateProvider) { 
  $stateProvider.state('state-two', { url: '/' }); 
}));

Upvotes: 16

zdwolfe
zdwolfe

Reputation: 85

I recently asked this question as a github issue and it was answered very helpfully. https://github.com/angular-ui/ui-router/issues/537

You should do a $rootScope.$apply() and then be able to test. Note that by default if you use templateUrl you will get an "unexpected GET request" for the view, but you can resolve this by including your templates into your test.

'use strict';

describe('Controller: CourseCtrl', function () {

  // load the controller's module
  beforeEach(module('myApp'));

  // load controller widgets/views/partials
  var views = [
    'views/course.html',
    'views/main.html'
  ];

  views.forEach(function(view) {
    beforeEach(module(view));
  });

  var CourseCtrl,
    scope;

  // Initialize the controller and a mock scope
  beforeEach(inject(function ($controller, $rootScope) {
    scope = $rootScope.$new();
    CourseCtrl = $controller('CourseCtrl', {
      $scope: scope
    });
  }));

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

Upvotes: 4

Related Questions