Quality is a top priority for our load testing tool. Today I wrote my first AngularJS directive unit test. testing , angularjs , json https://octoperf.com/blog/2015/07/01/angular-directives-unit-testing/ OctoPerf ZI Les Paluds, 276 Avenue du Douard, 13400 Aubagne, France +334 42 84 12 59 contact@octoperf.com Development 598 2021-01-04

Unit testing AngularJS directives

OctoPerf is JMeter on steroids!
Schedule a Demo

As we are currently working on marketing our load testing solution we don’t get much time left for coding. We identified some issues that could spoil user experience. It could make us loose some prospects.

So I took one day off backlinking / mailing / phoning / marketing to return to my beloved IDE. One day of coding in six weeks, it feels like holidays! I took the opportunity to improve our frontend SonarQube metrics. We now have only 5 hours left of technical debt. Not as well as the backend code quality, but it is on the right track:

Technical debt

I fixed a large part of the issues and started to unit tests our AngularJS directives using Karma.

A first try with AngularJS directives and Karma

A simple directive

In OctoPerf we use a simple directive to format error messages:

1
2
3
4
5
6
7
8
9
app.directive('appErrorMessage', function() {
    return {
        restrict: 'EA',
        scope: {
            errorMessage : '='
        },
        templateUrl:'app/shared/directives/tools/app_error_message.html'
    };
});

The following code would display the next image if the signinFormController controller captures an error during user authentication:

1
<app-error-message error-message="signinFormController.authError"></app-error-message>

Technical debt

Troubles during unit testing

We use Karma to test most of the JavaScript code written for our frontend. But testing AngularJS directives is not as straightforward as testing controllers or services. The official documentation is a good starting point.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
describe('App tools directives', function () {

    var $compile,
        $rootScope,
        $templateCache;

    beforeEach(module('app'));
    beforeEach(module('app.templates'));

    beforeEach(inject(function(_$compile_, _$rootScope_, $httpBackend){
        $compile = _$compile_;
        $rootScope = _$rootScope_;
        $httpBackend.whenGET('assets/l10n/en.json').respond('');
    }));

    it('appErrorMessage should display message', function() {
        $rootScope.msg = 'abcd1234';
        var element = $compile("<div><span app-error-message error-message=\"msg\"></span></div>")($rootScope);
        $rootScope.$digest();
        expect(element.html()).toContain("abcd1234");
    });
});

Angular translate

The first trouble is that we use angular-translate (We currently only support English but everything is ready to translate our GUI in other languages).

If like me you get the error Error: Unexpected request: GET assets/l10n/en.json while running Karma, you may inject $httpBackend before each test and let it expect a GET request to the translation file:

1
$httpBackend.whenGET('assets/l10n/en.json').respond('');

template URL

The next trouble is dealing with template URLs. My directive uses an external template referenced by the line templateUrl:‘app/shared/directives/tools/app_error_message.html’.

I got the error message Error: Unexpected request: GET app/shared/directives/tools/app_error_message.html, but would not want to configure my $httpBackend to return the template for each directive. The solution is to install and configure the karma-ng-html2js-preprocessor plugin:

1
npm install karma-ng-html2js-preprocessor --save-dev

Once installed, there should be a reference to this preprocessor in your package.json file: “karma-ng-html2js-preprocessor”: “~0.1.2”.

You also need to configure your karma.conf.js file:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
module.exports = function (config) {
    config.set({

        basePath: './',

        files: [...],

        plugins: [
            'karma-chrome-launcher',
            ...
            'karma-ng-html2js-preprocessor'
        ],

        preprocessors: {
            'app/**/*.html': ["ng-html2js"]
        },

        ngHtml2JsPreprocessor: {
            // the name of the Angular module to create
            moduleName: "app.templates"
        }
    });
};

And load the module app.templates in your directive test: beforeEach(module(‘app.templates’));

ng-include

Ng-include is a basic directive that allows you to include fetch, compile and include an external HTML fragment. The last trouble I faced was to deal with testing directives that use it. You have to declare an included content in $templateCache to efficiently test it:

1
2
3
4
$templateCache.put('include.html', '<div>Library</div>');
var element = $compile("<div><span ng-include=\"'include.html'\"></span></div>")($rootScope);
$rootScope.$digest();
expect(element.html()).toContain("<span");

Next step

I only had time to test a tiny part of the directives we use in OctoPerf. I would be proud to reach at least 95% test coverage by testing all of them.

Unit testing AngularJS directives
Tags:
Share:

Be the first to comment

Thank you

Your comment has been submitted and will be published once it has been approved.

OK

OOPS!

Your post has failed. Please return to the page and try again. Thank You!

OK

Want to become a super load tester?
OctoPerf Superman