【问题标题】:How to trigger ng-change in directive test in AngularJS如何在AngularJS的指令测试中触发ng-change
【发布时间】:2013-07-13 03:11:29
【问题描述】:

我有以下 AngularJS 指令,它创建了一个 input 元素。输入具有运行doIt() 函数的ng-change 属性。在我的指令的单元测试中,我想检查用户更改输入时是否调用了 doIt 函数。但是测试没有通过。虽然在手动测试时它可以在浏览器中工作。

指令:

...
template: "<input ng-model='myModel' ng-change='doIt()' type='text'>" 

测试:

el.find('input').trigger('change') // Dos not trigger ng-change

现场演示(ng-change):http://plnkr.co/edit/0yaUP6IQk7EIneRmphbW?p=preview


现在,如果我手动绑定 change 事件而不是使用 ng-change 属性,则测试通过。

template: "<input ng-model='myModel' type='text'>",
link: function(scope, element, attrs) {
  element.bind('change', function(event) {
    scope.doIt();
  });
}

现场演示(手动绑定):http://plnkr.co/edit/dizuRaTFn4Ay1t41jL1K?p=preview


有没有办法使用ng-change 并使其可测试?谢谢。

【问题讨论】:

  • ng-change 在模型更改时触发,而不是事件。为什么不只为您的“doIt”fn 测试控制器代码?
  • @marko,谢谢你的好问题。我想要在指令的测试中做的就是检查当用户更改输入时是否调用了 doIt。 doIt 本身将在指令的测试中被模拟,因为它已经在控制器的测试中进行了测试,正如你所建议的那样。

标签: angularjs angularjs-directive


【解决方案1】:

来自您的解释性评论:

我想要在指令的测试中做的就是检查doIt 在用户更改输入时是否被调用。

ng-change 指示的表达式是否被正确评估实际上是 ngModel 指令的责任,所以我不确定我是否会以这种方式测试它;相反,我相信 ngModel 和 ngChange 指令已正确实现并经过测试以调用指定的函数,并且只需测试调用函数本身以正确的方式影响指令。端到端或集成测试可用于处理完整使用场景。

也就是说,您可以获取驱动 ngModel 更改回调的 ngModelController 实例并自己设置视图值:

it('trigger doIt', function() {
  var ngModelController = el.find('input').controller('ngModel');
  ngModelController.$setViewValue('test');
  expect($scope.youDidIt).toBe(true);
});

不过,正如我所说,我觉得这超出了 ngModel 的职责范围,打破了使用自然可组合指令获得的黑盒。

示例:http://plnkr.co/edit/BaWpxLuMh3HvivPUbrsd?p=preview


[更新]

看了一圈AngularJS源码,发现下面的也行:

it('trigger doIt', function() {
  el.find('input').trigger('input');
  expect($scope.youDidIt).toBe(true);
});

在某些浏览器中该事件似乎有所不同; input 似乎适用于 Chrome。

示例:http://plnkr.co/edit/rbZ5OnBtKMzdpmPkmn2B?p=preview

这里是relevant AngularJS code,它使用$sniffer 服务来确定要触发的事件:

changeInputValueTo = function(value) {
  inputElm.val(value);
  browserTrigger(inputElm, $sniffer.hasEvent('input') ? 'input' : 'change');
};

即使有这个,我也不确定我是否会以这种方式测试指令。

【讨论】:

  • 关于事件,我认为input 用于文本输入等,change 用于选择框和单选按钮。至少这对我在 PhantomJS 上运行测试有用。
  • 看来 browserTrigger 只存在于 angular-scenario.js 中。这意味着它仅用于 e2e 测试,对吧?我无法将此功能用于单元测试,我相信这是故意的。
  • @unludo 也许;我的观点只是 AngularJS 源代码是为了在某些浏览器中触发不同的事件而编写的。
【解决方案2】:

我搜索了“角度指令触发 ng-change”,这个 StackOverflow 问题是我最接近任何有用的问题,所以我会回答“如何在指令中触发 ng-change”,因为其他人一定会登陆此页面,我不知道如何提供此信息。

在指令的链接函数中,这将触发元素上的 ng-change 函数:

element.controller('ngModel').$viewChangeListeners[0]();

element.trigger("change") 和 element.trigger("input") 对我不起作用,我在网上也找不到其他任何东西。

例如,在 blur 上触发 ng-change:

wpModule.directive('triggerChangeOnBlur', function () {
    return {
        restrict: 'A',
        link: function (scope, element, attrs) {
            element.on('blur', function () {
                element.controller('ngModel').$viewChangeListeners[0]();
            });
        }
    };
}]);

很抱歉,这不是直接回答 OP 的问题。我非常乐意就在哪里以及如何分享这些信息提出一些合理的建议。

【讨论】:

  • 理想情况下,您会问一个单独的问题,然后自己回答(听起来很奇怪,但值得鼓励)。我也在寻找不完全是 OP 或您的问题的东西,如果有专门的问题,我会更快地找到答案。
【解决方案3】:

简单而且有效 在您的单元测试环境中:

spyOn(self, 'updateTransactionPrice');


var el = compile('<form name="form" latest novalidate json-schema="main.schema_discount" json-schema-model="main._data"><input type="text" ng-model="main._data.reverse_discount" ng-class="{ \'form-invalid\': form.reverse_discount.$invalid }" ng-change="main.transactionPrice(form);" name="reverse_discount" class="form-control-basic" placeholder="" ng-disabled="!main.selectedProduct.total_price"></form>')(scope);
el.find('input').triggerHandler('change');

expect(self.updateTransactionPrice).toHaveBeenCalled();

【讨论】:

    【解决方案4】:

    我一直在寻找这条简单的线路。 只是为了把它保存在这里。

    如何从 html-select 中选择值,使用 Karma,从而使 ng-change 功能正常工作?

    HTML:

    控制器或指令JS:

      $scope.itemTypes = [{name: 'Some name 1', value: 'value_1'}, {name: 'Some name 2', value: 'value_2'}]
    
      $scope.itemTypeSelected = function () {
        console.log("Yesssa !!!!");
      };
    

    业力测试片段:

      angular.element(element.find("#selectedItemType")[0]).val('value_1').change();
      console.log("selected model.selectedItemType", element.isolateScope().model.selectedItemType);
    

    控制台:

    'Yesssa !!!!'
    'selected model.selectedItemType', 'value_1'
    

    【讨论】:

      【解决方案5】:

      一直试图让它工作,但每次尝试都失败了。最后得出结论,我的 ng-model-options 在 onUpdate 上设置了去抖动设置,是问题所在。

      如果您有去抖,请确保您使用 $timeout 服务刷新。在 Angular mock 中,此超时服务已通过刷新操作进行扩展,该操作处理所有未完成的请求/操作。

          var tobetriggered = angular.element(element[0].querySelector('.js-triggervalue'));
          tobetriggered.val('value');
          tobetriggered.trigger('change');
          $timeout.flush();
      

      【讨论】:

        猜你喜欢
        • 2014-10-04
        • 2019-11-21
        • 1970-01-01
        • 2016-12-09
        • 2018-01-02
        • 1970-01-01
        • 2014-08-04
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多