1

I have search around a few places to get ideas on this.

How do I unit test $scope.broadcast, $scope.$on using Jasmine

$scope.$on not working in Jasmine SpecRunner

It has helped I think but seems I am still missing something. I am trying to test a controller that has a $rootScope.$on in the code. So in the unit test I am trying to trigger the $broadcast so the $on gets it and the code runs. But my current code is not working.

Here is my controller:

constructor($state: ng.ui.IStateService, store: angular.a0.storage.IStoreService, jwtHelper: angular.jwt.IJwtHelper, $rootScope: any) {
    this.currentDate = new Date();
    this.state = $state;
    this.store = store;
    this.jwtHelper = jwtHelper;
    this.userFullname = '';

    $rootScope.$on('$stateChangeStart',
        (event: any, toState: any, toParams: any, fromState: any, fromParams: any, error: any) => {
            var jwtToken = this.store.get('token');
            if (jwtToken != null) {
                var decodedToken: any = this.jwtHelper.decodeToken(jwtToken);
                } 
        });

}

Here is my test:

 beforeEach(angular.mock.inject(($compile: ng.ICompileService, $rootScope: any, $controller: any, $state: ng.ui.IStateService, jwtHelper: angular.jwt.IJwtHelper) => {

    controllerScope = $rootScope.$new();
    navbarController = $controller('NavbarController', { $scope: controllerScope });
    currentDate = new Date();
    rootScope = $rootScope;
    state = $state;
    jwt = jwtHelper;
}

it('should get broadcast for user token', () => {
    spyOn(controllerScope, '$on');
    spyOn(jwt, 'decodeToken');
    //state.go('home'); Was trying a different way to trigger the event
    rootScope.$broadcast('$stateChangeStart', [{ toState: 'home' }]);
    expect(controllerScope.$on).toHaveBeenCalled();
    expect(jwt.decodeToken).toHaveBeenCalled();
});

Both spies say they are never called. What do I have missed aligned?

Community
  • 1
  • 1
ToddB
  • 1,464
  • 1
  • 12
  • 27

2 Answers2

2

Instead of testing whether or not the event listener is fired, you're verifying whether the code that registers the listener is fired.

It was fired, but controllerScope.$on got invoked in your beforeEach function, not in your it. spyOn can't reach into the past to detect prior function calls; even if it, that's probably not the test you're trying to do.

Basically you've done something like this:

window.addEventListener('mouseMove', someFunctionExpr);
spyOn(window, 'addEventListener');
triggerMouseMoveEvent();
expect(window.addEventListener).toHaveBeenCalled();

I can't speak to decodeToken / jwt -- your code doesn't reference it anywhere so I don't know where to expect it to be called.

Ryan Cavanaugh
  • 209,514
  • 56
  • 272
  • 235
  • Thanks. I updated the my code to show where the jwt call is happening at. It happens inside the function that the $on registers. That is why I added that spy as well but it also says it is not fired. – ToddB Jan 15 '16 at 23:53
  • Just to provide an update on this as to the root of my issues. 1) Per what Ryan said the "$on" is fired to early to get spied on. It was firing but just not able to catch it. 2) I had another jwtHelper method I needed to spyon and fake that was before the spyon I was watching and that previous method was blocking the method I was watching, hence it did not get called. Once I got that aligned all was good. Tweaked it to test my function and not the event routing. – ToddB Jan 19 '16 at 15:46
0

If you want to test that the event gets broadcast, do spyOn(scope, '$broadcast').and.callThrough();

If you want to test the logic of the controller, wrap it in a function and test that separately from $broadcast / $on (you're not testing Angular's event passing mechanism, after all).

btk
  • 3,158
  • 2
  • 29
  • 30
  • i want to test that 1) The controller is listening for the event 2) when the event happens the function that is defined executes correctly. Do you have an answer or approach? – ToddB Jan 16 '16 at 03:13