This article gives an overview on how to execute and write unit tests for your TypeScript code.

Running tests

Unit tests are executed automatically on the build server. After you push your local changes to a remote branch, TeamCity runs the tests. More details can be found in Karma Unit Tests build.

To run unit tests locally, open a terminal window, change to censhare-Client5/web and run the following command:

yarn test

Watch mode

The watch mode is useful to cover the code you have just written, or for test driven development (TDD). It gives you immediate feedback on whether your tests pass or fail. In watch mode, your tests are recompiled and evaluated on each file save.

To run tests in watch mode, first you must run the test build in watch mode. Use the following command:

yarn test:build.watch

After the build is done for the first time, open a second terminal window and run the Karma test runner:

yarn karma start --auto-watch

Tests infrastructure

To write and run tests, an assertion framework is required to make assertions on our code. A test runner is required to run tests and report the results. At censhare we use the Jasmine assertion framework and the Karma test runner.

Writing unit tests

In the censhare Client, we use TypeScript with Angular and AngularJS frameworks.

Important

At censhare, all Angular code must be covered by unit tests.

The tests should be written for components, services, directives and pipes. Learn more about how to write unit test for Angular at https://angular.io/guide/testing.

A huge part of our code written with the AngularJS framework. We do not require to cover legacy code with unit tests. However, when you modify or refactor existing code, unit tests are mandatory.

Write unit tests when you fix a bug or develop a new features in AngularJS. Also consider migrating to Angular at that point!. You find a general guide on writing AngularJS unit tests at https://docs.angularjs.org/guide/unit-testing.

Unit test components with external templates

When you test a component, it is possible that the component uses the templateUrl property for loading the respective template. In censhare, the application is served via SystemJS. With this method, the compiler has to load the template before instantiating a component which is an asynchronous operation. For that reason, we recommend to fall back to using the template property in AngularJS. It imports the HTML file with an es6 import statement with a !text suffix. This ensures that your test is running synchronously. See in the following example how this works:

import myTemplate from './my-template.html';
 
export myComponent = {
    // importing template statically instead of using templateUrl
    template: myTemplate
};

For Angular components, you can still use the templateUrl property and the compileComponents() method

The problem will be resolved when we switch to Angular CLI, which pre-processes all templateUrl references via Webpack plugin.

Dealing with dependencies

When you test an AngularJS module, it is possible that you have to instantiate a large tree of modules. The reason is that the tested module explicitly imports its dependent modules which import their dependent modules and so on.

In order to run a test in an isolated environment, create a fake module with no dependencies and mock whatever is needed later. See in the following example how this works:

import { main } from './main.component';
 
beforeEch(() => {
    // create a fake module with no dependencies
    angular.module('fakeModule', [])
        .component('mainComponent', main);
 
    // make an anonymous mock module to execute tests with
    angular.mock.module('fakeModule');
})

Code coverage

Coverage report

In order to track how much logic is actually tested, the code coverage report is generated during the build. To retrieve this information, Karma uses the Istanbul tool via the karma-coverage plugin. During the processing, the source code is automatically modified (instrumented). Additional calls are added here and there without changing the logic. It may make debugging tests difficult. Therefore, we only generate coverage reports for yarn test and yarn test:tc single run commands. Running Karma directly as yarn karma startwith or without --auto-watch paramether does not generate a coverage report. This ensures that the code remains unchanged and easy to debug.

When running yarn test locally, you can open the report on the web/coverage/html/index.html page in your browser.

For the TeamCity build, the report is available on the Code Coverage tab.

Coverage threshold

Ideally, the tests hit all code lines in your module and cover 100%. However, it is common practice a lower percentage as sufficient and assume that the remaining number corresponds to non-critical code. Currently, unit tests at censhare require 80% coverage. Be aware that this number is subject changes!