Angular 1 TDD – Creating a component

In this blogpost, I will explain how to create an Angular component using TDD and show how it affects the code coverage report.

Prerequisites

This blogpost assumes the following has been installed on your machine.

  • Visual Studio Code (http://bit.ly/1KvI3PL)
  • Node.js (http://bit.ly/1Wdc3FQ)
  • A project with the following libraries (see this post on how to enable this):
    • Gulp (Task runner)
    • Bower (web package manager)
    • ngMock (AngularJS module relating to unit testing)
    • BardJS (library to avoid certain repetitive testing code)
    • Sinon 1.x or 2.x (Needed by BardJS to mock services)
    • AngularJS (MVW JavaScript framework)
    • Chai (TDD/BDD assertion library)
    • Mocha (JavaScript test framework)
    • Karma (JavaScript test runner by the AngularJS team)

 

Result

The following will be in place at the end of this post:

  • Component unit tests
  • A component
  • A code coverage report

 

Creating the unit test

Let’s start by writing a unit test for our component. The component will display the game character data on the screen.

Let’s create the test:

describe('game character component', function () {
    var element, scope;
    var gameCharacterStub =
        {
            "id": 9460,
            "name": "Janus",
            "created_at": 1472328002062,
            "updated_at": 1472328002125,
            "slug": "janus",
            "url": "https://www.igdb.com/characters/janus",
            "people": [
                80788
            ],
            "games": [
                9498
            ]
        };

    beforeEach(function () {
        module('gameApp');
        module('partials');
        bard.inject(this, '$compile', '$rootScope');
    });

    beforeEach(function () {
        scope = $rootScope.$new();
        scope.model = gameCharacterStub;
        element = angular.element('<game-character' +
            ' data-model="model"' +
            '>' +
            '</game-character>'
        );
        //Compile a piece of HTML containing the component
        $compile(element)(scope);
        //Fire all the watches, so the scope will be evaluated
        scope.$digest();
    });

    it('should render game character data', function () {
        var nameElement = angular.element(element[0].querySelector('div.name'));
        expect(nameElement.text()).to.equal('Janus');
    });
});

We are planning to create a simple component that contains a div and returns the name of the game character. The first beforeEach has an added module ‘partials’. We will come back to this later in the templateUrl section. The second beforeEach contains the code to render the component.

  1. The scope is initialized and populated.
  2. The HTML is defined using the angular.element syntax
  3. The HTML is built using the $compile statement
  4. The scope is evaluated using the $digest statement. This means that any HTML fields referring to the scope will be populated with the objects in the scope.

The unit test uses the querySelector syntax to select the div with the class name ‘name’. element.find, which uses JQLite, can be used only when we want to search an element by id. See https://docs.angularjs.org/api/ng/function/angular.element for a full list of supported functionality.

After we run the test, the following error occurs:

This is the result of the component being nonexistent.

 

Creating the component

Let’s create the component:

(function() {
    'use strict';

    angular
        .module('gameApp')
        .component('gameCharacter', {
            template: '<div class="name">{{$ctrl.model.name}}</div>',
            controller: 'GameCharacterController',
            bindings: {
                model: '=',
            },
        });
})();

A simple component is created containing a div that shows the value of the bound model object (which is filled when the scope.$digest in our unit tests is executed).

When we run Karma, the component unit test passes.

 

Using templateUrl

Ideally, when the HTML in a component becomes more complex, you would want to use the templateUrl property in the component:

(function() {
    'use strict';

    angular
        .module('gameApp')
        .component('gameCharacter', {
            templateUrl: 'src/components/gameCharacter.component.html',
            controller: 'GameCharacterController',
            bindings: {
                model: '='
            }
        });
})();

With the HTML file containing the following:

<div class="name">{{$ctrl.model.name}}</div>

If Karma is run, it fails with an error too long to show here.
Karma needs additional configuration to be able to work with the templateUrl property.

First of all, we need to install the Karma ng2html preprocessor:

Next let’s open the karma.conf.js file and make sure we add HTML files to the files array. This ensures our newly created gameCharacter.component.html file is loaded through Karma.

// list of files / patterns to load in the browser
        files: [
            'bower_components/angular/angular.min.js',
            'bower_components/angular-mocks/angular-mocks.js',
            'bower_components/bardjs/dist/bard.js',
            'src/**/*.module.js',
            'src/**/*.js',
            'test/**/*.spec.js',
            'src/**/*.html'
        ],

Next we add the ng-html2js preprocessor to our HTML files.

// preprocess matching files before serving them to the browser
        // available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor
        preprocessors: {
            'src/**/*.js': ['coverage'],
            'src/**/*.html': ['ng-html2js']
        },

We’ll also define a moduleName to load the templates in:

ngHtml2JsPreprocessor: {
            moduleName: 'partials'
        },

And finally, we’ll load the partials module in the unit test before the BardJS inject statement:

beforeEach(function () {
        module('gameApp');
        module('partials');
        bard.inject(this, '$compile', '$rootScope');
    });

In short, this preprocessor converts all the HTML files into angular modules and puts these in the template cache.

More information can be found at: (https://github.com/karma-runner/karma-ng-html2js-preprocessor)

When karma is run, the test passes:

 

Code coverage

The code coverage report looks as follows:

Even if the unit test is not written, it will result in 100% code coverage. This is because the JS file is loaded into the module and all code is run. In the next post, I will show how to cover code for directives when there is interaction required from the client.

 

Summary

We have created a unit test for a component using the $compile functionality. Next, we created the controller using either the template and templateUrl properties.

In the next post, we will do TDD for an Angular directive.

2 thoughts on “Angular 1 TDD – Creating a component

  1. Thanks for posting it. However, it would be nice call the title: “Angular JS TDD…” for Angular 1.x instead of “Angular TDD” which is for Angular 2.x or Angular 4.x.

Leave a Reply