How to unit test Angular controller with $scope.$o

2020-08-13 01:52发布

问题:

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.

回答1:

$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