In a previous post we met SonarTS, the first official static code analyzer for TypeScript by SonarSource. That post focused on getting SonarQube and TypeScript up and running. Now we are ready to extend on that scenario by adding code coverage metrics to our reports.
Couple of things we need to get this to work:
- A unit test, of course, covering some (or all) of our custom code;
- A generated coverage report;
- Configure our scripts to upload the report to our SonarQube installation.
So let’s start with a unit test. There are a few ways to achieve this (and a couple of frameworks that can help us), but I will not cover any options in detail here. In this case I will work with Karma as a test runner, Jasmine as the test framework, Google Chrome as the browser and write the actual unit test in TypeScript too.
Jasmine: https://jasmine.github.io
Jasmine is our test framework. It allows us to describe our tests and run on any JavaScript enabled platform. Simply add it to our project using npm:
npm install jasmine-core --save-dev
And since we are going to write our tests using TypeScript, we need to install the correct typings:
npm install @types/jasmine --save-dev
Karma: https://karma-runner.github.io
Karma is our test runner and responsible for executing our code and unit tests using real browsers.
npm install karma --save-dev npm install karma-cli --save-dev npm install karma-jasmine --save-dev npm install karma-chrome-launcher --save-dev
Karma-TypeScript: https://www.npmjs.com/package/karma-typescript
This enables us to write unit tests in Typescript with full type checking, seamlessly without extra build steps or scripts. As a bonus we get remapped test coverage with karma-coverage and Istanbul.
npm install karma-typescript --save-dev
Creating the test
We have a single component based on a “Hello World” service.
import { IHelloService } from "./hello.service.interface"; export class HelloComponent { constructor(private helloService: IHelloService) {} public sayHello(): string { return this.helloService.sayHello(); } }
And the unit test could look something like this:
import { HelloComponent } from "./hello.component"; import { IHelloService } from "./hello.service.interface"; class MockHelloService implements IHelloService { public sayHello(): string { return "Hello world!"; } } describe("HelloComponent", () => { it("should say 'Hello world!'", () => { const mockHelloService = new MockHelloService(); const helloComponent = new HelloComponent(mockHelloService); expect(helloComponent.sayHello()).toEqual("Hello world!"); }); });
Running the unit test
To run the tests we need to configure Karma with some options:
module.exports = function(config) { config.set({ frameworks: ["jasmine", "karma-typescript"], files: [ { pattern: "src/**/*.ts" } ], preprocessors: { "**/*.ts": ["karma-typescript"] }, karmaTypescriptConfig: { reports: { "lcovonly": { "directory": "coverage", "filename": "lcov.info", "subdirectory": "lcov" } } }, reporters: ["dots","karma-typescript"], browsers: ["Chrome"], singleRun: true }); };
The important parts are:
- Frameworks: both jasmine and karma-typescript. This supports our unit tests written in TypeScript;
- karmaTypeScriptConfig: here we choose lcov as the default report format. You can enable other reporters too, but this is the format SonarQube understands by default. Please see this link for more reporters: https://www.npmjs.com/package/karma-typescript
- reporters: “dots” renders “.” as a visual queue when tests are running. “karma-typescript” is the reporter that actually produces the required output format.
- browsers: Chrome, but you can pick other browsers if you like. It needs to be installed, of course.
Please note: To be able to remap the code to the coverage reports, source maps must be enabled (i.e. by setting the property through tsconfig.json).
When you now run the test, it should compile and automate a Chrome browser to execute the unit test. On completion it should generate a lcov.info report in the coverage folder.
Upload the results
If you already setup SonarQube, you just need to make sure you have the LCOV location configured. If not, see this post or take a look at the sample project.
To specify the report location, add this line to the sonar-project.properties file:
sonar.typescript.lcov.reportPaths=coverage/lcov/lcov.info
When you now run the SonarQube scanner, it should analyze the source code and upload the results + the coverage file to SonarQube.
You can find the source code here: https://github.com/yuriburger/sonar-ts-demo
/Y.