AngularJS provides support for testing directives.  Testing directives that use inline templates -- html that is embedded within the directive's code -- is a straightforward process.  What isn't straightforward is testing a directive that uses an external html template.

The Example

I have created an 'ssn' directive whose purpose is to display a user's social security number split out into three different fields, validate an ssn across all these three fields, and store the social security number as a single field internally.  The directive is as follows:

directivesModule.directive('ssn', function()
	return {
		restrict: 'A',
		replace: true,
		scope: {
			ssn: '=ngModel',
		//templateUrl points to an external html template.
		templateUrl: '/myApp/templates/ssnControl.htm', 

The file referenced in the templateUrl config option contains:

<div class="ssnContainer">
	<input type="text" tabindex="{{tabIndex1}}" name="ssn1" class="ssn1" ng-disabled="disabled"
	       placeholder="XXX"  maxlength="3" ng-model="ssn1"/>
	<input type="text" tabindex="{{tabIndex2}}" name="ssn2" class="ssn2" ng-disabled="disabled"
	       placeholder="XX"  maxlength="2" ng-model="ssn2"/>
	<input type="text" tabindex="{{tabIndex3}}" name="ssn3" class="ssn3" ng-disabled="disabled"
	       placeholder="XXXX" maxlength="4" ng-model="ssn3"/>

The above html would be cumbersome to include directly in the directive file and is correctly externalized from the directive code.  We now need to configure Angular's test framework to include the file in the testing classpath and process the file so it may be used by Angular during testing.

Configuring AngularJS to include and Pre-process the directive's external html template


files = [

  //include the directory where directive templates are stored.

// generate js files from html templates to expose them during testing.
preprocessors = {
  'main/webapp/templates/**/*.htm': 'html2js'


The first configuration to set up is actually including the template in the test classpath.  This is done by adding an item in the files array of the karma.conf.js config file (line 16).  This will make the html file available to karma's html2js pre-processor.  the html2js pre-processor is responsible for converting HTML files into AngularJS templates.  Adding a preprocessor is done by referencing a file path and supplying the name of a pre-processor to act on that file path (lines 20 - 22).  The newly generated AngularJS template is now ready to be referenced in the directive's unit test.

Exposing the AngularJS Template to the Directive Under Test

There are 2 steps needed to expose the template to the directive:

  1. 1. Load the template file as if it was a module (line 8)
  2. 2. map the url of the template file in the template cache to the url the directive will use to access it during runtime (lines 13-14)

Loading a template as a module is straightforward, placing the template in the template cache bears some explanation.

The Template Cache

AngularJS maintains a cache of all html files it has converted into AngularJS templates, storing each cache in a configuration object.  The cache's config object keys are the urls of the template files as downloaded from the server.  In our situation, the url of the file during tesing will differ from the url when loaded during runtime.  If we did nothing to fix this, the template would never be found by the directive because it would be trying to load an AngularJS template using a template key (the url) that doesn't exist in the config object.  Inserting a new entry into the template cache (lines 13-14) with the url that the directive will be calling at runtime solves this problem

describe("ssnControl Directive", function() {

	var $compile, $rootScope, template;

	//load all modules, including the html template, needed to support the test

	beforeEach(inject(function($templateCache,_$compile_,_$rootScope_) {

		//assign the template to the expected url called by the directive and put it in the cache
		template = $templateCache.get('main/webapp/templates/ssnControl.htm');

		$compile = _$compile_;
		$rootScope = _$rootScope_;

	it("should display 3 text input fields, populated with ssn data", function() {

		var ssn1 = '123';
		var ssn2 = '45';
		var ssn3 = '6789';

		$rootScope.ssn = ssn1 + ssn2 + ssn3;

		//create the element angularjs will replace with the directive template
		var formElement = angular.element('<div ssn ng-model="ssn"></div>');
		var element = $compile(formElement)($rootScope);


		//use jquery to find the sub elements.



Now, when the directive is instantiated by the AngularJS framework, it will be able to fetch its processed template and allow you to test.


The ability to test AngularJS directives provides an extremely powerful tool to use during your web app's development.  Implementing the above steps will make you able to follow the best practice of maintaining external html directives from your directive's code, and at the same time be able to test directives with complex html templates.