How To Level Up Your Angular Unit Testing Game (3/3)
Tags: testing, angular, tips, webdev
Published: 2019-01-28 18:13:40

---
title: How To Level Up Your Angular Unit Testing Game (3/3)
published: true
description: Tips to run Angular unit tests and customize your test tooling
tags: testing,angular,tips,webdev
cover_image: https://github.com/alisaduncan/tutorial-angular-httpclient/blob/test-configuration/post-images/level-up-mario.jpg?raw=true
series: level-up-angular-unit-testing 
---

_This is the third post in the series about Angular unit testing. If you are unfamiliar with using Angular CLI or making changes to Karma configuration, please read the first two posts in the series._

In the last post, we learned how to edit _karma.conf.js_ to add reporters and set coverage thresholds. In this post, we'll create shared testing files and create our own parameter to pass in while testing to limit the files that the test runs. As projects become larger, running all the tests can take a while, so this is an easy way to target tests. It's time to level up your testing game and get pumped up to unit test!

![](https://media.giphy.com/media/bdpIDWZFkI67S/giphy.gif)


> Angular CLI doesn't expose all the configuration options Karma has, so if targeting tests isn't your thing, maybe there's other ways you tailored Angular CLI tests. I'm showing this as an example of how to work around limitations in Angular CLI, not a suggestion on correct ways to configure Karma. I'd love to hear all about the ways you've tailored Angular CLI unit tests in the comments!


# Follow Along

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

{% link https://dev.to/alisaduncan/intercepting-http-requests---using-and-testing-angulars-httpclient %}

You can clone the [tutorial-angular-httpclient](https://github.com/alisaduncan/tutorial-angular-httpclient) and run tests too. All the level ups discussed in the posts are found in "test-configuration" branch.


# Create Shared Testing Files
As your app grows, you may find that you're creating the same fake more than once. We can DRY things up a bit by creating testing files. 

Create your fake and place it in a testing folder. For ease, I named my folder "testing" and made it a child of "src" directory. If you're following the repo, I created a _fake-user.service.ts_.

In _$/src/tsconfig.app.json_, add the "testing" folder in the "exclude" array. It needs to match a glob pattern. So in my case I have

```json
"exclude": [
      "test.ts",
      "**/testing/*",
      "**/*.spec.ts"
]

``` 

Your spec files can now refer to your shared fake instead of having to define it in the file!

I also like to add a shortcut path to the "testing" folder so that I don't have the world's worst import statement. In your _$/tsconfig.json_, add a "paths" property after "libs" to add a shortcut to your testing folder like this

```json
"lib": [
      "es2018",
      "dom"
],
"paths": {
      "@testing/*": ["src/testing/*"]
}
```

In the spec files, I can import my fake users service by using
```ts
import { FakeUserService } from '@testing/fake-user.service';
```

![](https://media.giphy.com/media/24i3lhBG6zhVC/giphy.gif)


# Target Tests Using Jasmine Constructs
Before diving in too deep into custom code to target tests, let's start by using Jasmine's native constructs to force only specific tests to run. You can force tests by prepending 'f' to the test suite or test.

So change `describe` to `fdescribe` or `it` to `fit`. Then only the tests with the 'f' prefix will run. Your test output still includes all tests (it skips the tests not prefixed). 

You'll want to remember to remove the 'f' prefixes. Luckily you can add a lint rule to remember to remove the forced tests.

In your _$/tslint.json_, add a new rule called "ban". We'll add "fit" and "fdescribe" like this:

```json
"ban": [
      true,
      ["fit"],
      ["fdescribe"]
]

```

![](https://media.giphy.com/media/nj0k2dDx4YAgg/giphy.gif)

But, using the native construct has its disadvantages. You have to edit the code and remember to remove the 'f' prefix. Also, when you have a lot of tests, it can still take time to complete the test run because it still evaluates whether to run each test. Then your output is full of test cases that were skipped. Boooo... Luckily, we can work around this.

## Target Tests By Limiting Files
A way to run only the file you want is to edit the regex Angular CLI uses to find all _*.spec_ files. We can hardcode the test files we want to run.

In `$/src/test.ts`, find the line

```ts
const context = require.context('./', true, /\.spec\.ts$/);
```

Edit the regex to target the test files you want, such as

```ts
const context = require.context('./', true, /\.service\.spec\.ts$/);
```

to run all the _service.spec.ts_ files. 

![](https://media.giphy.com/media/3o7527pa7qs9kCG78A/giphy.gif)

This method certainly gets the job done. It speeds up the test run and only shows the service files in the console output, but now we have no lint safety to remember to revert changes we made. There's another option-- to create a custom parameter.


# Create Custom Parameter
I'd like to pass in the files for the test run. We can add custom code to enable us to do this. Sometimes it's fun to take the covers off a library and dig in to the code ourselves. Taking things apart and putting it back together in way that works better for our needs helps us learn and makes us better engineers and craftsmen.

> Warning: Angular v7 purposefully limits parameters passed in to Karma so this solution only works on Angular v6. Follow the instructions with at the risk of it breaking when you upgrade, but maybe it will give you some ideas of the sorts of things you can try!


## Handle CommandLine Argument
In the _karma.config.js_, we need to parse the commandline arguments to grab the test files. I'm going to call this parameter `specs`. 

At the top of _karma.config.js_ define a variable to hold the list of test files and parse through the command line arguments to grab the list of test files. My code looks like this (yep, it's a little ugly-- it can certainly be cleaned up):

```js

let specs = [];
if (process.argv.indexOf('specs') !== -1) {
  specs = process.argv.slice(process.argv.indexOf('specs') + 1);
  specs = specs.slice(0, specs.findIndex(el => el.includes('--')) === -1 ? specs.length : specs.findIndex(el => el.includes('--')));
}
```

Then add `specs` in the `args` array:

```js
client: {
      args: [specs],
      clearContext: false // leave Jasmine Spec Runner output visible in browser
},
```

That's it for _karma.conf.js_.


## Create Regex Of Files To Test
So now we can pass in a list of files to limit tests to. We do this in the _test.ts_ file.

We need to get a hold of the `karma` context, so declare it by adding 

```ts
declare const __karma__: any;
```
as the first line after the import statements.

Then we need to build out the file list regex. I grabbed the list of files from the karma context and added files to a variable. We still want to run all tests if we don't limit the files.

```ts
const args = __karma__.config.args[0];
let fileRegex: RegExp = /.*/;
if ( args && args.length) {
  const files = args.join('|');
  fileRegex = new RegExp(`(${files})\.spec\.ts$`);
}
```

We want to use `fileRegex` instead of the one Angular defined, so the line where we hard coded service tests previously changes to

```ts
const specFiles = context.keys().filter(path => fileRegex.test(path));
```

Lastly, we want to load the modules properly to take in to account `specFiles`

```ts
specFiles.map(context);
```

To see all the code in context, here's a gist of what we just did.

{% gist https://gist.github.com/alisaduncan/ab9f8c418683d59ee3f80c8e4cd16278 %}


Now we can start using this new parameter.

Here's the kicker. Angular CLI won't recognize the `specs` parameter, but there's a trick to get Angular CLI to accept it by including the the app name. Create a npm script called `test:specs`

```json
"test:specs": "ng test tutorial-angular-httpclient specs"
```


Run the tests by passing the list of files through npm. You can add other commands as you like. For example

```bash
npm run test:specs -- user.service auth.interceptor --watch false --browsers ChromeHeadless
```

Hey hey! Finally only running the test files you want to target!

![](https://i.pinimg.com/236x/ee/18/c5/ee18c5b18dbd9c3cc5c67980809af204.jpg)


_I'd love to hear how you level up your unit testing!_


![](https://media.giphy.com/media/l1KtXmfi3EnjM5zpK/giphy.gif)