嵌套控制器的单元测试

时间:2012-09-18 09:55:04

标签: unit-testing angularjs

我正在尝试为嵌套的控制器编写单元测试,但无法弄清楚如何在我的测试中模拟相同的行为。

我有2个控制器:

function FirstController ($scope) {
    $scope.childs = [{
         title : 'Hello, earth!'
    }];
};

function SecondController ($scope) {
    $scope.child.title = $scope.child.title + $scope.$index;
};

在我的HTML中:

<div data-ng-controller="FirstController">
    <div data-ng-repeat="child in childs" data-ng-controller="SecondController">
        {{ child.title }}
    </div>
</div>

这可以按预期工作(http://jsfiddle.net/tcayp/1/

单元测试:

// FirstController
it('Should have childs', function () {
    scope = {};
    ctrl = new FirstController(scope);
    expect(scope.childs.length).toBeGreaterThan(0);
});
// SecondController
it('Should have inherited a child', function () {
    scope = {};
    ctrl = new SecondController(scope);
    expect(scope.child.title).toEqual('Hello, earth!0');
});

在SecondController-test中,我无法弄清楚如何从ng-repeat模拟继承链。

3 个答案:

答案 0 :(得分:16)

理想情况下,通过单元测试,我们希望单独测试类(单元)。在一次测试中测试2个控制器可能太多了:测试会变得更复杂,更脆弱。

仔细研究一下提供的示例,人们可能会注意到它实际上不是测试2个控制器,而是确保数据在父作用域中可用。因此,只关注一个控制器(SecondController)和继承的数据,就可以编写如下测试:

describe('Testing the SecondController controller', function() {

    var $parentScope, $scope, ctrl;
    it('should prepare title', inject(function($rootScope, $controller) {

        //setup hierarchy of scopes with data             
        $rootScope.childs = [{
            title : 'Hello, earth!'
        }];
        $scope = $rootScope.$new();
        $scope.$index = 1;

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

        expect($scope.childs[0].title).toEqual('Hello, earth!1');        
    }));
});

以下是完整的jsFiddle:http://jsfiddle.net/pkozlowski_opensource/h8xry/13/

我真的建议不要一起测试2个控制器,但只是为了回答问题,它也是可能的:

describe('Testing the SecondController controller', function() {

    it('should prepare title', inject(function($rootScope, $controller) {

        $controller('FirstController', {
            $scope: $rootScope
        });

        var $scope = $rootScope.$new();
        $scope.$index = 1;

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

        expect($scope.childs[0].title).toEqual('Hello, earth!1');        
    }));
});

和jsFiddle:http://jsfiddle.net/pkozlowski_opensource/4Qy6b/1/

答案 1 :(得分:2)

AngularJS documentation建议通过实例化每个控制器并在应用程序之间建立相同的范围层次结构来测试嵌套控制器。这是有道理的,因为(在某一点上)你想在真实的环境中测试你的控制器。

答案 2 :(得分:1)

在测试中使用新范围实例化父控制器:

mainScope = $rootScope.$new();
$controller('ParentController', {$scope: mainScope});

并在您的子控制器中,使用先前实例化的范围实例化新范围:

childScope = mainScope.$new();
$controller('ChildController', {$scope: childScope});

AngularJS documentation的示例:

describe('state', function() {

  var mainScope, childScope, grandChildScope;

  beforeEach(module('myApp'));

  beforeEach(inject(function($rootScope, $controller) {
      mainScope = $rootScope.$new();
      $controller('MainController', {$scope: mainScope});
      childScope = mainScope.$new();
      $controller('ChildController', {$scope: childScope});
      grandChildScope = childScope.$new();
      $controller('GrandChildController', {$scope: grandChildScope});
  }));

  it('should have over and selected', function() {
      expect(mainScope.timeOfDay).toBe('morning');
      expect(mainScope.name).toBe('Nikki');
      expect(childScope.timeOfDay).toBe('morning');
      expect(childScope.name).toBe('Mattie');
      expect(grandChildScope.timeOfDay).toBe('evening');
      expect(grandChildScope.name).toBe('Gingerbread Baby');
    });
});