AngularJS: 如何在我正在单元测试的控制器中存根函数?

AngularJS: How do I stub a function inside a controller which I am unit-testing?

我有一个控制器,当前正在加载相关视图时调用 RESTful API。因此,我不确定这个问题是否与我测试的方式或代码本身的实现有关 - 我很乐意接受任何从整体上解决问题的建议,即帮助我单元 -执行我想要的测试代码:)

以下是一些示例来说明我的观点:

控制器:

angular.module('app.myModule',[])
.controller('MyController', function($scope) {

  $scope.notTestingThis = function() {
    var thisFails = $scope.do.not.want.to.mock.these.objects.substr(0,10);
  };

  $scope.testingThis = function(myString) {
    $scope.newString = myString;
  };

  $scope.notTestingThis();

});

测试(按原样):

describe('MyControllerTest', function() {

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

  var $controller;

  beforeEach(inject(function(_$controller_) {
    $controller = _$controller_;
  }));

  describe('$scope.testingThis()', function() {

    it('sets newString', function() {
      $scope = {
        'notTestingThis': {}
      };
      var myController = $controller('MyController', {$scope: $scope});
    });

  });

});

加载此视图时,我需要以某种方式调用 notTestingThis 函数,但是在我的单元测试中我想隔离 testingThis 功能。问题是当我在我的测试中初始化控制器时,它当然会调用 notTestingThis 并尝试对不存在的对象执行操作(我不关心这个测试)。

显然,按照本示例尝试对有问题的函数进行存根是没有用的,因为 $scope 在初始化时将被重写。有没有办法在您尝试测试的控制器中存根或模拟单个功能,或者我是否错过了某个地方?一位同事提出的一些建议是:

Enhance the controller to be aware of the unit-test itself, allowing you to adjust program flow based on the injected mock, i.e. something like the following in the controller:

if (!$scope.methodA) {
   $scope.methodA = function() {...}
}

...或...

Change the way the notTestingThis function is called, by listening to an initialization event from the $rootScope instead of calling it directly, which would allow me to mock the $rootScope so that it doesn't trigger this event, thus preventing notTestingThis from being called

我不禁觉得我在想这个问题。有什么见解吗?

只需加倍查询此查询,以防其他人查询。我查看了这两个建议,并在实施时与我最初的评论有所不同(因为我误解了这些建议),即:

选项 1

不是包装每个函数,而是将调用代码包装在:

if(!testingBoolean) { $scope.notTestingThis(); };

使用 testingBoolean false 除非作为(可选)参数传递给控制器​​,从而允许测试代码阻止调用

选项 2:

$broadcast 来自 $rootScope 使用服务功能。将调用代码包装在该事件的侦听器中,然后从控制器调用该函数。在执行测试时模拟 $rootScope 将防止触发 notTestingThis 函数。

真正的解决方案

然而,这些方法似乎分散了注意力;尽管没有在任何地方明确说明,但控制器似乎不应该自己调用函数。正确的做法似乎是重新构建应用程序设计,以便指令通过绑定调用函数,从而简化单元测试。