Tuesday, April 26, 2016

All Code on Github

Introduction

Angular Directives are part of the web part/components group of client-side tools that allow quick additions to web pages. The idea is that with a very simple and short addition to an html page, complex functionality and UX are available.

Imagine a user login form with the traditional validation contained in a html template and Angular controller. The main, calling web page’s html could just include <login></login> to bring in that rich validation and display. Directives are wonderful for encapsulating the complexity away from the containing HTML.

Testing the directive is trickier. Granted the controller isn’t difficult to test. But the compiled html and scope are not so easy.

Perhaps it is enough to test the controller, and depend on the browser and the Angular library to manage the rest. You could definitely make that case. Or perhaps you leave the compiled directive testing to the end to end (e2e) tests. That is also a fair. If either of these solutions doesn’t work for you, this article will explain 3 ways to test the compiled Angular directive.

Template
When you build the directive, you can choose static (hopefully short) html in the definition of the directive. Notice that {{data}} will be replaced with the $scope.data value from the directive.

Compilation

Angular compiles the controller and the template in order to replace the html tag. This compilation is necessary to test the directive.

Prerequisites

This article assumes you have some understanding of javascript, Angular, and unit testing. I’ll focus on how the test was setup and compiled the directive so that it could be tested. You need node and npm to install packages and run scripts. The other dependencies are listed in the package.json files.

Karma, Mocha, Chai, Phantom Test System

Since Angular is a client-side framework, Karma acts as the web server and manages the web browser for html and javascript. Instead of running a real browser, I chose Phantom so everything can run from the command line. Mocha and chai are the test framework and assert library.

While the Angular code is browserified, that doesn’t have any impact on how the directive is tested.

The Source Code and Test

With very little difference, each of the 3 examples is just about the same: a very simple controller that has $scope.data, an html template that uses {{data}} from the scope, and a test that compiles the directive and validates that the {{data}} syntax was replaced with the $scope.data value.

As part of the test setup, in the beforeEach functions, the test brings in the angular app, brings in the mock library, and sets the scope. Inside the test (the ‘it’ function), the element function defines the directive’s html element, and compiles the scope and the element. The compiled html text value is tested against the scope value – which we expect to be the same. The overall test concept is the same for each of the examples. Get the controller and template, compile it, check it. The details differ in exactly how this is done in the 2 remaining examples.

The 2 TemplateUrl Examples

The first example above relied on the directive definition to load the template html. The next 2 examples use the TemplateUrl property which has the html stored in a separate file so that method won’t work. Both of the next 2 examples use the templateCache to load the template, but each does it in a different way.The first example loads the template and tests the template as though the templateCache isn’t used. This is a good example for apps that don’t generally use the templateCache and developers that don’t want to change the app code in order to test the directive. The templateCache is only used as a convenience for testing. The second example alters the app code by loading the template in the templateCache and browserifying the app with the ‘templates’ dependency code. This is a good way to learn about the templateCache and test it.

Testing TemplateUrl – least intrusive method

The second example stores the html in a separate file instead of in the directive function. As a result, the test (and the karma config file) needs to bring the separate file in before compiling the element with the scope. The controller:

As part of the test setup, in the beforeEach functions, the test brings in the angular app, brings in the mock library, brings in the html template, and sets the scope. The template is brought in via a help function in another file in the test folder called preloadTemplate. Inside the test (the ‘it’ function), the element function defines the directive’s html element, and compiles the scope and the element. The compiled html text value is tested against the scope value – which we expect to be the same.This is the line of the test file that deals with the templateCache:

beforeEach(preloadTemplate('/templates/menu.html'));

The app and test code do not use the templateCache in any other way. The test itself is almost identical to the first test that uses the static html in the directive.

Testing TemplateUrl – most intrusive method

In this last example, the app.js code in the /client directory is altered to pull in a new ‘templates’ dependency module. The templates module is built in the gulpfile.js by grabbing all the html files in the /public/templates directory and wrapping it in javascript that adds the templates to the templateCache. The test explicitly pulls the template from the templateCache before compilation. gulpfile.js – to build templates dependency into /client/templates.js

The Test Results

The test results for each of the 3 projects is checked in to the github project. Karma ran with debug turned on so that the http and file requests could be validated. When you look at the testResult.log files (1 in each of the 3 subdirectories), you want to make sure that the http and file requests that karma made were actually successful. The lines with ‘Fetching’, ‘Requesting’, and ‘(cached)’ are the important lines before the test runs.