ngMock 将 $scope local 注入控制器

ngMock injecting $scope local into controller

这是已成功回答的 的延续。

我正在学习如何使用 Karma、Jasmine 和 ngMock 对 AngularJS 应用程序进行单元测试。这是我有疑问的代码:

describe('myController function', function() {

  describe('myController', function() {
    var scope;

    beforeEach(module('myApp'));

    beforeEach(inject(function($rootScope, $controller) {

      // These are the 2 lines I'm a bit confused about:
      scope = $rootScope.$new();
      $controller('MyController', {$scope: scope});

    }));

    it("...");
  });
});

问题1:为什么我们要创建一个新的scope并将其包含在locals注入区的这一行:$controller('MyController', {$scope: scope});?它似乎工作得很好(例如,scope 现在表示来自该控制器的 $scope 对象并具有它应该具有的所有属性和功能),但代码似乎暗示我们正在重置控制器的 $scope 与我们新创建的(和空的)scope(来自 scope = $rootScope.$new(); 行)。所以我想我只是没有完全理解那些当地人的 purpose/inner-workings。

问题 2: 我也从搜索中看到,新的 scope 是通过两种常见方式创建的,要么是上面显示的 $rootScope.$new() ,或者简单地声明 scope = {}。即使是 Angular 文档也是两种方式,如 here (using $rootScope.$new()) and here 所示(使用 $scope = {}。为什么要这样做而不是另一种方式?有区别吗?

为什么我们要创建一个新的作用域

有几个原因浮现在脑海中,但简短的回答是,如果您不想,就没有。您可以轻松地传入 $rootScope 并且您的测试仍然有效。它通常也是这样做的,因为它更符合 angular 中实际发生的情况 - 注入控制器的 $scope 可能是 $rootScope.[=30= 的后代]

什么是$controller('MyController', {$scope: scope});在做什么?

ngMock 模块提供 $controller 函数作为创建控制器的一种构造函数 - 好吧,从技术上讲,它是 [=19= 中 $controller 函数的装饰器] 模块,但这并不重要。第一个参数通常是一个字符串,但也可以是一个函数。

If called with a function then it's considered to be the controller constructor function. Otherwise it's considered to be a string which is used to retrieve the controller constructor...

第二个参数是 "locals" 在创建期间注入到控制器中。

所以,在你的例子中,你说你想创建 "MyController" 控制器,你想注入你的 scope 变量作为控制器中名为 $scope 的参数功能。

整个点是将您自己的 scope 版本注入控制器,但是您在测试中创建的版本,以便您可以断言由于控制器而发生在范围上的不同事情。

这是依赖注入的好处之一。

例子

如果以下内容有意义:

var scope = {};
scope.add = function(){};
expect(typeof scope.add).toBe('function');

那么让我们更进一步,将函数的添加移动到另一个函数中:

var addFunctionToScope = function(scope) {
  scope.add = function(){};
};
var scope = {};
addFunctionToScope(scope);
expect(typeof scope.add).toBe('function');

现在假装将函数添加到作用域的新函数实际上只是称为 $controller,而不是 "addFunctionToScope"。

var scope = {};
$controller('SomeController', {$scope: scope});
expect(typeof scope.add).toBe('function');

为什么要使用 $rootScope.$new() 而不是 {}

同样,您可以使用其中任何一个。但是,如果您计划使用某些作用域特定函数,例如 $on$new$watch 等——您将需要注入一个实际的作用域对象,该对象使用现有作用域上的 $new 函数。

文档