Jitrak Blog

Npm package for write jest easy

Npm package สำหรับเขียน jest ให้ง่าย

30 Sep 2023 16:05

Written by: Yosapol Jitrak

Tags:

Uni test

Automation

Test double

Jest

TypeScript

jest-extended

jest-extended

ตัวนี้มาช่วยเรื่องการ Assert ของเรา ที่ Jest อาจจะมีไม่เพียบพอ วิธีติดตั้งก็แสนง่าย สามารถไปอ่านได้ตาม Installation

วิธีติดตั้งครับ

pnpm i -D jest-extended

ยกตัวอย่างของที่น่าจะเป็นประโยชน์นะครับ

test('passes when given an empty string', () => {
  expect('').toBeEmpty();
  expect('hello').not.toBeEmpty();
});

test('passes when given an empty array', () => {
  expect([]).toBeEmpty();
  expect(['hello']).not.toBeEmpty();
});

test('passes when given an empty object', () => {
  expect({}).toBeEmpty();
  expect({ hello: 'world' }).not.toBeEmpty();
});
test('passes when value is null or undefined', () => {
  expect(null).toBeNil();
  expect(undefined).toBeNil();
  expect(true).not.toBeNil();
});
test('passes when value is in given array', () => {
  expect(1).toBeOneOf([1, 2, 3]);
  expect(4).not.toBeOneOf([1, 2, 3]);
});
test('passes when given array is in range', () => {
  expect([4, 5, 7, 9]).toBeInRange(4, 10);
  expect([12, 13, 15, 17]).not.toBeInRange(4, 9);
});
test('passes when value is a date', () => {
  expect(new Date()).toBeDate();
  expect('01/01/2018').not.toBeDate();
  expect(new Date('01/01/2018')).toBeDate();
  expect(undefined).not.toBeDate();
});
test('passes when input is after date', () => {
  expect(new Date('01/01/2019')).toBeAfter(new Date('01/01/2018'));
  expect('01/01/2018').not.toBeAfter(new Date('01/01/2019'));
});
test('passes when input is equal to or after date', () => {
  expect(new Date('01/01/2019')).toBeAfterOrEqualTo(new Date('01/01/2018'));
  expect(new Date('01/01/2019')).toBeAfterOrEqualTo(new Date('01/01/2019'));
  expect('01/01/2018').not.toBeAfterOrEqualTo(new Date('01/01/2019'));
});
test('passes when input is before date', () => {
  expect(new Date('01/01/2018')).toBeBefore(new Date('01/01/2019'));
  expect('01/01/2019').not.toBeBefore(new Date('01/01/2018'));
});
test('passes when input is equal to or before date', () => {
  expect(new Date('01/01/2018')).toBeBeforeOrEqualTo(new Date('01/01/2019'));
  expect(new Date('01/01/2018')).toBeBeforeOrEqualTo(new Date('01/01/2018'));
  expect('01/01/2019').not.toBeBeforeOrEqualTo(new Date('01/01/2018'));
});
test('passes when input is in given date range', () => {
  expect(new Date('05/01/2019')).toBeBetween(new Date('01/01/2019'), new Date('10/01/2019'));
  expect(new Date('05/01/2019')).toBeBetween(new Date('05/01/2019'), new Date('10/01/2019'));
  expect(new Date('01/01/2019')).not.toBeBetween(new Date('05/01/2019'), new Date('10/01/2019'));
});
test('calls mock1 before mock2', () => {
  const mock1 = jest.fn();
  const mock2 = jest.fn();

  mock1();
  mock2();
  mock1();

  expect(mock1).toHaveBeenCalledBefore(mock2);
});
test('calls mock1 after mock2', () => {
  const mock1 = jest.fn();
  const mock2 = jest.fn();

  mock2();
  mock1();
  mock2();

  expect(mock1).toHaveBeenCalledAfter(mock2);
});
test('passes when object contains given entry', () => {
  const o = { a: 'foo', b: 'bar', c: 'baz' };
  expect(o).toContainEntry(['a', 'foo']);
  expect(o).toContainEntry(['b', 'bar']);
  expect(o).toContainEntry(['c', 'baz']);
  expect(o).not.toContainEntry(['a', 'qux']);
});
test('passes when object contains all of the given entries', () => {
  const o = { a: 'foo', b: 'bar', c: 'baz' };
  expect(o).toContainEntries([['a', 'foo']]);
  expect(o).toContainEntries([
    ['c', 'baz'],
    ['a', 'foo'],
  ]);
  expect(o).not.toContainEntries([
    ['b', 'qux'],
    ['a', 'foo'],
  ]);
});

