0

The problem:
- using AngularJs
- given an implemented a service
- trying to test it with Jasmine

Here is how my service looks like:

angular.module('app.common').service('StateInterceptor',['$state','$rootScope',function($state,$rootScope){
    $rootScope.$on('$stateChangeStart', function(event, toState, toParams, fromState, fromParams){/*stuffs*/}

}

and here is how I'm trying to test whether the $rootScope.$on has been called:

describe("Testing StateInterceptor service", function(){
var $state, $rootScope, $scope, StateInterceptor;

beforeEach(module('app.common'));

beforeEach(inject(function($injector) {
    $rootScope = $injector.get("$rootScope");
    spyOn($rootScope, "$on");

    $state = $injector.get("$state");

    StateInterceptor = $injector.get("StateInterceptor");
}));

it("WHEN created THEN it should call '$rootScope.$on' event listener", function(){
    //expect($rootScope.$on).toHaveBeenCalled(); //not working neither
    expect($rootScope.$on).toHaveBeenCalledWith("$stateChangeStart", jasmine.any(Function));
});

What I've got is an error message, saying:

Expected spy $on to have been called with... (blabla) but it was never called.

I've tried almost everything like: - creating rootscope in this was: $rootScope = $rootScope.$new(); - spying $rootScope.$on like in this way: spyOn($rootScope.prototype, "$on")

but neither was working. Do anybody know how to test it in the right way?

Edit1:

the original code seems to working so I've pasted here the exactly code that I have. So my service looks like exactly that:

angular.module('app.common').service('StateInterceptor',['$state','$rootScope',function($state,$rootScope){
    var self = this;
    self.bootstrapped = false;
    self.changeStart = [];


    self.init = function(){
        if(!self.bootstrapped){
            $rootScope.$on('$stateChangeStart',
                function(event, toState, toParams, fromState, fromParams){
                /*staffs*/
                }
            );
        }
    }
    self.bootstrapped = true;

    self.init();
    return self;
}]);

Edit2:

I've put a few console.log to see how the code executed:

beforeEach(inject(function($injector) {
    $rootScope = $injector.get("$rootScope");
    spyOn($rootScope, "$on");

    $state = $injector.get("$state");

    StateInterceptor = $injector.get("StateInterceptor");
}));

And for the code for the first line within self.init function:

console.log("init runs");

And the result is: 'init runs' 'before' 'after'

Strange! Any idea why is that?

Iamisti
  • 1,680
  • 15
  • 30
  • Tried to replicate it here based on your code and it seems to work: http://plnkr.co/edit/oTJ8ZTHsKqCAzx7H4juU?p=preview – tasseKATT Dec 29 '14 at 11:08
  • you right, check my update above – Iamisti Dec 29 '14 at 11:19
  • Is that the exact code? Cause there are syntax errors. – tasseKATT Dec 29 '14 at 11:23
  • sorry, there are stuffs in he listener of course. I might deleted more than I wanted. Check now. – Iamisti Dec 29 '14 at 11:26
  • but I guess the problem will be that calling beforeEach(module("app.common")) is initializing the service as well and the code runs in this section before I inject the service into my test suite. – Iamisti Dec 29 '14 at 11:27
  • but am I right that every angularJS service is singleton? So there is no need to do that. – Iamisti Dec 29 '14 at 11:27
  • Still seems to work: http://plnkr.co/edit/jq4S436NOyZ7hZSnqmzS?p=preview – tasseKATT Dec 29 '14 at 12:04
  • `beforeEach(module('app.common'))` will not initialize the service. It will not be created until someone asks for it (this instance will be cached and reused). – tasseKATT Dec 29 '14 at 12:08
  • Thanks tasseKATT! So after I realized it should work, I made a bigger investigation within the module. There was another service which is using this service. So calling 'beforeEach(module("app.common"))' called the StateInterceptor before I could test it. – Iamisti Dec 29 '14 at 12:10
  • So I've made the changes and it works now! – Iamisti Dec 29 '14 at 12:11
  • If you create an answer, I can accept it as a solution, because you helped me figuring out the cause. – Iamisti Dec 29 '14 at 12:12
  • You're welcome :) I think it's better if you post an answer yourself describing what the issue was since I never really gave the actual solution :) – tasseKATT Dec 29 '14 at 12:13

2 Answers2

0

Try to call $broadcast for "$stateChangeStart" event, it should automatically comes in $on().

Suneet Bansal
  • 2,664
  • 1
  • 14
  • 18
0

First of all, thanks a lot for tasseKATT for helping me out. He was the guy who lead me to the solution.

The problem was that I had an another function in this module which run immediately as I inject the module in my test suite with 'beforeEach(module("app.common"))'.

Here is how the other function looked like:

angular.module('app.common').run(function($q,StateInterceptor,$modal){
  //stuffs, but pls notice that it injects the StateInterceptor which caused the problem!
});

So I've put this out to another module and make it as a dependency to the other one.

Iamisti
  • 1,680
  • 15
  • 30