It has been a rough day, trying to do it, and reading lots of methods on how to do it. To make a long story longer, we were looking for a way to test a directive that requires a parent’s controller, without using the (still none existent) parent.
For instance, let’s take this example:
angular.module('nestedDirective') .directive('fatherDirective', function(){ return { controller: function(){}, controllerAs: 'fatherCtrl', template: '<child-directive></child-directive>' } }) .directive('childDirective', function(){ return { controller: function(){}, controllerAs: 'childCtrl', template: '<div></div>', require: '^^fatherDirective' } });
In many cases, we saw people offering to do the following test:
describe('test childDirective', function(){ var $rootScope, $compile, scope, element, ctrl; beforeEach(module('nestedDirectives')); beforeEach( inject(function(_$rootScope_, _$compile_){ $rootScope = _$rootScope_; $compile = _$compile_; scope = $rootScope.$new(); element = $compile(angular.element('<father-directive></father-directive>'))($scope); // get the child directive's element element = element.find('child-directive'); // get the child directive's controller ctrl = element.scope().childCtrl; }) ) })
While this method works, it is not what we wanted. Assume that fatherDirective also requires its parent? Or maybe several parents? Do I need to mock all the data all the way to the <html> tag?
The way we found was to mock the parent’s controller, and add it to some div element that wraps the childDirective:
describe('test childDirective', function(){ var $rootScope, $compile, scope, element, ctrl; var mockedFatherCtrl = { }; beforeEach(module('nestedDirectives')); beforeEach( inject(function(_$rootScope_, _$compile_){ $rootScope = _$rootScope_; $compile = _$compile_; scope = $rootScope.$new(); element = $compile(angular.element('<div><child-directive></child-directive></div>'))($scope); // add the mock controller to the father element.data('$fatherDirectiveController', mockedFatherCtrl); // get the child directive's element element = element.find('child-directive'); // get the child directive's controller ctrl = element.scope().childCtrl; }) ) })
It appears that angular adds the directives’ controllers to the directives’ elements and you can access them all via the element.data() that will list them for you. The syntax is:
‘$’ + directiveName + ‘Controller’
So the line
element.data('$fatherDirectiveController', mockedFatherCtrl);
actually adds the mocked controller as the father directive’s controller that the child directive is looking for via the require attribute. This way, you can do real isolated testing for directives that require controllers.
Thank you very much it helped me while testing an directive which had parent directive dependencies. I wanted to ignore all of the parent dependencies. I mocked the parent controller part and it worked.
element.data(‘$fatherDirectiveController’, mockedFatherCtrl);
is nice trick.
Glad it could help 🙂