如何在AngularJS中的指令测试中触发ng-change

时间:2013-07-13 03:11:29

标签: angularjs angularjs-directive

我有以下AngularJS指令,用于创建input元素。输入具有运行ng-change函数的doIt()属性。在我的指令单元测试中,我想检查用户更改输入时是否调用了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并使其可测试?谢谢。

5 个答案:

答案 0 :(得分:31)

来自您的解释性评论:

  

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

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

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

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

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

答案 1 :(得分:1)

我googled&#34;角度指令触发ng-change&#34;这个StackOverflow问题是我最接近任何有用的问题,所以我将回答&#34;如何在指令中触发ng-change&#34;,因为其他人必然会登陆此页面,我不会#39;知道如何提供这些信息。

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

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

element.trigger("change")element.trigger("input")对我不起作用,我也无法在网上找到任何其他内容。

例如,触发模糊的ng-change:

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

对不起,这不是直接回答OP的问题。我将非常乐意就何处以及如何分享这些信息提出一些合理的建议。

答案 2 :(得分:1)

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

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

答案 3 :(得分:0)

我长时间寻找这条简单的线条。 只是为了保存在这里。

如何使用Karma从html-select中选择值,以便让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 !!!!");
  };

Karma测试片段:

  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'

答案 4 :(得分:0)

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

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

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