I can imagine two approaches:
1) Use both directives
Suppose we have the following directives:
app.directive('foo', function() { return { restrict: 'E', controller: function($scope) { this.add = function(x, y) { return x + y; } } }; }); app.directive('bar', function() { return { restrict: 'E', require: '^foo', link: function(scope, element, attrs, foo) { scope.callFoo = function(x, y) { scope.sum = foo.add(x, y); } } }; });
To test the callFoo method, you can simply compile both directives and let bar use the foo implementation:
it('ensures callFoo does whatever it is supposed to', function() { // Arrange var element = $compile('<foo><bar></bar></foo>')($scope); var barScope = element.find('bar').scope(); // Act barScope.callFoo(1, 2); // Assert expect(barScope.sum).toBe(3); });
Worker Plunker .
2) mock foo controller
It is not quite simple and a bit complicated. You can use element.controller() to get the element's controller, and mock it with Jasmine:
it('ensures callFoo does whatever it is supposed to', function() { // Arrange var element = $compile('<foo><bar></bar></foo>')($scope); var fooController = element.controller('foo'); var barScope = element.find('bar').scope(); spyOn(fooController, 'add').andReturn(3); // Act barScope.callFoo(1, 2); // Assert expect(barScope.sum).toBe(3); expect(fooController.add).toHaveBeenCalledWith(1, 2); });
Worker Plunker .
The tricky part is when one directive immediately uses another controller in its link function:
app.directive('bar', function() { return { restrict: 'E', require: '^foo', link: function(scope, element, attrs, foo) { scope.sum = foo.add(parseInt(attrs.x), parseInt(attrs.y)); } }; });
In this case, you need to compile each directive individually so that you can make fun of the first one before the second uses it:
it('ensures callFoo does whatever it is supposed to', function() { // Arrange var fooElement = $compile('<foo></foo>')($scope); var fooController = fooElement.controller('foo'); spyOn(fooController, 'add').andReturn(3); var barElement = angular.element('<bar x="1" y="2"></bar>') fooElement.append(barElement); // Act barElement = $compile(barElement)($scope); var barScope = barElement.scope(); // Assert expect(barScope.sum).toBe(3); expect(fooController.add).toHaveBeenCalledWith(1, 2); });
Worker Plunker .
The first approach is simpler than the second, but it is based on the implementation of the first directive, i.e. you are not checking objects. On the other hand, while mocking the directory controller is not so easy, it gives you more control over the test and eliminates the dependency on the first directive. So choose wisely. :)
Finally, I donβt know an easier way to do all this. If anyone knows of a better approach, please improve my answer.