3

I have a directive for which i am trying to write some unit test for:

return {
    restrict: 'E',
    replace: true,
    controllerAs: 'vm',
    transclude: true,
    template:   '<ul>' +
                    '<li ng-show="vm.hideItem">Home</li>' +
                    '<li ng-show="vm.hideItem">Products</li>' +
                    '<li ng-show="!vm.hideItem">Cart</li>' +
                    '<li ng-show="vm.hideItem">Contact Us</li>' +
                '</ul>',  
    link: function(scope, element, attrs, vm) {

        function getData(index) {
            if (index){
                vm.hideItem = true
            }
            else {
                var li = element.find("li");
                li.attr("context-driven", "");
            }
        }

        function getIndex(){
            index = myService.getIndex();
            getData(index);
        }

        getIndex();

    },
    controller: function(){}
};

I have the following, which pass:

describe('<-- myDirective Spec ------>', function () {

    var scope, $compile, element, myService;

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

    beforeEach(inject(function (_$rootScope_, _$compile_, _myService_) {
        scope = _$rootScope_.$new();
        $compile = _$compile_;
        myService = _myService_;

        var html = '<my-directive></my-directive>';
        element = $compile(angular.element(html))(scope);
        scope.$digest();
    }));

    it('should return an unordered list', function () {
        var ul = element.find('ul');
        expect(ul).toBeDefined();
    });

How can i test the call of getIndex, getData and ensure myService has been called?

1
  • It is not possible to do what you're asking without refactoring the code. Again, it is testability reasons that should define the way the code is written, not vice versa. Test-unfriendly code won't get 100% test coverage (testing myService.getIndex call is all that could be done here). Commented Apr 25, 2016 at 4:34

1 Answer 1

4
+50

The key to successful directive testing is moving all view-related logic to controller, i.e.

    this.getIndex = function () {
        index = myService.getIndex();
        getData(index);
    }

After the element was compiled in spec, the controller instance can be retrieved and spied with

var ctrl = element.controller('myDirective');
spyOn(ctrl, 'getIndex').and.callThrough();

The good thing about writing specs is that they show design flaws. In current case it is DOM manual operation in getData. It is not clear from the code what context-driven attribute is for, but the same thing has to be achieved in Angular (data binding) and not jQuery (DOM manipulation) fashion in order to be test-friendly.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.