loading...
Cover image for How To Level Up Your Angular Unit Testing Game (2/3)

How To Level Up Your Angular Unit Testing Game (2/3)

alisaduncan profile image Alisa ใƒป5 min read

This is the second post in the series about Angular unit testing. If you are unfamiliar with using Angular CLI, please read the first post in the series.

In the previous post, we learned how to pass parameters into Angular CLI to run unit tests. In this post, we'll dive into Karma configuration and make the test runner more snazzy by configuring it for our unique needs. It's time to level up your testing game and get pumped up to unit test!

Follow Along

We're using Angular CLI to test the application and using code from a previous post I wrote.

You can clone the tutorial-angular-httpclient and run tests too. All the level ups discussed in the posts are found in "test-configuration" branch.

Karma Configuration

In your Angular app, find $/src/karma.config.js. This is the config file Karma uses to run tests. Let's start editing it!

Reporters

Out of the box, Angular CLI enables 'progress' and 'kjhtml' reporters. In v7 and v5, you can configure reporters via commandline but sadly, that option was removed in v6. We can still make changes via the config file for v6 though!

If you are using Angular v5 or v7, pass in a comma separated list of reporters using the reporters flag.

To change karma.config.js, in the reporters array, remove 'progress' and replace it with 'dots'. Now when you run tests, the output to the console uses the dot reporter instead of the progress reporter.

Add A New Reporter

But our test output could be way better. It's nothing to write home about yet. Let's try adding a reporter.

Add the 'karma-spec-reporter' npm package

npm install karma-spec-reporter --save-dev

Require the reporter in karma.conf.js by adding the require statement to the plugins array

plugins: [
      require('karma-jasmine'),
      require('karma-chrome-launcher'),
      require('karma-jasmine-html-reporter'),
      require('karma-coverage-istanbul-reporter'),
      require('@angular-devkit/build-angular/plugins/karma'),
      require('karma-spec-reporter')
    ],

Add to the reporters array

reporters: ['kjhtml', 'spec']

Now when you run tests, you'll see an output like this

There's all sorts of reporters available for Karma. If you want unit test output available in your CI environment, you can add a reporter for JUnit output and configure Jenkins to consume JUnit reports as an example. If you need some more flair in your life, add the nyan cat progress reporter.

Code Coverage

Now that you know how to run tests and set it up to get the output format you like, it's time to measure coverage.

Keep in mind test coverage metrics can't determine if an application is working as intended or if the tests you have are "good" by testing the important things.

Angular CLI automatically adds and configures Istanbul as a coverage reporter. In karma.conf.js there's a section for coverageIstanbulReporter and it's already set up to generate reports. Prior to v7 of Angular, it only had 'html' and 'lcov' enabled out of the box. Add 'text-summary' to the array if it's not already there.

Run test with the coverage flag enabled. In v6, use --code-coverage.

ng test --watch false --browsers ChromeHeadless --codeCoverage

Now you have nice output in the console for coverage summary.

Running coverage with 'html' report creates a visual overview of coverage in html format. You can view coverage results for the entire app broken down per file.

And step into coverage report for a single file and see the number of times each line of code was executed.

Add native report formats such as 'text', 'json', 'Cobertura', 'TeamCity', or use the 'lcov' report as part of your CI process so you can show test coverage trend lines per build.

Maintaining Thresholds

If you want to ensure a certain level of coverage, you can configure coverageIstanbulReporter object in karma.conf.js to add thresholds.

To the thresholds object, fill in keys for statements, lines, branches, and functions.

coverageIstanbulReporter: {
      dir: require('path').join(__dirname, '../coverage'),
      reports: ['html', 'lcovonly', 'text-summary'],
      fixWebpackSourcePaths: true,
      thresholds: {
            statements: 80,
            lines: 80,
            branches: 80,
            functions: 80
      }
},

Now when you run tests with code coverage, your tests will complete with failure if your project doesn't meet the thresholds of coverage.

Setting Up angular.json For Defaults

Sure, we can create npm scripts to wrap all the parameters we send in while running unit tests, but there's cases where we always want a certain configuration set. An example might be code coverage. Despite watch or browsers configurations, we always want to run coverage. We can set that up in angular.json.

In $/angular.json file, find the configuration for "test". It has an "options" property. Add a new property for "codeCoverage" and set the value to true.

You can set more properties than just coverage. All parameters that you pass in to Angular CLI can be configured. Check out the schema for angular.json for more details.

Add A Git Hook

Want to make sure every push to the repo has passing unit tests and adequate coverage? Add the npm package 'husky' and add the pre-push step that runs unit tests following their instructions.

Now people can't push code into the repo without passing unit tests that meet code coverage. Applied to all branches, this requirement might make it difficult for teams to collaborate so you might want to target only master branch following a method posted in this GitHub issue.

Apply husky git hook to specific branch only #186

Is there a way to configure husky to apply a specific git hook, i.e. prepush ONLY to a specific branch i.e. master?

I know I can modify the hooks in the package, but that makes it more complicated because husky installs its own hooks, then I have to go overwrite it with a modified version using some scripts.

Mainly the purpose is to only run e2e or unit tests before pushing to master, but not every time I push to dev (which gets annoying waiting and seeing the browsers popup every time).

In the next post, we'll learn about ways to target tests to run and dive a little deeper into custom solutions.

Posted on by:

alisaduncan profile

Alisa

@alisaduncan

I'm a developer who's also an automated testing enthusiast, a team organizer, and a diagram drawer. I co-organize an Angular meetup and volunteer for a women in tech group. I'm a nerd and I like wine.

Discussion

markdown guide
 

Great article! Thanks for sharing these, they're very helpful.