Objective
To unit test a directive which loads an external template for the HTML.
Technologies
Grunt, Karma, Jasmine, PhantomJS, AngularJS.
Note: It could be classed as an E2E test if we want to mock the $httpbackend and simulate events together with the controller (also to load the templates into the $templateCache) but for this one we’ll do a simple unit test to get it working. Also we could extend our app to use app.directives and pass that into the test. But for this test everything is under the app module.
Example:
In this example I am testing a bootstrap navbar directive to have been appended to the DOM and contain links.
Directory structure:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
app / bower_components / ... scripts / directives / navbar.js ... ... index.html config / spec - unit.conf.js ... test / spec - unit / directives / navbar.js ... package.json Gruntfile.js bower.json ... |
package.json
1 2 3 4 5 6 7 8 9 10 11 12 |
... "karma": "~0.10.8", "grunt-karma": "~0.6.2", "karma-script-launcher": "~0.1.0", "karma-html2js-preprocessor": "~0.1.0", "karma-chrome-launcher": "~0.1.1", "karma-phantomjs-launcher": "~0.1.1", "karma-jasmine": "~0.1.4", "karma-ng-html2js-preprocessor": "~0.1.0" ... |
spec-unit.conf.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
... files: [ 'app/bower_components/angular/angular.js', 'app/bower_components/angular-mocks/angular-mocks.js', 'app/views/**/*.html', 'app/scripts/*.js', 'app/scripts/**/*.js', 'test/mock/**/*.js', 'test/spec-unit/**/*.js' ], browsers: [ //'Chrome' 'PhantomJS' ], preprocessors: { 'app/views/**/*.html': ['ng-html2js'] }, ngHtml2JsPreprocessor: { stripPrefix: 'app/', }, ... |
/test/spec-unit/directives/directive.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
'use strict'; describe('Directive: navbar', function () { var el, scope; beforeEach(module('app')); beforeEach(module('views/partials/navbar.html')); beforeEach(inject(function ($rootScope, $compile) { el = angular.element('<navbar ng-controller="NavbarCtrl"></navbar>'); scope = $rootScope; $compile(el)($rootScope); scope.$digest(); })); it('should attach a navbar to the page with links', function () { var links = el.find('a'); expect(links.length).toBeGreaterThan(0); }); }); |
/app/scripts/directives/navbar.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 |
'use strict'; /** * @ngdoc directive * @name ngSeed.directives:navbar * @description * Sets the current navbar option in focus. */ angular.module('app') .directive('navbar', ['$location', function ($location) { return { restrict: 'E', replace: true, transclude: true, templateUrl: 'views/partials/navbar.html', scope: { heading: '@' }, controller: 'NavbarCtrl', link: function ($scope, $element, $attrs, NavbarCtrl) { $scope.$location = $location; $scope.$watch('$location.path()', function (locationPath) { var $li, link, $liElements = $element.find("li"); angular.forEach($liElements, function (i, v) { $li = angular.element(i); link = $li.find("a").attr('href'); if (link.toLowerCase() == locationPath) { $li.addClass("active"); } else { $li.removeClass("active"); } }); }); } } } ]); |
/app/index.html
1 2 3 4 5 6 7 8 |
... <body ng-app="app"> <navbar ng-controller="NavbarCtrl"></navbar> ... |
Running the test results in terminal should look like this:
.png)
Tips:
In your directive use Angular.element not $ (jQuery)
You can use Chrome Karma debugger to see whats going either use console.log or debugger; in your code as usual.
.png)
.png)