Ben Harold
Ben Harold

Reputation: 6432

How can I trigger `$on` events in AngularJS Karma unit tests?

I am attempting to trigger the $scope.$on() method in a controller that is fired when I $rootScope.broadcast() an event. I found this question useful: How can I test events in angular?, but I'm still having trouble detecting an event being broadcast from the $rootScope up through a controller's $scope.

So far I've managed to test that the $broadcast method is called on a corresponding $rootScope, but not that the $on method was called on a corresponding $scope when $broadcast is called on the $rootScope.

I attempted to $rootScope.$broadcast directly in my test, but my spy is not picking up on the event.

This is my controller:

angular.module('app.admin.controllers.notes', [])
    .controller('NotesCtrl', function($scope) {

      $scope.$on('resource-loaded', function(event, resource) { // I want to test this
        $scope.parentType = resource.type;
        $scope.parentId = resource.id;
      });

    });

This is my test:

describe('The notes controller', function() {

  beforeEach(module('app.admin.controllers.notes'));

  var scope, rootScope, NotesCtrl;

  beforeEach(inject(function($controller, $injector, $rootScope) {
    rootScope = $rootScope;
    scope = $rootScope.$new(); // I've tried this with and without $new()
    NotesCtrl = $controller('NotesCtrl', {$scope: scope}); // I've tried explicitly defining $rootScope here
  }));

  it('should respond to the `resource-loaded` event', function() {
    spyOn(scope, '$on');
    rootScope.$broadcast('resource-loaded'); // This is what I expect to trigger the `$on` method
    expect(scope.$on).toHaveBeenCalled();
  });

});

And here's the plunkr. I've included a passing test of the $broadcast method for reference, mainly because I setup the tests in the same manner.

I've read quite a few questions relating to testing events in AngularJS, and it always seems to be a scoping issue. I've heard that in Karma unit testing, $rootScope and $scope are the same thing, but I'm not really sure what the implication is. I've tried defining the $rootScope and the $scope as the same object, as well as explicitly injecting the $rootScope into the NotesCtrl during testing, but nothing makes my test go green.

How can I get the $on method in my NotesCtrl to fire for this test?

Upvotes: 6

Views: 9099

Answers (3)

Emmanuel Dieval
Emmanuel Dieval

Reputation: 31

@kirill.buga

Using $broadcast is right, not $emit because :

https://docs.angularjs.org/api/ng/type/$rootScope.Scope Dispatches an event name upwards through the scope hierarchy notifying the registered $rootScope.Scope listeners.

Problem of @ben-harold is trying to spy $on instead of the result of code in the $on.

Upvotes: 1

kirill.buga
kirill.buga

Reputation: 1159

Try to use

$rootScope.$emit('resource-loaded');

Works fine in my tests.

Upvotes: 1

JB Nizet
JB Nizet

Reputation: 692181

What makes it not working is the fact that you're spying the $on function. It works fine when not sying it: http://plnkr.co/edit/hNEj7MmDDKJcJ7b298OB?p=info. And the reason is actually simple. When an event is brodcasted, what is called is not the $on() function. What is called is the callback function passed as argument to $on() previously: the listener.

Note that, by spying the $on function, you're not testing your code here. All you're trying to test is that when broadcasting en event, child scopes receive it. So you're testing AngularJS itself.

Upvotes: 12

Related Questions