需要控制器的测试指令 [英] Testing directives that require controllers

查看:22
本文介绍了需要控制器的测试指令的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

所以我确实看到了另一个问题:How to mock required指令 UT 中的指令控制器 这基本上是我的问题,但似乎这个线程的答案是改变你的设计".我想确保没有办法做到这一点.我有一个指令声明了一个由子指令使用的控制器.我现在正在尝试为 children 指令编写 jasmine 测试,但我无法让它们在测试中编译,因为它们依赖于控制器.这是它的样子:

addressModule.directive('address', ['$http', function($http){返回 {替换:假,限制:'A',范围: {配置:'='},模板:'<div id="addressContainer">'+'<div ng-if="!showAddressSelectionPage" basic-address config="config"/>'+'<div ng-if="showAddressSelectionPage" 地址选择器地址="standardizedAddresses"/>'+'</div>',控制器:函数($scope){this.showAddressInput = function(){$scope.showAddressSelectionPage = false;};this.showAddressSelection = function(){$scope.getStandardizedAddresses();};this.finish = 函数(){$scope.finishAddress();};},链接:函数(范围,元素,属性){...}}}])

子指令:

addressModule.directive('basicAddress360', ['translationService', function(translationService){返回 {替换:真的,限制:'A',范围: {配置:'='},模板:'...',要求:^address360",链接:功能(范围,元素,属性,地址控制器){...}}}])

茉莉花测试:

it(应该做点什么",inject(function($compile, $rootScope){parentHtml = '

';subDirectiveHtml = '

';parentElement = $compile(parentHtml)(rootScope);parentScope = parentElement.scope();指令元素 = $compile(subDirectiveHtml)(parentScope);指令范围 = 指令元素.范围();$rootScope.$digest();}));

我有没有办法用 jasmine 测试 sub 指令,如果是这样,我错过了什么?即使我可以在没有控制器功能的情况下测试指令本身,我也会很高兴.

解决方案

我能想到两种方法:

1) 使用两个指令

假设我们有以下指令:

app.directive('foo', function() {返回 {限制:'E',控制器:功能($范围){this.add = 函数(x,y){返回 x + y;}}};});app.directive('bar', function() {返回 {限制:'E',要求:'^foo',链接:函数(范围,元素,属性,foo){scope.callFoo = 函数(x,y){scope.sum = foo.add(x, y);}}};});

为了测试 callFoo 方法,你可以简单地编译这两个指令并让 bar 使用 foo 的实现:

it('确保 callFoo 做它应该做的任何事情', function() {//安排var element = $compile('<foo><bar></bar></foo>')($scope);var barScope = element.find('bar').scope();//行为barScope.callFoo(1, 2);//断言期望(barScope.sum).toBe(3);});

工作Plunker.

2) 模拟 foo 的控制器

这个不太简单,有点棘手.你可以使用 element.controller() 来获取一个元素的控制器,并用 Jasmine 模拟它:

it('确保 callFoo 做它应该做的任何事情', function() {//安排var element = $compile('<foo><bar></bar></foo>')($scope);var fooController = element.controller('foo');var barScope = element.find('bar').scope();spyOn(fooController, 'add').andReturn(3);//行为barScope.callFoo(1, 2);//断言期望(barScope.sum).toBe(3);期望(fooController.add).toHaveBeenCalledWith(1, 2);});

工作Plunker.

当一个指令立即在其 link 函数中使用另一个的控制器时,棘手的部分出现了:

app.directive('bar', function() {返回 {限制:'E',要求:'^foo',链接:函数(范围,元素,属性,foo){scope.sum = foo.add(parseInt(attrs.x), parseInt(attrs.y));}};});

在这种情况下,您需要单独编译每个指令,以便您可以在第二个使用它之前模拟第一个:

it('确保 callFoo 做它应该做的任何事情', function() {//安排var fooElement = $compile('<foo></foo>')($scope);var fooController = fooElement.controller('foo');spyOn(fooController, 'add').andReturn(3);var barElement = angular.element('<bar x="1" y="2"></bar>')fooElement.append(barElement);//行为barElement = $compile(barElement)($scope);var barScope = barElement.scope();//断言期望(barScope.sum).toBe(3);期望(fooController.add).toHaveBeenCalledWith(1, 2);});

工作Plunker.

第一种方法比第二种方法容易得多,但它依赖于第一个指令的实现,即您不是对事物进行单元测试.另一方面,虽然模拟指令的控制器并不那么容易,但它可以让您更好地控制测试并消除对第一个指令的依赖.所以,明智地选择.:)

最后,我不知道有更简单的方法来完成上述所有操作.如果有人知道更好的方法,请改进我的答案.

So I did see another question: How to mock required directive controller in directive UT which is basically my problem but it seems the answer to this thread was "change your design." I wanted to make sure there is no way to do this. I have a directive that declares a controller which is used by children directives. I am now trying to write jasmine tests for the children directive but I cant get them to compile in the tests because they are dependent on the controller. Here is what it looks like:

addressModule.directive('address', ['$http', function($http){
        return {
            replace: false,
            restrict: 'A',
            scope: {
                config: '='
            },
            template:   '<div id="addressContainer">' +
                            '<div ng-if="!showAddressSelectionPage" basic-address config="config"/>' +
                            '<div ng-if="showAddressSelectionPage" address-selector addresses="standardizedAddresses"/>' +
                        '</div>',
            controller: function($scope)
            {
                this.showAddressInput = function(){
                    $scope.showAddressSelectionPage = false;
                };

                this.showAddressSelection = function(){
                    $scope.getStandardizedAddresses();
                };

                this.finish = function(){
                    $scope.finishAddress();
                };
            },
            link: function(scope, element, attrs) {
              ...
            }
       }
}])

child directive:

addressModule.directive('basicAddress360', ['translationService', function(translationService){
        return {
            replace: true,
            restrict: 'A',
            scope: {
                config: '='
            },
            template:
                '...',
            require: "^address360",
            link: function(scope, element, attrs, addressController){
            ...
            }
       }
}])

jasmine test:

it("should do something", inject(function($compile, $rootScope){
            parentHtml = '<div address/>';
            subDirectiveHtml = '<div basic-address>';

            parentElement = $compile(parentHtml)(rootScope);
            parentScope = parentElement.scope();
            directiveElement = $compile(subDirectiveHtml)(parentScope);
            directiveScope = directiveElement.scope();
            $rootScope.$digest();
}));

Is there no way for me to test the sub directive with jasmine and if so, what am I missing? Even if I could test the directive itself without the controller functions I would be happy.

解决方案

I can think of two approaches:

1) Use both directives

Let's assume we have the following directives:

app.directive('foo', function() {
  return {
    restrict: 'E',
    controller: function($scope) {
      this.add = function(x, y) {
        return x + y;
      }
    }
  };
});

app.directive('bar', function() {
  return {
    restrict: 'E',
    require: '^foo',
    link: function(scope, element, attrs, foo) {
      scope.callFoo = function(x, y) {
        scope.sum = foo.add(x, y);
      }
    }
  };
});

In order to test the callFoo method, you can simply compile both directives and let bar use foo's implementation:

it('ensures callFoo does whatever it is supposed to', function() {
  // Arrange
  var element = $compile('<foo><bar></bar></foo>')($scope);
  var barScope = element.find('bar').scope();

  // Act
  barScope.callFoo(1, 2);

  // Assert
  expect(barScope.sum).toBe(3);
});    

Working Plunker.

2) Mock foo's controller out

This one is not quite straightforward and a little tricky. You could use element.controller() to get the controller of an element, and mock it out with Jasmine:

it('ensures callFoo does whatever it is supposed to', function() {
    // Arrange
    var element = $compile('<foo><bar></bar></foo>')($scope);
    var fooController = element.controller('foo');
    var barScope = element.find('bar').scope();
    spyOn(fooController, 'add').andReturn(3);

    // Act
    barScope.callFoo(1, 2);

    // Assert
    expect(barScope.sum).toBe(3);
    expect(fooController.add).toHaveBeenCalledWith(1, 2);
  });

Working Plunker.

The tricky part comes up when one directive uses the other's controller right away in its link function:

app.directive('bar', function() {
  return {
    restrict: 'E',
    require: '^foo',
    link: function(scope, element, attrs, foo) {
      scope.sum = foo.add(parseInt(attrs.x), parseInt(attrs.y));
    }
  };
});

In this case you need to compile each directive individually so you can mock the first one out before the second one uses it:

it('ensures callFoo does whatever it is supposed to', function() {
  // Arrange
  var fooElement = $compile('<foo></foo>')($scope);
  var fooController = fooElement.controller('foo');
  spyOn(fooController, 'add').andReturn(3);

  var barElement = angular.element('<bar x="1" y="2"></bar>')
  fooElement.append(barElement);

  // Act
  barElement = $compile(barElement)($scope);
  var barScope = barElement.scope();

  // Assert
  expect(barScope.sum).toBe(3);
  expect(fooController.add).toHaveBeenCalledWith(1, 2);
});

Working Plunker.

The first approach is way easier than the second one, but it relies on the implementation of the first directive, i.e, you're not unit testing things. On the other hand, although mocking the directive's controller isn't so easy, it gives you more control over the test and removes the dependency on the first directive. So, choose wisely. :)

Finally, I'm not aware of an easier way to do all of the above. If anyone knows of a better approach, please improve my answer.

这篇关于需要控制器的测试指令的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