When starting a new Angular application, the Angular CLI sets up everything you need for unit testing using Karma and Jasmine.
I will show you how to ditch Karma and Jasmine and use Jest as your unit testing framework and runner.
Jest
For more details of Jest, please check Jest Getting Started
We will concentrate on how to setup our workspace for Jest with Angular 10.
Type definitions for Jest
The @types/jest package contains Jest’s type declaration files allowing TypeScript to perform the necessary type checking.
Jest Preset Angular
The jest-preset-angular package is the tool that makes it possible to run our Angular unit tests using Jest. It includes:
ts-jest, a library that allows Jest to transpile our TypeScript code in-memory before running the tests.- Snapshot serializers to enable snapshot testing for our Angular components.
- AST transformers to remove the CSS styles and inline the HTML templates of our components making them usable with JSDOM.
Configure Jest
Let’s say we’ve generated an Angular application called iris-app with the following command:
ng new iris-appInstall the needed dependencies
Note: I am using npm manager to add dependencies if you are using Yarn please change the command accordingly.
npm install jest \ jest-preset-angular \ @types/jest --save-devCreate the jest.config.js file at the root of your project
Create the jest.config.js file 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: ["text", "html"], coverageDirectory: "coverage/iris-app", moduleNameMapper: pathsToModuleNameMapper(compilerOptions.paths || {}, { prefix: "<rootDir>/", }),};The jest.config.js holds Jest’s configuration. with this configuration, Jest will:
- look for
.spec.tsfiles inside our project’ssrcfolder, - read our TypeScript configuration file
tsconfig.jsonfor any TypeScript aliases to make them understandable by Jest, - compile our TypeScript code in-memory before running the tests,
- collect code coverage information and write them down in a folder called
coverage/iris-app.
Update the content of the src/test.ts file
Replace the content of the src/test.ts with the following:
import "jest-preset-angular/setup-jest";
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, }; },});The code above does two things:
- Import the
jest-preset-angularJavaScript module to set up our Angular testing environment. - Mock some properties and functions of the global
windowobject to make sure our tests can run in a JSDOM environment.
Update the content of the tsconfig.spec.json file
Replace the content of the tsconfig.spec.json with the following:
{ "extends": "./tsconfig.json", "compilerOptions": { "outDir": "./out-tsc/spec", "types": ["jest", "node"], "esModuleInterop": true, "emitDecoratorMetadata": true }, "files": ["src/test.ts", "src/polyfills.ts"], "include": ["src/**/*.spec.ts", "src/**/*.d.ts"]}- Register Jet’s type definitions files with the TypeScript compiler.
- Enable the
esModuleInteropoption of the TypeScript compiler otherwise Jest will output a lot of warnings in the console. - Enable the
emitDecoratorMetadataoption of the TypeScript compiler otherwise Angular’s Dependency Injection won’t work with Jest.
Update the test script inside the package.json
Replace the test script inside the package.json with the following:
{ "scripts": { "test": "jest", "test:cov": "jest --coverage" }}Run the tests
We can now run our unit tests using the following command from the root of our project.
npm run testRemove Karma
Now that we’ve successfully set up Jest we can remove Karma.
Remove dependencies
npm uninstall karma \ karma-chrome-launcher \ karma-coverage-istanbul-reporter \ karma-jasmine \ karma-jasmine-html-reporterRemove the Karma configuration file
rm karma.conf.jsRemove the test target inside the angular.json file
Instead of ng test we will now run npx jest to launch our unit tests.
Therefore the test target inside the angular.json file is useless. You can remove the section projects.iris-app.architect.test from your angular.json file.
{ "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.css"], "scripts": [] } }}Summary
We saw how we can set up Jest for unit tests. This can be certainly automated using Angular schematics, but it’s not that hard to do it manually.