Angular11 With Jest : Configuration

Angular11 With Jest : Configuration

# 1: Making Jest work locally with Angular11

Introduction

Our company was moving in full gear towards test automation. In order to support this initiative, it was decided that all applications should implement unit tests and get them to run on the pipeline level. I was tasked with figuring out the way to go with our JavaScript front-end unit testing. Long story short, it's either to go with Jest or Jasmine. Most of our older applications run on angular 11, therefore it is only appropriate to begin the effort with that particular version.

Pre-Requisite

  • Angular version: 11.1.0

  • @angular/cli: 11.1.0

  • nodeJS version: 14.15.1

  • npm version: 6.14.8

  • CI/CD: Azure DevOps

  • Jest version: 26.5.2

  • NVM version: 1.1.10

Why Jest?

According to the survey results of the State of JavaScript 2020, Jest scored highest in awareness and usage, 2nd in interest, and fourth in retention. This suggests that Jest is a popular testing framework, and there is a huge community behind this library.

So I decided to try it out with Jest first. The plan is to configure our existing angular11 with jest, get it to run locally, and then configure our Azure DevOps pipeline to run the test, and publish the result on the dashboard.

Configuration That Works Both Locally and On The Pipeline!

It was quite a challenge finding the right guidance on the internet for the right configuration that works both locally and on the Azure pipeline. The following steps were originally written by Marcin Milewicz in his Angular Testing Series: How to add Jest to Angular project smoothly? article which is based on Angular10 and was the closest version to what I am currently working on

Creating an Angular11 application

I am using node v14.15.1 and npm v6.14.8 in my environment, so if things don't work for you, consider installing NVM to install that specific node version and switch it.

Install angular/cli v11.1.0 globally

Check your current list of globally installed packages

npm list -g --depth 0

uninstall your current angular/cli if you have existing angular/cli installed and install version 11.1.0 globally. This will ensure you are scaffolding and running with angular/core and its dependencies on version 11.1.0

npm install -g @angular/cli@11.1.1

Create angular11 app

ng new angular11-jest

now cd into the angular folder, in this example angular11-jest, and open it in VS Code. Let's take a quick look at the package.json. If you are using the right cli version, you should see the below dependencies.

Configuring Jest

Installing specific Jest, Jest-preset-angular, and @type/jest version

Simply running npm install jest and the rest of the packages above was causing issues for me first locally as it was unable to find some functions, and then on the Azure pipeline as it was unable to find a lot of things! So I figured, to refer to Marcin's GitHub Repo and use the exact package version.

npm install jest@26.5.2 --save-dev
npm install jest-preset-angular@8.3.1 --save-dev
npm install @types/jest@26.0.14 --save-dev

Create jest.config.js

Create a jest.config.js at the root level and add the following content

const { pathsToModuleNameMapper } = require('ts-jest/utils');
const { compilerOptions } = require('./tsconfig');

module.exports = {
  preset: 'jest-preset-angular',
  roots: ['<rootDir>/src/'],
  testMatch: ['**/+(*.)+(spec).+(ts)'],
  setupFilesAfterEnv: ['<rootDir>/src/test.ts'],
  collectCoverage: true,
  coverageReporters: ['html'],
  coverageDirectory: 'coverage/my-app',
  moduleNameMapper: pathsToModuleNameMapper(compilerOptions.paths || {}, {
    prefix: '<rootDir>/',
  }),
};

Replace test.ts content

Navigate to src/test.ts and replace the contents with the following

import 'jest-preset-angular';

Object.defineProperty(window, 'CSS', { value: null });
Object.defineProperty(window, 'getComputedStyle', {
  value: () => {
    return {
      display: 'none',
      appearance: ['-webkit-appearance'],
    };
  },
});

Object.defineProperty(document, 'doctype', {
  value: '<!DOCTYPE html>',
});
Object.defineProperty(document.body.style, 'transform', {
  value: () => {
    return {
      enumerable: true,
      configurable: true,
    };
  },
});

Modify tsconfig.spec.json CompilerOptions

Change the types, and add esModuleInterop and emitDecoratorMetadata options.

"compilerOptions": {
    "outDir": "./out-tsc/spec",
    "types": ["jest", "node"],
    "esModuleInterop": true,
    "emitDecoratorMetadata": true
  },

Delete commented lines in tsconfig.json

Make sure to delete the default commented line on top of tsconfig.json as it will cause a JSON read error when running the test.

Modify package.json test command

Finally in package.json, in the script options, change "test":"ng test" to jest. If you do not have the Jest package installed globally, running the jest command will result in the error jest command being unknown. It is a recommended approach to run jest locally by running it via the npm script.

"test": "jest",

Remove all Jasmine & Karma dependencies

We won't be running Jasmine & Karma anymore. Let's go ahead and remove all of their existence from the app.

npm uninstall karma karma-chrome-launcher karma-coverage-istanbul-reporter karma-jasmine karma-jasmine-html-reporter

remove all the test section in angular.json

"test": {
  "builder": "@angular-devkit/build-angular:karma",
  "options": {
    "main": "src/test.ts",
    "polyfills": "src/polyfills.ts",
    "tsConfig": "tsconfig.spec.json",
    "karmaConfig": "karma.conf.js",
    "assets": [
      "src/favicon.ico",
      "src/assets"
    ],
    "styles": [
      "src/styles.scss"
    ],
    "scripts": []
  }
}, // REMOVE ALL "test" section

Remove karma.conf.js file

Test run

npm run test

Run the command and if you followed all the steps, you should see something like below!

Congratulations! You manage to make jest run with your angular 11!

Configuring on existing Angular11 app

As I mentioned above, I am working on configuring Jest with our existing angular11 app. Our components were generated using the CLI which means the spec.ts classes were also generated for each component. Running the test resulted in hundreds of failing test suites! That was quite discouraging to see.

Start from square zero

Even with jest-angular-preset automatically making our Jasmine unit test testable with jest, I prefer to write them in a 'jesty' way. I renamed all my spec files to spec.ignore and began modifying them one by one. To rename all spec.ts files to spec.ignore or some other name, use the following command. Change the extension back to spec.ts once you think it is ready for testing.

for /r %x in (*.spec.ts) do ren "%x" *.ignore

You can now begin by choosing a simple component and modifying its spec.ts class

We don't need the TestBed

The testbed is the no 1 culprit in failing the test in my Azure pipelines. According to Vugar Abdullayev's article, Angular Unit Testing Without Testbed, I decided to ditch the testbed and slowly do component mockings in a Jest way.

To demonstrate using the test project in this article let's modify our app.component.spec.ts as follows

import { AppComponent } from './app.component';

describe('AppComponent', () => {
  let component: AppComponent;

  beforeEach(() => {
    component = new AppComponent();
  });

  it('should create', () => {
    expect(component).toBeTruthy();
  });

  it(`should have as title 'angular11-jest'`, () => {
    expect(component.title).toEqual('angular11-jest');
  });
});

go ahead and run the test again and you should see the following output

Conclusion

As you can see, it's quite a setup to run a simple unit test. In Angular16 however, it is much neater but we are not always going to be working on the latest technology aren't we? In the next article, we are going to build an Azure pipeline that will run the jest unit test, and publish the result to the dashboard.

Follow this series and stay tuned!

Did you find this article valuable?

Support Moruling James by becoming a sponsor. Any amount is appreciated!