简体   繁体   中英

How to unit test an angular directive

Unit testing an angular directive is not very hard, but I found out that there are different ways to do it.

For the purpose of this post, lets assume the following directive

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

Now I can think of two ways to unit test this

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

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

So, I'm curious what the pro's and cons are of both methods and which one is most robust ?

Here is how you test your AngularJS directive :

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

What I like to do is to create stories of my test like this dummy example.

'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() {

    ...

  });

});

So I guess you just didn't state it in second example, but in case you did, use describe enclosure always when testing, just a good practice.

As for the test themselves, I would advise on avoiding the method where you explicitly call for scope.$digest(), especially as it does not seem necessary for the purpose of your test.

Shortly, I would go for method 1.

I find the first method more "correct" because it doesnt depend on a clicking event. I believe that if you want to test the clicking of an element and the effects of it you should use protractor and use jasmine only for unit tests. In that way you will have a nice separation between the unit tests and the UI tests.

Also it makes the test more maintable. eg if you decide to fire toggle on hover instead of on click in the second method you will have to update the tests as well.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM