【问题标题】:Testing $scope in AngularJs controller with dependency on $filter在依赖于 $filter 的 AngularJs 控制器中测试 $scope
【发布时间】:2013-01-07 19:33:45
【问题描述】:

我已经阅读了一些教程和基本示例,但我很难为我的控制器编写单元测试。我已经看到代码 sn-ps 实例化控制器并让 angular 注入 $rootScope 对象,该对象又用于为控制器创建一个新的 scope 对象。但我不明白为什么ctrl.$scope未定义

 describe('EmployeeCtrl', function () {
    var scope, ctrl, $httpBackend;

    beforeEach(inject(function (_$httpBackend_, $rootScope, $controller, $filter) {
        $httpBackend = _$httpBackend_;       

        scope = $rootScope.$new();
        ctrl = $controller('EmployeeCtrl', { $scope: scope});
        expect(ctrl).not.toBeUndefined();
        expect(scope).not.toBeUndefined();   //<-- PASS!      
        expect(ctrl.$scope).not.toBeUndefined();  //<-- FAIL!       
    }));
});

我最终使用了scope 变量而不是ctrl.$scope,但是在我的第一次测试中,我无法弄清楚如何在我的控制器中对函数变量进行单元测试:

控制器:

 function EmployeeCtrl($scope, $http, $filter, Employee) {
  var searchMatch = function (haystack, needle) {
   return false;
  }
 }

损坏的单元测试:

it('should search ', function () {                
    expect(ctrl.searchMatch('numbers','one')).toBe(false);
});

这就是我得到的

TypeError: Object # has no method 'searchMatch'

您如何测试该功能?作为一种解决方法,我将我的方法移动到 $scope 以便我可以测试 scope.searchMatch 但我想知道这是否是唯一的方法。

最后,在我的测试中出现$filter 也未定义,你如何注入它?我试过了,但没有用:

ctrl = $controller('EmployeeCtrl', { $scope: scope, $filter: $filter });

谢谢

更新: 上面提到的注入 $filter 的方法效果很好。

【问题讨论】:

  • 我的茉莉花测试在注入方法上中断。没有找到。我错过了什么。角度不为空。我有一个通过验证它已加载的测试。

标签: javascript angularjs jasmine


【解决方案1】:

我的理解是,在 Angularjs 中,当应用程序启动并且控制器修改范围时,您将范围对象传递给控制器​​。控制器不再被调用。

在 Angularjs 中,控制器真正做的是初始化作用域:控制器只运行一次。 如果您理解这一点,您就会意识到向控制器询问范围是这样的:

currentScope = myController.scope;

没有意义。

(顺便说一句,我不喜欢 Angular 中的一件事是他们选择的名称。如果“控制器”正在做的是初始化范围,那么它并不是真正的控制器。有很多这样的在 Angular API 中)。

我认为测试“控制器”的“正确”方法是在 Jasmine beforeEach 子句中从头开始创建一个新的空白范围,并使用“控制器”进行初始化 一个像这样的新空白范围::

var ctrl, myScope;

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

然后测试新创建的范围是否具有预期的属性:

it('In the scope, the initial value for a=2', function() {
            expect(myScope.a).toBe(2);
});

换句话说,你不测试控制器;您测试控制器创建的范围。

所以,你做对了。

【讨论】:

  • 这更有意义。非常感谢!
【解决方案2】:

这是另一种可能更容易遵循的模式:

var $scope;
beforeEach(module(function ($provide) {
  $scope = {
    // set anything you want here or nothing
  };
  $provide.value('$scope', $scope);
}));

var ctrl;
beforeEach(inject(function ($controller) {
  ctrl = $controller('MyController');
}));

it('should test something...', function () {
  // $scope will have any properties set by
  // the controller so you can now do asserts
  // on them.
});

您可以使用相同的模式为 $location 和 $window 以及其他设置值。

【讨论】:

    【解决方案3】:

    只有两种可能:

    searchMatch 添加到范围(如您所述)或

    返回searchMatch函数:

    function EmployeeCtrl($scope, $http, $filter, Employee) {
      return {
        searchMatch: function (haystack, needle) {
          return false;
        }
      };
     }
    

    如果它真的必须保持私有,那么你必须测试使用它的功能。

    要获取$filter,您可以尝试以下操作:

    var $filter = angular.injector(['ng']).get('$filter'); 
    

    【讨论】:

    • 谢谢,我该如何注入 $filter?
    • 我已经更新了我的答案。尝试使用 angular.injector(),它返回一个 $injector 对象,其中包含 Angular 服务,如 $filter、$controller、...
    【解决方案4】:

    要测试过滤器,请执行以下操作以获取目标过滤器:

    var ctrl, myScope, $filter;
    beforeEach(inject(function(_$filter_){
        $filter = _$filter_;
    }))
    

    如果您的过滤器名称为“AccountTimeZone”,则执行以下操作:

    let accountTimeZoneFilter = $filter('AccountTimeZone');
    

    【讨论】:

      猜你喜欢
      • 2015-04-02
      • 2014-04-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-10-27
      • 2016-05-03
      • 2020-01-05
      • 2019-01-02
      相关资源
      最近更新 更多