dnc253 dnc253 - 29 days ago 14
AngularJS Question

How do you mock directives to enable unit testing of higher level directive?

In our app we have several layers of nested directives. I'm trying to write some unit tests for the top level directives. I've mocked in stuff that the directive itself needs, but now I'm running into errors from the lower level directives. In my unit tests for the top level directive, I don't want to have to worry about what is going on in the lower level directives. I just want to mock the lower level directive and basically have it do nothing so I can be testing the top level directive in isolation.

I tried overwriting the directive definition by doing something like this:

angular.module("myModule").directive("myLowerLevelDirective", function() {
return {
link: function(scope, element, attrs) {
//do nothing
}
}
});


However, this does not overwrite it, it just runs this in addition to the real directive. How can I stop these lower level directives from doing anything in my unit test for the top level directive?

Answer

Due to the implementation of the directive registration, it does not seem possible to replace an existing directive by a mocked one.

However, you have several ways to unit test your higher level directive without interference from lower level directives :

1) Do not use lower level directive in your unit test template :

If your lower level directive is not added by your higher level directive, in your unit test use a template with only you higer-level-directive :

var html = "<div my-higher-level-directive></div>";
$compile(html)(scope);

So, lower level directive will not interfere.

2) Use a service in your directive implementation :

You can provide the lower level directive linking function by a service :

angular.module("myModule").directive("myLowerLevelDirective", function(myService) {
    return {
        link: myService.lowerLevelDirectiveLinkingFunction
    }
});

Then, you can mock this service in your unit test to avoid interference with your higher level directive. This service can even provide the whole directive object if needed.

3) You can overwrite your lower level directive with a terminal directive :

angular.module("myModule").directive("myLowerLevelDirective", function(myService) {
    return {
        priority: 100000,
        terminal: true,
        link: function() {
            // do nothing
        }
    }
});

With the terminal option and a higher priority, your real lower level directive will not be executed. More infos in the directive doc.

See how it works in this Plunker.

Comments