如何使用ngMock注入$ controller

时间:2015-12-31 17:15:57

标签: angularjs unit-testing angular-mock ngmock

我正在尝试使用Karma,Jasmine和ngMock学习Angular的单元测试。 Angular文档中至少有两个地方显示如何为控制器编写单元测试,我只是对他们如何做事有几个问题。

来自Controller docs, section on testing controllers

describe('myController function', function() {

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

    beforeEach(module('myApp'));

    beforeEach(inject(function($rootScope, $controller) {
      $scope = $rootScope.$new();
      $controller('MyController', {$scope: $scope});
    }));

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

问题1:这一个主要是对我有意义,但这是我不太了解的事情。我知道$controller正在抓取一个“MyController”的实例,但看起来返回的内容并没有被保存或在任何地方使用,所以我唯一能想到的是我们抓住那个控制器以便得到正确的$scope对象?即使这看起来似乎没有被保存在任何地方的变量中,所以我仍然对它在幕后如何工作有点困惑。我对module()的工作方式也有同样的困惑,因为我们似乎在声明我们正在使用哪个模块,而不是在其他任何地方保存或使用它。幕后是否有缓存模块/控制器/范围的东西,所以我们不必手动保存它或什么东西?

以下是Unit Testing docs的另一个例子:

describe('PasswordController', function() {
  beforeEach(module('app'));

  var $controller;

  beforeEach(inject(function(_$controller_){
    // The injector unwraps the underscores (_) from around the parameter names when matching
    $controller = _$controller_;
  }));

  describe('$scope.grade', function() {
    it('sets the strength to "strong" if the password length is >8 chars', function() {
      var $scope = {};
      var controller = $controller('PasswordController', { $scope: $scope });
      $scope.password = 'longerthaneightchars';
      $scope.grade();
      expect($scope.strength).toEqual('strong');
    });
  });
});

问题2:beforeEach函数中,它正在执行下划线包装。这是为了让您可以保持相同的服务名称而不必更改它吗?

问题3: it()块然后使用$controller执行此操作,这次保存返回的内容 {{1} } 但似乎仍然没有超越这一点使用它。那么为什么要将它保存到变量?我唯一可以想到的就是你保存它以防你需要在这个var controller块内再次使用它,但它们在这个例子中没有?

我一直在寻找一个很好的解释,我似乎找不到一个。对不起,如果有一个愚蠢的解释我错过了,但我正处于紧张状态,不能再花时间旋转我的车轮了。

2 个答案:

答案 0 :(得分:1)

1)调用模块('myApp')将加载您的模块,基本上运行所有模式函数(提供者,指令,控制器)的声明,使您可以在以后使用它们。这就是在调用$ controller('myController')时angular如何找到我的Controller。

对于从$ controller返回的控制器的引用,它取决于您的实现。如果您使用$ scope,一旦实例化控制器,您可以通过$ scope引用这些函数。

控制器中的

......

$scope.cancel = function () {
   doStuff();
};

然后你的测试看起来像这样......

describe(’test Controller', function () {
  var scope;

  beforeEach(module(‘myApp'));

  beforeEach(inject(function ($rootScope, $controller) {
    scope = $rootScope.$new();
    $controller(‘myController', {
      $scope: scope
    });
  }));

  describe(’testing stuff', function () {
    it(’test cancel function', function () {
      scope.cancel();
      expect(...);
    });
  });
});

如果您正在使用controllerAs语法并在控制器中为“this”分配函数,则可以在测试中使用对控制器的引用...

控制器中的

......

this.cancel = function () {
   doStuff();
};

然后你的测试看起来像这样......

describe(’test Controller', function () {
  var scope,
      controller;

  beforeEach(module(‘myApp'));

  beforeEach(inject(function ($rootScope, $controller) {
    scope = $rootScope.$new();
    controller = $controller(‘myController', {
      $scope: scope
    });
  }));

  describe(’testing stuff', function () {
    it(’test cancel function', function () {
      controller.cancel();
      expect(...);
    });
  });
});

2)是的, $ controller 可以避免名称冲突;

3)我认为这是一个不好的例子,如果他们不使用它们,他们不应该保存它。我更喜欢在beforeEach()中进行所有测试设置。

答案 1 :(得分:0)

@bobbyz:$ controller服务的第二个参数是注入的哈希值。为$ scope添加引用时,根据Controller的定义创建控制器实例时,将修改相同的引用对象。这样,$ scope就能获得所有方法或变量。