jest-when

jest-when ตัวนี้มาช่วยเรื่อง Mock ถ้าได้รับค่าจะทำตามเงื่อนไข ว่าง่าย ๆ ก็คือ Swith case ของการ Mock นั่นเอง

วิธีติดตั้ง

pnpm i -D jest-when

ยกตัวอย่างการใช้งาน

import { when } from 'jest-when';

const fn = jest.fn();
when(fn).calledWith(1).mockReturnValue('yay!').calledWith(2).mockReturnValue('nay!');

expect(fn(1)).toEqual('yay!');
expect(fn(2)).toEqual('nay!');

jest-mock-extended

jest-mock-extended ตัวนี้สำหรับนี้ถือว่าเป็น Hero เพราะช่วยลดการ Mock ของได้มาก ๆ จากบทความที่แล้วเรื่อง Test double ผมมียกตัวอย่างของ NSubstitute ซึ่งเป็น Mocking library ของฝั่ง .NET สำหรับตัว jest-mock-extended จะทำงานเหมือนกันครับ แต่จะเป็นสำหรับ JavaScript และ TypeScript

วิธีติดตั้ง

pnpm i -D jest-mock-extended

ยกตัวอย่างการเป็นการใช้งานจริงเลยนะครับ ในที่นี้ใช้ NestJS Framework นะครับ

describe('GetUserByIdUseCase', () => {
  const userRepository = mock<UserRepository>();
  let userUserByIdUseCase: GetUserByIdUseCase;

  beforeEach(() => {
    userUserByIdUseCase = new GetUserByIdUseCase(userRepository);
  });

  afterEach(() => {
    jest.resetAllMocks();
  });

  it(`should be get correctly user`, async () => {
    // Arrange
    const userId = 'userId';
    const mockUser = mock<IUser>({
      id: userId,
    });
    userRepository.getById.mockResolvedValue(mockUser);

    // Act
    const actual = await userUserByIdUseCase.execute(userId);

    // Assert
    expect(userRepository.getById).toHaveBeenCalledWith(userId);
    expect(actual).toStrictEqual(mockUser);
  });
});
export interface UserRepository {
  createUserRoot(user: IUser): Promise<IUser>;
  getById(id: string): Promise<IUser>;
}

@Injectable()
export class GetUserByIdUseCase {
  constructor(@Inject('UserRepository') private userRepository: UserRepository) {}
  public async execute(userId: string): Promise<IUser> {
    return this.userRepository.getById(userId);
  }
}

ในที่นี้จะเห็นว่าเราทำ Dummy object ของ IUser แถมช่วยให้เราสามารถทำ Stub และ Mock object ที่ Function getById ของ userRepository ได้อย่างง่าย ๆ เลยทีเดียว


axios-mock-adapter

axios-mock-adapter ตัวนี้จะช่วยให้เราสามารถ Mock request ของ axios ได้ง่าย ๆ

วิธีติดตั้ง

pnpm i -D axios-mock-adapter

ขอยกตัวอย่างเพียงนิดหน่อยนะครับ ที่เหลือลองไปศึกษากันดูเองนะครับ

import MockAdaptor from 'axios-mock-adapter';

const mockAxios = new MockAdaptor(axios);
// Mock GET request to /users when param `searchText` is 'John'
// arguments for reply are (status, data, headers)
mock.onGet('/users', { params: { searchText: 'John' } }).reply(200, {
  users: [{ id: 1, name: 'John Smith' }],
});

// Returns a failed promise with Error('Network Error');
mock.onGet('/users').networkError();

จบแล้วนะครับบทความนี้ หวังว่าจะเป็นประโยชน์กับหลาย ๆ คนที่เขียน Uni test ด้วย Jest นะครับ