The Angular CLI ships with Jasmine and Karma to run unit tests. However, out of the box, the unit tests are a bit slow for my tastes. Using Jest for our unit tests speeds things up and only runs the tests when the component has been changed.
Setup Jest
When you create a new Angular CLI project, a few things need to be configured in order to get the unit tests up and running with Jest. First, you will need to add/remove the following packages to your project:
yarn remove @types/jasmine --dev
yarn remove @types/jasminewd2 --dev
yarn add @types/jest jest jest-preset-angular --dev
Create a file called setup-jest.ts
in the src
directory. The only code necessary will be the import 'jest-preset-angular';
, however, the code following the import helps solve a few issues due to using jsdom.
import "jest-preset-angular";
const mock = () => {
let storage = {};
return {
getItem: key => (key in storage ? storage[key] : null),
setItem: (key, value) => (storage[key] = value || ""),
removeItem: key => delete storage[key],
clear: () => (storage = {}),
};
};
Object.defineProperty(window, "localStorage", { value: mock() });
Object.defineProperty(window, "sessionStorage", { value: mock() });
Object.defineProperty(window, "getComputedStyle", {
value: () => ["-webkit-appearance"],
});
// Used to fix the ReferenceError: CSS is not defined ERROR
Object.defineProperty(window, "CSS", { value: mock() });
// Suppresses console warning from Angular Material
console.warn = () => {
return;
};
Object.defineProperty(window, "SVGElement", { value: Element });
/**
* ISSUE: https://github.com/angular/material2/issues/7101
* Workaround for JSDOM missing transform property
*/
Object.defineProperty(document.body.style, "transform", {
value: () => {
return {
enumerable: true,
configurable: true,
};
},
});
/**
* Fixes Material Snackbar window binding
*/
Object.defineProperty(window, "matchMedia", {
value: () => {
return window.matchMedia.bind(window);
},
});
Update the package.json
Add the following to the specified JSON objects.
{
...
"scripts": {
...
"test": "jest --watch",
"test:ci": "jest --runInBand --coverage"
},
"jest": {
"preset": "jest-preset-angular",
"setupTestFrameworkScriptFile": "<rootDir>/src/setup-jest.ts",
"globals": {
"ts-jest": {
"tsConfigFile": "src/tsconfig.spec.json"
},
"__TRANSFORM_HTML__": true
},
"transform": {
"^.+\\.(ts|js|html)$": "<rootDir>/node_modules/jest-preset-angular/preprocessor.js"
},
"coveragePathIgnorePatterns": [
"/node_modules/",
".html$",
"setup-jest.ts",
"/index.ts"
]
},
...
}
Cleanup Jasmin/Karma
It is now safe to cleanup the Jasmine and Karma setup files. To do this, you should delete the src/karma.conf.js
and src/test.ts
files. You will also need to delete the following lines from the angular.json
file:
"test": {
"builder": "@angular-devkit/build-angular:karma",
"options": {
"main": "src/test.ts",
"polyfills": "src/polyfills.ts",
"tsConfig": "src/tsconfig.spec.json",
"karmaConfig": "src/karma.conf.js",
"styles": ["src/styles.scss"],
"scripts": [],
"assets": ["src/favicon.ico", "src/assets"]
}
}
With Angular 6, the defaults in the tsconfig.json
and tsconfig.spec.json
have been changed so Jest will throw an error. You can find the fix in the jest-preset-angular documentation.