如何对角度指令进行单元测试

时间:2015-11-17 13:21:29

标签: javascript angularjs unit-testing

单元测试角度指令并不是很难,但我发现有不同的方法可以做到这一点。

出于本文的目的,我们假设以下指令

angular.module('myApp')
    .directive('barFoo', function () {
        return {
            restrict: 'E',
            scope: true,
            template: '<p ng-click="toggle()"><span ng-hide="active">Bar Foo</span></p>',
            controller: function ($element, $scope) {
                this.toggle() {
                    this.active = !this.active;
                }
            }
        };
    });

现在我可以想出两种单元测试方法

方法1:

describe('Directive: barFoo', function () {
    ...
    beforeEach(inject(function($rootScope, barFooDirective) {
        element = angular.element('<bar-foo></bar-foo>');
        scope = $rootScope.$new();
        controller = new barFooDirective[0].controller(element, scope);
    }));

    it('should be visible when toggled', function () {
        controller.toggle();
        expect(controller.active).toBeTruthy();
    });
});

方法2:

beforeEach(inject(function ($compile, $rootScope) {
    element = angular.element('<bar-foo></bar-foo>');
    scope = $rootScope.$new();
    $compile(element)(scope);
    scope.$digest();
}));

it ('should be visible when toggled', function () {
    element.click();
    expect(element.find('span')).not.toHaveClass('ng-hide');
});

所以,我很好奇这两种方法的优点和缺点是什么,哪种方法最强大?

3 个答案:

答案 0 :(得分:2)

以下是测试AngularJS指令的方法:

describe('Directive: barFoo', function () {
  var createElement, element, parentScope, directiveScope;
  
  beforeEach(module('myApp'));

  beforeEach(inject(function($injector) {
    var $compile = $injector.get('$compile');
    var $rootScope = $injector.get('$rootScope'),

    parentScope = $rootScope.$new();
    parentScope.paramXyz = ...  <-- prepare whatever is expected from parent scope

    createElement = function () {
      element = $compile('<bar-foo></bar-foo>')(parentScope);
      directiveScope = element.isolateScope();
      parentScope.$digest();
      $httpBackend.flush(); <-- if needed
    };
  }));


  it('should do XYZ', function() {
    parentScope.xyz = ...    <-- optionnal : adjust scope according to your test
    createElement();

    expect(...) <-- whatever, examples :

    var submitButton = element.find('button[type="submit"]');
    expect( submitButton ).to.have.value('Validate');
    expect( submitButton ).to.be.disabled;
    submitButton.click();
  });

答案 1 :(得分:1)

我喜欢做的就是创建我的测试故事,就像这个虚拟的例子一样。

'use strict';

describe('app controller', function() {

  var scope;

  ...   

  beforeEach(angular.mock.module('MyModule'));

  it('should have properties defined', function() {

    expect(scope.user).toEqual(user);
    ...    

  });

  it('should have getFaqUrl method', function() {

    expect(scope.getFaqUrl).toEqual(refdataService.getFaqUrl);

  });

  it('should signout and delete user data from local storage', function() {

    ...

  });

});

所以我想你在第二个例子中并没有说明它,但是如果你这样做了,在测试时总是使用describe enclosure,这只是一个很好的做法。

至于测试本身,我建议避免使用明确调用范围的方法。$ digest(),特别是因为它对于测试来说似乎没有必要。

很快,我会选择方法1.

答案 2 :(得分:1)

我发现第一种方法更“正确”,因为它不依赖于点击事件。我相信如果你想测试元素的点击及其效果,你应该使用量角器并仅使用茉莉花进行单元测试。通过这种方式,您可以在单元测试和UI测试之间实现良好的分离。

它也使测试更加可维护。例如如果您决定在悬停时触发toggle而不是在第二种方法中点击,则您还必须更新测试。