Jest
Verify
With primitive datatype
const { sum, student } = require('../src/main.js');
test('adding should be correct', () => {
expect(sum(1,2)).toBe(3);
})
With objects
test('student has to be correct', () => {
expect(student()).toEqual({
name: "Austin",
age: 3
})
})
Test suit
Test suit will be executed from top to bottom
describe("test-outer", () => {
console.log("outer");
test('outer-test 1', () => console.log("outer-test1"));
describe("test-inner", () => {
test('inner-test 1', () => console.log("inner-test1"));
describe("test-inner-inner", () => {
test('inner-inner-test 1', () => console.log("inner-inner-test1"));
})
})
test('outer-test 2', () => console.log("outer-test2"));
})
So the order of execution is:
outer-test-1
- test-inner suit
inner-test 1
- test-inner-inner suit
inner-inner-test 1
outer-test 2
A suit is just a decorative group of tests.
Mocking
Function mock
You can mock a function using jest.fn()
. For example, considering the following code:
export const forEach = (items: any[], callback: (args: any[]) => any) => {
for (const item of items) {
callback(item);
}
}
And then to test it:
import { forEach } from "../src/forEach";
describe('forEach with number', () => {
test('should be correct with positive number', () => {
const mockCallBack = jest.fn();
forEach([1,2,3,4,5], mockCallBack);
expect(mockCallBack.mock.calls.length).toEqual(5);
})
});
We can access the attribute of the mock function by calling .mock
on the mock function.
Mock module
For example considering our code:
export const doNotMock = () => 5;
export const mockThis = () => "hello world";
You can mock the whole module by calling
import * as app from "../src/myModule";
jest.mock("../src/myModule");
test("should mock", () => {
(app.mockThis as jest.Mock).mockReturnValue("xin chao")
expect(app.mockThis()).toEqual("xin chao")
});
However now our doNotMock
function is also mocked as well and not returning 5.
Partial mock
Consider the same above code, we want to mock mockThis
function but not doNotMock
function. To do that, we can do the following
jest.mock("../src/mockPartial", () => {
const original = jest.requireActual("../src/mockPartial");
return {
__esModule: true,
...original,
mockThis: jest.fn().mockReturnValue("xin chao")
}
})
test('should only mock the correct one', () => {
expect(app.doNotMock()).toEqual(5);
expect(app.mockThis()).toEqual("xin chao")
})
In here, we only mock the mockThis
function, the rest of the functionality is kept the same.
Async mock
If the function is async and we want to return a value, we have to use mockResolvedValue
instead, for example:
export const student = () => ({
name: "Austin",
age: 3
})
export const studentAsync = async () => student()
import * as app from "../src/main";
test('async module mock', () => {
(app.studentAsync as jest.Mock).mockResolvedValue({ name: "hi", age: 4 })
expect(app.studentAsync()).toEqual({name: "hi", age: 3})
})
Export default mock
Sometimes we want to mock the whole export default file. For example:
We have a class
interface VeryComplicatedClass {
age: number
name: string
}
class VeryComplicatedClass {
constructor(name: string, age: number) {
}
public getAge() {
return this.age;
}
public getName() {
return this.name;
}
}
export default VeryComplicatedClass;
This can simply be mocked like this
import VeryComplicatedClass from "../src/export-default";
jest.mock("../src/export-default");
(VeryComplicatedClass as jest.Mock).mockImplementation(() => ({
getName: () => "Austin",
getAge: () => 3
}));
test("should simplify the mock", () => {
const instance = new VeryComplicatedClass("wow", 3);
expect(instance.getAge()).toEqual(3);
expect(instance.getName()).toEqual("Austin");
})
Spying
Spying is being used when you don't want to modify the object but want to monitor and test the current behavior of that object
export const spotify = {
play() {
return true;
}
}
import { spotify } from "../src/spy-on";
test('play video should trigger play', () => {
const spyMethod = jest.spyOn(spotify, 'play');
const isPlaying = spotify.play();
expect(spyMethod).toHaveBeenCalledTimes(1);
expect(isPlaying).toBe(true);
})
Note that in here, we didn't change the object but try to spy on the method play
.