I am unit testing an Angular directive and would like to mock or stub in some way the instantiation of the named controller in the unit test.
So first I suppose on to some code...
'use strict';
angular.module('App.Directives.BreadCrumbs', [])
.directive('kxBreadcrumbs', function () {
return {
restrict: 'E',
controller: 'BreadCrumbsController',
template:
'<!-- Breadcrumbs Directive HTML -->' +
'<ol class="breadcrumb">' +
' <li ng-repeat="crumb in crumbPath">' +
' <a ng-class="{true: \'disable\', false: \'\'}[crumb.last]" href="{{crumb.href}}" ng-click="updateCrumb(crumb.name)">{{crumb.name}}</a>' +
' </li>' +
'</ol>' +
'<!-- End of Breadcrumbs Driective HTML -->'
};
});
This is one sample directive that I would unit test, the important thing to take away from this is the named controller.
So in my unit test
'use strict';
describe('Directives: Breadcrumbs', function () {
var//iable declarations
elm,
scope,
$rootScope
;
beforeEach(function () {
module('App.Directives.BreadCrumbs');
module('App.Controllers.BreadCrumbs');
module('App.Constants'); // <--- Comes from the controller dependancy
});
beforeEach(inject(function (_$rootScope_, $compile) {
$rootScope = _$rootScope_;
scope = $rootScope.$new();
elm = angular.element('<kx-breadcrumbs></kx-breadcrumbs>');
$compile(elm)(scope);
scope.$apply();
}));
it('Should create the breadcrumbs template', function () {
scope.crumbPath = [{name: 'home', href: '/'},{name: 'coffee', href: '/coffee'},{name: 'milk', href: '/milk'}];
scope.$apply();
var listItem = $(elm).find('li');
expect(listItem.length).toBe(3);
expect($(listItem).text()).toContain('home');
expect($(listItem).text()).toContain('coffee');
expect($(listItem).text()).toContain('milk');
});
});
You can see the inclusion of the 3 modules - the directive, the controller and the third one the constants. This is referenced by the controller as a dependancy so in order to pull this into the unit test I need to pull in the dependancy or in much worse cases the dependancies from the controller. But as I am not unit testing the functionality of the controller in the directive unit test, this seem redundant and bloating of code through inclusion of modules. Ideally I would like to only include the module that I am unit testing.
module('App.Directives.BreadCrumbs');
and not (modules added to exemplify my point more)
module('App.Directives.BreadCrumbs');
module('App.Controllers.BreadCrumbs');
module('App.Constants'); // <--- Comes from the controller dependancy
module('App.Service.SomeService'); // <--- Comes from the controller dependancy
module('App.Service.SomeOtherService'); // <--- Comes from the SomeService dependancy
When we unit test controllers we can mock services that are passed in either completely or by using jasmine spies. Can we accomplish the same sorta thing in unit test of directives so I don't have to follow the dependancy trail?
You can create mocks in module configuration block by using $controllerProvider.register()
for controllers, $provide.provider()
, $provide.factory()
, $provide.service()
and $provide.value()
for providers, factories and services:
JavaScript
beforeEach(function () {
module('App.Directives.BreadCrumbs', function($provide, $controllerProvider) {
$controllerProvider.register('BreadCrumbsController', function($scope) {
// Controller Mock
});
$provide.factory('someService', function() {
// Service/Factory Mock
return {
doSomething: function() {}
}
});
});
});
Once you do so, Angular will inject your mock BreadCrumbsController
controller into kxBreadcrumbs
directive. This way you don't need to include real controller and it's dependencies into unit test.
For more information see Angular's official documentation on: