单元测试角度指令更新ngModel

时间:2014-11-26 13:45:11

标签: angularjs unit-testing jasmine karma-runner

我试图测试我为验证输入而编写的指令而我遇到了一些麻烦。 由指令管理的输入应包含有效的十六进制颜色值,如果用户使用无效值修改它,我想取消此修改。我的指令如下,并按预期工作:

module.directive('colorValidate', function() {
    return {
        restrict: 'A',
        scope: {
            color: '=ngModel'
        },
        link: function(scope, element) {
            var previousValue = '#ffffff';
            //pattern that accept #ff0000 or #f00
            var colorPattern = new RegExp('^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$');
            element.on('focus', function() {
                previousValue = scope.color;
            });
            element.on('blur', function() {
                if (!colorPattern.test(scope.color)) {
                    scope.$apply(function() {
                        scope.color = previousValue;
                    });
                }
                else {
                    scope.$apply(function() {
                        scope.color = scope.color.toLowerCase();
                    });
                }
            });
        }
    };
});

以下是使用此指令的输入示例:

<input color-validate type="text" ng-model="color.color"/>

第一个问题:这种访问和修改元素的ngModel的方式是否正确?

然后我的主要问题是测试部分。以下是我编写的两个简单的测试,但这些测试并没有真正起作用:

describe('colorValidate directive', function() {
    var scope,
        elem,
        compiled,
        html;

    beforeEach(function() {
        html = '<input color-validate type="text" ng-model="color.color"/>';

        inject(function($compile, $rootScope) {
            scope = $rootScope.$new();
            scope.color = {color: '#aaaaaa'};
            elem = angular.element(html);
            compiled = $compile(elem);
            compiled(scope);
            scope.$digest();
        });
    });

    it('should permit valid 6-chars color value', function() {
        elem.triggerHandler('focus');
        elem.val('#FF0000');
        elem.triggerHandler('blur');
        scope.$digest();
        expect(elem.val()).toBe('#FF0000');
    });

    it('should reject non valid color values', function() {
        elem.triggerHandler('focus');
        elem.val('#F00F');
        scope.$digest();
        elem.triggerHandler('blur');
        expect(elem.val()).toBe('#aaaaaa');
    });
});

第一次测试成功,第二次测试失败,因为测试值是&#39;#F00F&#39;而不是#aaaaaa&#39;。基本上,我的测试都没有实际修改指令处理的ngModel值...

1 个答案:

答案 0 :(得分:7)

调用elem.val()实际上并不会导致scope.color得到更新。换句话说,这个测试将失败:

it("should set scope", function () {
   elem.triggerHandler("focus");
   elem.val("#FF0000");
   scope.$digest();
   elem.triggerHandler("blur");

   //Will fail: expected #aaaaaa to be #ff0000
   expect(scope.color.color).toBe("#ff0000");
});

这是因为ngModel绑定到输入上的键事件并在那时更新模型(范围)。在元素上调用val()或value不会触发angular会认为某些内容发生变化的事件(即使在$ digest循环中)。因此,您应该通过更改模型值并声明它们被接受或重置来运行测试:

it('should permit valid 6-chars color value', function() {
    elem.triggerHandler('focus');
    scope.color.color = '#FF0000';
    //need to trigger a digest here for the two-way binding
    scope.$digest();
    elem.triggerHandler('blur');
    //Don't need a $digest here because you call scope.$apply() within the blur in both if/else conditions
    //scope.$digest();
    expect(scope.color.color).toBe('#ff0000');
});

it('should reject non valid color values', function() {
    elem.triggerHandler('focus');
    scope.color.color = '#F00F';
    //need to trigger a digest here for the two-way binding
    scope.$digest();
    elem.triggerHandler('blur');
    expect(scope.color.color).toBe('#aaaaaa');
});

您不需要测试该值是否已更新,因为大概是angular已经编写了测试以确保当指令具有双向绑定(= ngModel)时,当范围值发生变化时视图会更新指令。