13

I have a controller with an event listener in my Angular app, defined as follows.

  angular.module('test').controller('TestCtrl', function($rootScope, $scope, testService) {

    [...]

    $scope.$on("myEvent", function(args, value) {
      testService.doStuff(value);
    });
  });

This works perfectly in the app itself. However, when I try to unit test the functionality of the controller (using Jasmine and Karma), each test method throws the following error:

 TypeError: $scope.$on is not a function in [...]/testController.js

I create the controller in my test methods as follows.

  it('does stuff', inject(function($rootScope, $controller, testService) {
    var scope = $rootScope.$new;
    $controller('TestCtrl', {
      $scope : scope
    });

    [...]
  }));

I can get around the problem by changing my controller to use rootScope instead:

  $rootScope.$on(...)

but then of course the app doesn't work anymore as expected. I can also get rid of the error by introducing the $on method in my test code:

  var scope = $rootScope.$new;
  scope.$on = function() {};

but mocking the listener like this kind of defeats the purpose of testing my real code.

Why doesn't the test code find the $on method of the scope? (But finds it on rootScope, still...)

There must be something fundamental that I'm missing here but I haven't been able to figure it out even after lots of googling and reading the Angular source code.

MJV
  • 1,782
  • 2
  • 21
  • 33

1 Answers1

16

$rootScope.$new is a function. You need to invoke it ($rootScope.$new()):

it('does stuff', inject(function($rootScope, $controller, testService) {
  var scope = $rootScope.$new();
  $controller('TestCtrl', {
    $scope : scope
  });

  [...]
}));

Example plunk: http://plnkr.co/edit/t1x62msmOQDkNItGzcp9?p=preview

Matt York
  • 15,981
  • 7
  • 47
  • 51
  • Thank you! That was a really embarrassing mistake... (Argh!) I don't know why I had written it like that, but the mistake had infected all of my test classes. – MJV May 21 '13 at 14:23