The Angular DevOps Series: CT and Code Coverage with TeamCity

The Angular DevOps Series: CT and Code Coverage with TeamCity

There is more to Istanbul than Constantinople

This article describes how to set up Continuous Testing and Code Coverage for Angular Applications. Then it continues to show how to integrate this into TeamCity. We will configure TeamCity so that upon pushing a new commit for our Angular application it will:

  • Run the Unit Tests

  • Create a Code Coverage report

  • Build the application for production

Even if you are not using TeamCity you can apply the parts up until the integration and instead, use the tool of your choice.

Prerequisites: Basic knowledge of unit testing in Angular.

The Angular DevOps Series

This is the fourth post in the Angular DevOps Series. Make sure you take a look at the other posts in the series by Tim Deschryver, Todd Palmer and Andrew Evans:

Definitions

Continuous Integration (CI) is the practice of merging all developer working copies to the shared mainline several times a day.

In CI each integration is verified by an automated build to detect integration errors as quickly as possible. Having a reliable CI process builds trust in the system.

Continuous testing (CT) is the process of executing automated tests as part of the software delivery pipeline. It’s easy to forget to run tests but CI servers let you set up your project repository so that your tests run before every build.

CT is key to unlocking the agile culture change because it weaves testing activities into every part of the software design, development, and deployment processes.

Code coverage is a measure of how much code is covered by the tests.

Code coverage shows you what code was covered during the automated tests run on the build server. You can then use this information to direct focus on areas that are not sufficiently tested to drive up code quality.

TeamCity is a Java-based build management and CI server from JetBrains. TeamCity is commercial software and licensed under a proprietary license. A Freemium license for up to 100 build configurations and 3 free Build Agent licenses is available. Open Source projects can request a free license.

Tests

Let’s start by creating a few unit tests. To make it simple I will use the tests that are created for app.component.ts when we create a new project with the Angular CLI.

import { TestBed, async } from '@angular/core/testing';
import { AppComponent } from './app.component';

describe('AppComponent', () => {
  beforeEach(async(() => {
    TestBed.configureTestingModule({
      declarations: [
        AppComponent
      ],
    }).compileComponents();
  }));

  it('should create the app', () => {
    const fixture = TestBed.createComponent(AppComponent);
    const app = fixture.debugElement.componentInstance;
    expect(app).toBeTruthy();
  });

  it(`should have as title 'unittest'`, () => {
    const fixture = TestBed.createComponent(AppComponent);
    const app = fixture.debugElement.componentInstance;
    expect(app.title).toEqual('unittest');
  });

  it('should render title in a h1 tag', () => {
    const fixture = TestBed.createComponent(AppComponent);
    fixture.detectChanges();
    const compiled = fixture.debugElement.nativeElement;
    expect(compiled.querySelector('h1').textContent).toContain('Welcome to unittest!');
  });
});

When we run this with npm t we see that all three tests pass.

Code Coverage

Turning on code coverage means that you will be able to display what code your tests are exercising, and perhaps more importantly, what code isn’t being exercised. You can drill into the metrics to get a better understanding of it.

If You Can’t Measure It, You Can’t Improve It.

Let’s start by running the command for creating the code coverage report.

ng t --code-coverage

We get a Coverage summary in the console. And there is now a new “coverage” folder in the project root directory. This holds the coverage report in HTML. If we click the index.html we see the report.

And if we drill down to the app.component.ts file we see what lines of code are getting exercised by the unit tests.

Istanbul

It is a tool called Istanbul that creates the code coverage reports. It instruments the JavaScript code with line counters so that you can track how well the unit-tests exercise your code base. It has HTML and LCOV reporting.

It works well with most JavaScript testing frameworks and provides simple integrations to Jasmine and TeamCity that we are using. The important part is that it creates HTML reports which in turn can be read by TeamCity.

We now have generated a code coverage report. The next step is to integrate it into TeamCity.

Headless browser

Before we can start integrating with the CI system we need to address something. Locally it works fine using a normal browser for the unit tests. But on the build server, we can’t actually launch a browser GUI. The solution to this is called a headless browser.

A headless browser is a web browser without a graphical user interface.

We can use Headless Chrome by adding a flag when we run our tests.

ng t --browsers=ChromeHeadless

If we run this command we don’t have the browser popping up anymore and we can see that we instead are using the headless browser.

TeamCity

Turning on CT and code coverage in TeamCity means that you will be able to display these metrics after the build is complete. You will also be able to see the progress in the number of tests and code coverage over time. Another bonus is that you can stop the build if there is an error in the tests. This will encourage people to keep the tests working.

