6

How can I unit test different views for the below scenario

.state('app.sr.product.upload', {
            name: 'upload',
            url: '/upload',
            data: {
                tags: [],
                userCommunities: []
            },
            views: {
                "[email protected]": {
                    templateUrl: 'views/upload/upload.html',
                    controller: 'UploadCtrl',
                    controllerAs: 'ul'
                },
                "[email protected]": {
                    templateUrl: 'views/tags/tags.html',
                    controller: 'TagsCtrl',
                    controllerAs: 'vm'
                },
                "[email protected]": {
                    templateUrl: 'views/user-community/user-community.html',
                    controller: 'UserCommunityCtrl',
                    controllerAs: 'ul'
                },
            }
        })
  • If my view is [email protected] then how can I test that my controller is TagsCtrl, my controllerAs value is vm etc??

  • How can I unit test if my state is app.sr.product.upload then data.tags=[], data.userCommunities=[] etc.

I searched for lot of docs and tutorials but didnt get it .

Any help is appreciable. Thanks

3 Answers 3

5
+100

Try this on for size. I'm assuming you would be using jasmine for your tests, but the concept is the same for any testing framework.

When you run your test, first subscribe to the '$stateChangeSuccess' event and then navigate to that state. Once the event fires, check the toState values to see if they are what you expect them to be.

You can run the snippet to see the tests in action.

//write a unit test
describe('state changes', function() {
  beforeEach(module('app'));
  var $rootScope, $state;
  beforeEach(inject(function(_$rootScope_, _$state_) {
    // The injector unwraps the underscores (_) from around the parameter names when matching
    $rootScope = _$rootScope_;
    $state = _$state_;
  }));


  it('loads page 1', function(done) {
    //wait for the state to change, then make sure we changed to the correct state
    $rootScope.$on('$stateChangeSuccess', function(event, toState, toParams, fromState, fromParams) {
      expect(toState.controller).toEqual('Controller1');
      done();
    });
    //navigate to the state
    $state.go('state1');
    //start a digest cycle so ui-router will navigate
    $rootScope.$apply();
  });

  it('loads page 2', function(done) {
    //wait for the state to change, then make sure we changed to the correct state
    $rootScope.$on('$stateChangeSuccess', function(event, toState, toParams, fromState, fromParams) {
      expect(toState.controller).toEqual('Controller2');
      done();
    });
    //navigate to the state
    $state.go('state2');
    //start a digest cycle so ui-router will navigate
    $rootScope.$apply();
  });

  it('loads page 3', function(done) {
    //wait for the state to change, then make sure we changed to the correct state
    $rootScope.$on('$stateChangeSuccess', function(event, toState, toParams, fromState, fromParams) {
      expect(toState.controller).toEqual('Controller3');
      done();
    });
    //navigate to the state
    $state.go('state3');
    //start a digest cycle so ui-router will navigate
    $rootScope.$apply();
  });
});

//set up some dummy controllers and some dummy states
angular.module('app', ['ui.router']).controller('Controller1', function() {
  this.message = 'Page 1';
}).controller('Controller2', function() {
  this.message = 'Page 2';
}).controller('Controller3', function() {
  this.message = 'Page 3';
}).config(function($stateProvider, $urlRouterProvider) {
  $urlRouterProvider.otherwise("/state1");

  $stateProvider.state('state1', {
    url: "/state1",
    controller: 'Controller1',
    controllerAs: 'vm',
    template: '<h1>{{vm.message}}</h1>'
  }).state('state2', {
    url: "/state2",
    controller: 'Controller2',
    controllerAs: 'vm',
    template: '<h2>{{vm.message}}</h2>'
  }).state('state3', {
    url: "/state3",
    controller: 'Controller3',
    controllerAs: 'vm',
    template: '<h3>{{vm.message}}</h3>'
  });
});
h1 {
  color: red;
}
h2 {
  color: blue;
}
h3 {
  color: green;
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.0/angular.min.js"></script>

<script src="
https://cdnjs.cloudflare.com/ajax/libs/angular-ui-router/0.2.18/angular-ui-router.js"></script>
<link rel="stylesheet" type="text/css" href="http://jasmine.github.io/2.0/lib/jasmine.css">
<script src="http://jasmine.github.io/2.0/lib/jasmine.js"></script>
<script src="http://jasmine.github.io/2.0/lib/jasmine-html.js"></script>
<script src="http://jasmine.github.io/2.0/lib/boot.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.0/angular-mocks.js"></script>
<div ng-app="app">
  <a ui-sref="state1">State 1</a>
  <a ui-sref="state2">State 2</a>
  <a ui-sref="state3">State 3</a>
  <div ui-view></div>
</div>

Sign up to request clarification or add additional context in comments.

5 Comments

The problem with these tests is that they could produce false positives if the $stateChangeSuccess event handler functions are never called.
@ShaunScovil, I don't think that they would produce false positives. Because they are using the done callback, jasmine will throw an error after a timeout if the done function isn't called. So yeah, the tests might take a long time to run, but jasmine will let you know that your test never said it was done.
If for some reason the event handler function is not called, there is no expect() outside of the callback that would cause the test to fail.
Why I am getting Error: Unexpected request: GET views/index/index.html No more request expected.. Error
Your router is probably trying to load the index page, which is most likely your default page. You can add an interceptor for that file and just ignore it. $httpBackend.when('GET', 'views/index/index.html') .respond("<h1>I am the index</h1>");
1

If I'm not wrong, I think we missed the point of the initial question, which was

if my view is [email protected] then how can I test that my controller is TagsCtrl, my controllerAs value is vm etc??

and

How can I unit test if my state is app.sr.product.upload then data.tags=[], data.userCommunities=[] etc.

Here's how you can test these :

var $rootScope, $state, $injector, state;

beforeEach(inject(function(_$rootScope_, _$state_){
    $rootScope = _$rootScope_;
    $state = _$state_;
    state = $state.get('app.sr.product.upload');
}));

it('should have the correct data parameters', function () {

    expect(state.data.tags).toEqual('');
    expect(state.data.userCommunities).toEqual('');

});

it('should render the dashboard views with the right Controllers', function () {

    var product = state.views['[email protected]'];
    var tags= state.views['[email protected]'];
    var userCommunity = state.views['[email protected]'];

    expect(product.templateUrl).toEqual('views/upload/upload.html');
    expect(product.controller).toEqual('UploadCtrl');
    expect(product.controllerAs).toEqual('ul');

    // etc...

});

Also, in newer angular versions, you can just declare your controller like so:

controller: 'UploadCtrl as vm'

Comments

0

It's not something I would normally unit test. UI-Router itself is well covered by tests.

You'd do better with e2e (end-to-end) tests with Protractor. You simulate a click on a link, you expect url to be this, use expect number of elements in a list to be that etc.

But if you really need it:

  • locate root element of each view (f.e. by adding a specific class and using selectors)
  • you should be able to access scope and controller via angular.element wrapper methods

1 Comment

Can you please be more clear , If you can provide some demo code or any example, it will be a great help

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.