一尘不染

需要控制器的测试指令

angularjs

因此,我确实看到了另一个问题:如何在指令UT中模拟所需的指令控制器,这基本上是我的问题,但似乎该线程的答案是“更改设计”。我想确保没有办法做到这一点。我有一个指令声明一个由子指令使用的控制器。我现在正尝试为children指令编写茉莉花测试,但由于它们依赖于控制器,因此我无法让它们在测试中进行编译。看起来是这样的:

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) {
              ...
            }
       }
}])

子指令:

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

茉莉花测试:

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();
}));

我没有办法用茉莉花测试子指令吗?如果可以,我会缺少什么?即使我可以在没有控制器功能的情况下测试指令本身,我也会很高兴。


阅读 180

收藏
2020-07-04

共1个答案

一尘不染

我可以想到两种方法:

1)同时使用两个指令

假设我们有以下指令:

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);
      }
    }
  };
});

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

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);
});

工作柱塞

2)模拟出foo的控制器

这不是很简单,也有些棘手。您可以element.controller()用来获取元素的控制器,并使用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);
  });

工作柱塞

当一个指令在其link功能中立即使用另一个控制器时,就会出现棘手的部分:

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));
    }
  };
});

在这种情况下,您需要分别编译每个指令,以便可以在第二个指令使用它之前对其进行模拟:

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);
});

工作柱塞

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

最后,我不知道执行上述所有操作的简便方法。如果有人知道更好的方法,请改善我的答案。

2020-07-04