Demystifying Angular Unit Tests

Infragistics Team / Tuesday, May 08, 2018

Part 1: Setting up your Unit Testing Module Infrastructure

By: Jared Fineman

In the world of development, unit tests have long been viewed as second-class citizens. Developers either fail to find their usefulness or are too intimidated by their unfamiliarity. The issue is further compounded by the fact that when dealing with Angular Unit Tests, there’s a whole other layer of complexity and challenge that arises as well. In this article, I'd like to focus on the second issue: conquering the fear of the unknown surrounding Angular Unit Tests by demystifying their complexity.

To give background and set forth an example, I was working with a client who was interested in testing some custom business logic in one of their components, but the client didn’t quite have a feel for how to go about doing it. The combination of the component and business rules created complex scenarios, which were difficult to test within the context of the existing application. The client suggested creating some type of temporary UI element that could be used to help manipulate the underlying dates the component relied on. While this was a possibility, there had to be a more efficient approach than creating a UI element. This was where I stepped in and suggested Angular Unit Tests.

Upon the suggestion, they were intrigued by the concept of unit tests, and the idea of creating a Unit Testing Module was born. In the following, I’ll walk you through the basic infrastructure needed for an Angular Testing Module, and in the upcoming articles, we’ll go over some real-world test case examples that I presented to my client.

A lot of the basic information needed for setting up an Angular Testing Module can be found in the Angular Team’s documentation here: https://angular.io/guide/testing#setup. So, let’s run through a few prerequisites for setting up a testing module within your Angular application.

Within your package.json file you should have the following settings with variant version numbers:

  "devDependencies": {
    "@angular/cli": "1.6.8",
    "@angular/compiler-cli": "^5.2.5",
    "@types/jasmine": "~2.8.3",
    "jasmine-core": "~2.8.0",
    "jasmine-spec-reporter": "~4.2.1",
    "karma": "~2.0.0",
    "karma-chrome-launcher": "~2.2.0",
    "karma-cli": "^1.0.1",
    "karma-coverage-istanbul-reporter": "^1.2.1",
    "karma-jasmine": "~1.1.0",
    "karma-jasmine-html-reporter": "^0.2.2"

Jasmine is the name of a JavaScript unit testing library and Karma is the name of a test runner, both of which are non-specific to the Angular framework. In truth, we could create our Unit Testing Module using completely different frameworks, but these are the default ones used within most of the Angular Unit Testing documentation, so we’ll run with it. Also, you may have noticed a reference to Angular-cli; this is the library which we’ll ultimately use to run and launch our Angular Unit Tests.

Next, your karma.conf.js file should look similarly to this.

// Karma configuration file, see link for more information
// https://karma-runner.github.io/1.0/config/configuration-file.html

module.exports = function (config) {
  config.set({
    basePath: '',
    frameworks: ['jasmine', '@angular/cli', 'jquery-1.8.3'],
    files: [
      'https://code.jquery.com/jquery-1.11.2.min.js'
    ],
    plugins: [
      require('karma-jasmine'),
      require('karma-chrome-launcher'),
      require('karma-jasmine-html-reporter'),
      require('karma-coverage-istanbul-reporter'),
      require('@angular/cli/plugins/karma'),
      require('karma-jquery')
    ],
    client: {
      clearContext: false // leave Jasmine Spec Runner output visible in browser
    },
    coverageIstanbulReporter: {
      reports: ['html', 'lcovonly'],
      fixWebpackSourcePaths: true
    },
    angularCli: {
      environment: 'dev'
    },
    reporters: ['progress', 'kjhtml'],
    port: 9876,
    colors: true,
    logLevel: config.LOG_INFO,
    autoWatch: true,
    browsers: ['Chrome'],
    singleRun: false
  });
};

If you need any additional external libraries within your unit tests, like jQuery, you can provide that information within the frameworks and files properties.

Your .angular-cli.json file should look similar to the one below. Keep in mind to pay special attention to the paths provided within this file, as they may vary based on your file directory configuration.

{
  "$schema": "./node_modules/@angular/cli/lib/config/schema.json",
  "project": {
    "name": "my-dream-app"
  },
  "apps": [
    {
      "root": "src",
      "outDir": "dist",
      "assets": [
        "assets",
        "favicon.ico"
      ],
      "index": "index.html",
      "main": "main.ts",
      "polyfills": "polyfills.ts",
      "test": "test.ts",
      "tsconfig": "tsconfig.app.json",
      "testTsconfig": "tsconfig.spec.json",
      "prefix": "app",
      "styles": [
        "styles.css"
      ],
      "scripts": [],
      "environmentSource": "environments/environment.ts",
      "environments": {
        "dev": "environments/environment.ts",
        "prod": "environments/environment.prod.ts"
      }
    }
  ],
  "e2e": {
    "protractor": {
      "config": "./protractor.conf.js"
    }
  },
  "lint": [
    {
      "project": "src/tsconfig.app.json",
      "exclude": "**/node_modules/**"
    },
    {
      "project": "src/tsconfig.spec.json",
      "exclude": "**/node_modules/**"
    },
    {
      "project": "e2e/tsconfig.e2e.json",
      "exclude": "**/node_modules/**"
    }
  ],
  "test": {
    "karma": {
      "config": "./karma.conf.js"
    }
  },
  "defaults": {
    "styleExt": "css",
    "component": {}
  }
}

As referenced in the .angular-cli.json file, you’ll need a tsconfig.app.json file as well as a tsconfig.spec.json file. I’m leaving these out for the sake of brevity, but both can be found within the Angular Unit Testing example section. Also, I’ve highlighted a line referencing a test.ts file, because this file is extremely important and we’ll now discuss its key functionality.

The test.ts file contains a script that Karma uses to first initialize Angular’s built-in testing libraries, as well as find any test cases or specs that are written within your application. The file should look similar to the following, however, you may want to customize the script to further fit your system’s particular needs.

// This file is required by karma.conf.js and loads recursively all the .spec and framework files

import 'zone.js/dist/zone-testing';
import { getTestBed } from '@angular/core/testing';
import {
  BrowserDynamicTestingModule,
  platformBrowserDynamicTesting
} from '@angular/platform-browser-dynamic/testing';

declare const require: any;

// First, initialize the Angular testing environment.
getTestBed().initTestEnvironment(
  BrowserDynamicTestingModule,
  platformBrowserDynamicTesting()
);
// Then we find all the tests.
const context = require.context('./', true, /\.spec\.ts$/);
// And load the modules.
context.keys().map(context);

Lastly, to make sure that we’ve set up our Testing Module properly, we want to run ng test from the command-line, in order to begin compiling and running our tests. If the command causes a new window in Chrome to open up displaying Karma’s GUI, then we know we’ve succeeded.

If you like this post, please like it and share it. In addition, if you haven’t checked out Infragistics Ignite UI for Angular, be sure to do so! It has 50+ material-based Angular components to help you code speedy web apps faster.