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:

  1. outer-test-1
  2. test-inner suit
    1. inner-test 1
    2. test-inner-inner suit
      1. inner-inner-test 1
  3. 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.