19

我已经阅读了一些教程和基本示例,但我很难为我的控制器编写单元测试。我已经看到代码片段实例化控制器并让角度注入$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 的方法工作得很好。

4

4 回答 4

40

我的理解是,在 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);
});

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

所以,你做对了。

于 2013-01-11T09:07:58.387 回答
3

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

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 以及其他设置值。

于 2014-10-04T23:01:08.513 回答
1

只有两种可能:

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

返回searchMatch函数:

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

如果它真的必须保持私密,那么您将不得不测试使用它的功能。

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

var $filter = angular.injector(['ng']).get('$filter'); 
于 2013-01-07T20:01:08.773 回答
0

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

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

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

let accountTimeZoneFilter = $filter('AccountTimeZone');
于 2021-05-24T07:48:38.313 回答