测试使用jQuery的Angular指令

Test an Angular directive that uses jQuery

本文关键字:Angular 指令 jQuery 测试      更新时间:2023-09-26

我在为一些Angular指令编写单元测试时遇到问题。特别是那些在指令中使用jQuery的指令。我在下面设计了一个简单的例子来说明我的问题。

这个愚蠢的指令将click事件绑定到元素。单击时,它将隐藏该元素。根据Angular的说法,传递到指令中的元素将被包装为jQuery元素。如果jQuery可用,它将使用jQuery,否则它将使用Angular的jQueryLite。事实上,如果我在包含jQuery的浏览器中使用该指令,该指令会起作用,并隐藏单击的元素。

angular.module('myApp').directive('clickhide', function() { return {
    link: function(scope, element, attrs) { 
        element.bind('click', function(e) {
            element.hide();
            scope.$digest();
        });
    }
}});

以下是该指令的Karma单元测试规范:

describe('clickhide', function() {
    var scope, elm;
    beforeEach(module('MyApp'));
    beforeEach(inject(function($rootScope, $compile) {
        scope = $rootScope;
        elm = angular.element('<div clickhide></div>');
        $compile(elm)(scope);
        scope.$digest();
    }));
    it('should do a click', function() {
        elm[0].click();
        //OOPS: undefined is not a function error
        expect($(elm).find(":hidden").length).toBe(1);
    });
});

当我运行这个测试时,它失败了,错误为"未定义不是函数",这意味着没有加载jQuery,Angular使用了jQueryLite,它没有定义hide((。

我找到了两种方法来解决这个问题。

  1. 不要使用jQuery。需要重写我不想做的指令。

  2. 在我的指令中显式包装元素,使它们成为jQuery元素:$(element).hide()。还需要修改我的指令,我宁愿不这样做。如果别无选择,我当然可以这样做。

我觉得Angular应该可以在单元测试时自动在指令中使用jQuery,就像在web浏览器中使用一样。我相信关键点是Angular文档中的这一行:

要使用jQuery,只需在触发DOMContentLoaded事件之前加载它。

我不清楚在运行单元测试时什么时候会发生DOMContentLoaded。我的Karma配置文件包括jQuery作为第一个文件,在angular之前:

module.exports = function(config) {
  config.set({
    basePath: '../../',
    frameworks: ['jasmine', 'ng-scenario'],
    files: [
      'app/bower_components/jquery/dist/jquery.min.js',
      'app/bower_components/angular/angular.js',
      'app/bower_components/angular-route/angular-route.js',
      'app/bower_components/angular-sanitize/angular-sanitize.js',
      'app/bower_components/angular-mocks/angular-mocks.js',
      ...
    ],
    exclude: [],
    reporters: ['progress'],
    port: 9876,
    runnerPort: 9100,
    colors: true,
    logLevel: config.LOG_INFO,
    autoWatch: true,
    browsers: ['Chrome'],
    captureTimeout: 60000,
    singleRun: false
  });
};

我需要在测试中注入jQuery吗?也许这是因果报应的限制?有人知道吗?

尝试在测试的每个块之前覆盖$in。

beforeEach(function() {
  $.fn.hide = function() {
    this.css( "display", "none" )
  };
});

我能够通过在angular之前加载jquery来解决类似的问题。此外,如果$对您来说失败了,您可以始终使用angular.element(''(来代替$