If you are reading this blog you probably already are using TeamCity as your build management tool. So the interesting part is how we can integrate with it.

Unit Tests

The first thing we need is to have a format that TeamCity can read. To get this we can install an npm package called karma-teamcity-reporter.

npm i karma-teamcity-reporter

We then need to add it to plugins and reporters in karma.conf.js.

And now when we run our tests again we can see that the format is for TeamCity.

ng t --reporters=teamcity --browsers=ChromeHeadless

If we run this script in TeamCity the unit tests will be run. Before we had the unit tests running it only said “Success” but now we can see that we have passing tests in our build.

Like this blog post? Share it on Twitter! 🐦

Code Coverage

The next step is to get the coverage report in. For that, we add the code-coverage flag again. We also need to add coverage-istanbul to the reporters since we are overriding the default reporters list.

ng t --no-watch
     --code-coverage 
     --reporters=teamcity,coverage-istanbul 
     --browsers=ChromeHeadless

code-coverage option only works if the reporters' list includes a coverage tool. If no coverage reporter is present, it fails silently.

reporters switch replaces the default reporters list, so coverage-istanbul has to be explicitly re-added.

coverage-istanbul reporter does not produce a report on its own; the — code-coverage switch is still required.

Move testing packages to dependencies to be able to run CT on the build server.

Now we have the script ready that runs our tests and creates the code coverage report. Next, let’s configure TeamCity.

Artifact

To get the Istanbul coverage HTML report into TeamCity we can publish a TeamCity artifact. Most of the tools produce coverage reports in HTML format. You can publish it as an artifact and configure a report tab to show it in TeamCity.

If an artifact is published in the root artifact directory and its name is coverage.zip and there is an index.html file in it, the report tab will be shown automatically.

The easiest way to get TeamCity to recognize your coverage report is to output a build artifact that contains that nice HTML coverage report. Then you add the path to your zipped report artifact under:

Settings -> General Settings -> Artifact Paths:

coverage/** => coverage.zip

The path must be relative to the root of the build artifact directory.

When we click into our build we see a nice overview of our tests and there should now exist a Code Coverage tab.

From the code coverage tab, we get the same report we had locally with nice navigation of our file structure.

Enforcement

If your team decides on a set minimum amount to be unit tested, you can enforce this minimum. For example, suppose you want the code base to have a minimum of 80% code coverage. To enable this, open karma.conf.js, and add the following in the coverageIstanbulReporter: key.

coverageIstanbulReporter: {   
  reports: [ 'html', 'lcovonly' ],   
  fixWebpackSourcePaths: true,   
    thresholds: {     
    statements: 80,     
    lines: 80,     
    branches: 80,     
    functions: 80   
  } 
}

Cover all files

If you don’t have a spec file for every TypeScript file then your coverage report will be too optimistic. To get a more truthful percentage I added a test file app.module.spec.ts where I import app.module. This means all the files in your application will be exercised and checked if they have been tested. With lazily loaded modules you will probably have to make an explicit import for them as well.

Adding this to a project will give you a more truthful picture of your coverage.

BeforeBefore

AfterAfter

Script to config

As we can see the script has become quite long by now.

ng t --no-watch
     --code-coverage 
     --reporters=teamcity,coverage-istanbul 
     --browsers=ChromeHeadless

Architect is the tool that the CLI uses to perform complex tasks, such as compilation and test running, according to provided configurations.

Let’s add our alternative configuration under the test target in the architect section and call it ci-release.

"configurations": {
  "ci-release": { 
    "watch": false,      
    "codeCoverage": true,
    "reporters": [
      "teamcity",
      "coverage-istanbul"
    ],        
    "browsers": [
      "ChromeHeadless"
    ]
  }
}

And now the long script is only a short call for the configuration.

ng t --configuration=ci-release

Much better! Thanks to Lars Gyrup Brink Nielsen for this tip.

Conclusion

Now we have scripts ready for production that do all the things promised in the beginning:

  • Run the Unit Tests

  • Create a Code Coverage report

  • Build the application for production

"build:release": "npm run test:release && ng build --prod",
"prebuild:release": "npm ci --production --no-optional",
"test:release": "ng t --configuration=ci-release

Special thanks to Wincent Papousek who has been my partner in crime for getting our tests integrated with TeamCity. 🙏

Call to Action

I always enjoy feedback so please 👏, 📝 and tweet 🐦. Follow me on Twitter and Medium for blog updates.

Resources

Did you find this article valuable?

Support Michael Karén by becoming a sponsor. Any amount is appreciated!