Jest Unit Test Framework
Configuration
Jest is pre-configured with Nx workspace. Packages that are related with jest are:
●jest (^25.1.0)
●ts-jest (^25.0.0)
●jest-preset-angular (^8.1.3)
●@nrwl/jest (^9.2.0)
●@types/jest (24.0.9)
In angular.json every application and library that is generated by Nx, has its architect section. Under architect, it has test section that defines its testing environment configuration.
"test": {
"builder": "@nrwl/jest:jest",
"options": {
"jestConfig": "libs/core/jest.config.js",
"tsConfig": "libs/core/tsconfig.spec.json",
"setupFile": "test-setup.ts"
}
}
Basic Term
jest.config.js – this js file is configuration file for jest test cases, we have a global jest.config.js and application and libraries specific jest.config.js (which are subset of global jest.config.js)
setupFile – this file defines any extra setup required to run any specific test cases. Extra setup can be importing some external packages.
We have a global test-setup.ts and as well as applications and library specific test-setup.ts
Out of the box, other options for builder (@nrwl/jest) can be found in node_modules/@nrwl/jest/src/builders/jest/schema.json
How to run specific test case:
Define testMatch property in your (application|lib) jest.config.js with regex pattern value matching your test case file name.
testMatch: [‘**/+(user.service.)+(spec|test).+(ts|js)?(x)’]
Running Test Cases ->
npm test lib-name
How to Write Test Cases
For basic jest matchers please go through official jest website :
https://jestjs.io/docs/en/using-matchers
Pipes :
import { blankIfNullPipe } from './blank-if-null.pipe';
describe('blankIfNullPipe', () => {
let pipe: blankIfNullPipe = null;
beforeEach(() => {
pipe = new blankIfNullPipe();
});
it('create an instance', () => {
expect(pipe).toBeTruthy();
});
it('convert null value to blank', () => {
expect(pipe.transform(null)).toMatch(' ');
});
}
Component :
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import {
ButtonComponent
} from './button.component';
import {
NO_ERRORS_SCHEMA,
DebugElement,
ChangeDetectorRef,
ChangeDetectionStrategy
} from '@angular/core';
import {
By
} from '@angular/platform-browser';
import {
LocalizePipe
} from './localizePipe.pipe';
import {
LocalizeService
} from './LocalizeService.service';
const languageServiceMock = {
transform: jest.fn(() => {
return 'abc';
})
};
describe('ButtonComponent', () => {
let component: ButtonComponent;
let fixture: ComponentFixture < ButtonComponent > ;
let el: DebugElement;
beforeEach(async (done => {
TestBed.configureTestingModule({
declarations: [ButtonComponent, LocalizePipe],
providers: [{
provide: LocalizeService,
useValue: localizeServiceMock
}],
schemas: [NO_ERRORS_SCHEMA]
}) /* If ChangeDetectionStrategy -> OnPush (Approach 1) */ .overrideComponent(ButtonComponent, {
set: {
changeDetection: ChangeDetectionStrategy.Default
}
}).compileComponents().then(() => {
fixture = TestBed.createComponent(ButtonComponent);
component = fixture.componentInstance;
});
}));
it('should create', () => {
expect(component).toBeDefined();
});
it('should emit click event', () => {
expect.hasAssertions();
let clicked = false;
component.clickButton.subscribe(() => {
clicked = true;
});
component.click();
expect(clicked).toBeTruthy();
});
it('should have width if width is 100%', () => {
expect.hasAssertions();
el = fixture.debugElement.query(By.css('button'));
expect(el.styles['width']).toBeUndefined();
fixture.detectChanges();
expect(el.styles['width']).toBe('100%');
component.width = '10%';
// If ChangeDetectionStrategy -> OnPush (Approach 2)
const cd: ChangeDetectorRef = fixture.componentRef.injector.get(ChangeDetectorRef);
cd.markForCheck();
fixture.detectChanges();
expect(el.styles['width']).toBe('10%');
// If ChangeDetectionStrategy -> Default
fixture.detectChanges();
expect(el.styles['width']).toBe('10%'); });});
Service : That only holds data and doesn’t have any dependencies
import {
TestBed
} from '@angular/core/testing';
import {
UserService
} from './user.service';
import {
IUser
} from '../interfaces';
import {
User
} from '../classes';
const userJson: IUser = {
firstName: 'Akash',
lastName: 'Mishra',
permission: [],
userid: 1,
role: ['Editor', 'Super Admin', 'Post Admin', 'Customer Support'],
middleName: null,
userName: 'akash.mishra@gmail.com',
email: 'akash.mishra@gmail.com'
};
describe('UserService', () => {
let service: UserService;
beforeEach(() => {
TestBed.configureTestingModule({
providers: [UserService]
});
service = TestBed.inject(UserService);
service.setUser(userJson);
});
it('should be created', () => {
expect(service).toBeTruthy();
});
it('should have User object', () => {
const isUserObject = service.getUser() instanceof User;
expect(isUserObject).toBeTruthy();
});
it('should have permissions', () => {
const user: User = service.getUser();
expect(user.permissions.length).toBeGreaterThan(0);
});
});
Service : That holds Business Logic along with Http Calls and have dependencies
import {
TestBed,
async
} from '@angular/core/testing';
import {
ConfigurationService
} from './configuration.service';
import {
HttpClientTestingModule
} from '@angular/common/http/testing';
import {
CodeDecodePipe,
GlobalValues,
HttpService
} from './common';
import {
of
} from 'rxjs';
const globalValues = {
data: {
obj: {
customercare: {
genericApi: {
genericApiIdentifier: 'mockapi'
}
}
}
}
};
const mockData = [{
id: 10,
name: 'Akash',
configuration: {
id: '1',
name: 'Akash',
shortname: 'Akash'
},
child: null,
icon: null,
description: null,
uploadfile: null,
parentId: null,
iconName: null,
enabled: true,
hierarchy: null,
createdBy: null,
modifierby: null,
creationTime: null
}
];
const httpServiceMock = {
sendGETRequest: jest.fn(),
sendPOSTRequest: jest.fn()
};
describe('ConfigurationService', () => {
let service: ConfigurationService;
let httpService: HttpService;
beforeEach(() => {
TestBed.configureTestingModule({
imports: [HttpClientTestingModule],
providers: [HtmlDecodePipe, {
provide: GlobalValues,
useValue: globalValues
}, {
provide: HttpService,
useValue: httpServiceMock
}]
});
service = TestBed.inject(ConfigurationService);
httpService = TestBed.inject(HttpService);
});
it('should be created', () => {
expect(service).toBeTruthy();
});
it('should return root module data', async (() => {
expect.hasAssertions();
httpServiceMock.sendPOSTRequest.mockReturnValueOnce(of(mockData));
service.getRootModule().subscribe(response => {
expect(httpServiceMock.sendPOSTRequest.mock.calls.length).toBe(1);
expect(response.length).toBe(1);
});
}));
});
Generate Code Coverage Report
Add collectCoverage property in your jest.config.js
collectCoverage: true
Coverage report generated at the location specified in your jest.config.js by property coverageDirectory
coverageDirectory: ‘./coverage/libs/core/tools’,
Open generated index.html of coverage for your lib, will be shown as below:

You can check specific file coverage by clicking on file name, and will be shown like :
