HomeAbout

Unit Test

Rules

Avoid fixtures that are defined outside of the test suite as a common fixture and used in multiple suites.

  • Compromises the test isolation.
  • Makes it hard to change.

Debugging

When debugging a Jest test, console.log in the actual implementation, not in the test.

Testing

Testing specific assertion:

jest some_test.test.js -t "expression matcher for a test description"

Assertions

toBe vs toEqual vs toStrictEqual

toBe is a primitive comparison.

  • use for string.

toEqual is a deep comparison.

  • use for array and objects.

Two identical objects can have identical toEqual but different toStrictEqual or toBe

  • toStrictEqual tests reference
  • toBe tests Object.is()
  • toEqual tests values

Rule: avoid toBe and prefer toStrictEqual

const a = { id: "1" }; expect(a).toEqual({ id: "1" }); // Pass expect(a).toStrictEqual({ id: "1" }); // Fail // `b` points to memory location of `a` const b = a; expect(a).toEqual(b); // Pass expect(a).toStrictEqual(b); // Pass

Special Assertions

// assertion to be implemented later it.todo() // run specified assertion exclusively describe.only() it.only() // positional injector parameter // %p is inserted to each assertion description describe.each([1, 2, 3])("each element %p exists", (el) => { test(`returns ${el}`, () => { expect(el).toBe(true); }) })

Mocking

Importing mocks of react-router and Apollo

Uses jest.requireActual

jest.mock('react-router-dom', () => ({ ...(jest.requireActual('react-router-dom') as any), useParams: () => ({ id: 'abc-123' }), })); jest.mock('@apollo/client', () => ({ ...jest.requireActual('@apollo/client' as any, useQuery: jest.fn(), useMutation: jest.fn(), }));

Methods

mockResolvedValueOnce

Syntax

.mockResolvedValueOnce(value);

Is a syntactic sugar for:

jest.fn().mockImplementationOnce(() => Promise.resolve("some value"));

Import

With above, you have to mock the import first before calling the mockResolvedValueOnce:

import someModule from 'someModule'; jest.mock('someModule', () => { someMethod: jest.fn() }); // assertion (someModule.someMethod as jest.Mock).mockResolvedValueOnce("some value");

When importing a default nested file, here is the syntax:

import someModule from 'someLibrary/someDomain'; jest.mock('someLibrary/someDomain', () => ({ default: jest.fn(), }));

Behavior

mockResolvedValueOnce would be used to guarantee the called expression returns the mock once:

import {jest, test} from '@jest/globals'; test('async test', async () => { const asyncMock = jest .fn<() => Promise<string>>() .mockResolvedValue('default') .mockResolvedValueOnce('first call') .mockResolvedValueOnce('second call'); await asyncMock(); // 'first call' await asyncMock(); // 'second call' await asyncMock(); // 'default' await asyncMock(); // 'default' });

.toHaveReturned()

Purpose

Tests whether a mock function successfully returned at least one time

  • No param passed
test('fn returns', () => { const testFn = jest.fn(()=>true); testFn(); expect(testFn).toHaveReturned(); })

spyOn

default export

You can spyOn default exports using following syntax:

import * as someModuleDefaultExport from 'someModule'; someModuleDefualtExportSpy = jest.spyOn( someModuleDefaultExport, 'someModuleDefaultExport' )

Async Jest

.resolves and .rejects

By using .resolves matcher, Jest will wait for that Promise to resolve.

  • If the Promise is rejected, it'll automatically fail.
// resolves test('testing Promise return expected resolved value', async () => { await expect( async () => await service.fetchData()).resolves.toBe('value'); }); // rejects test('testing Promise rejects to throw an expected error', async () => { await expect( async () => await service.fetchData()).rejects.toThrow(new NotFoundError().message); });
AboutContact