Jest Expect API

Jest Expect API (매처)

테스트를 작성하다 보면 값이 특정 조건을 만족하는지 확인해야 할 때가 많아요. expect는 그 '확인'을 위한 매처(matcher) 함수들을 제공하는 진입점이에요. 어떤 값이 기대와 같은지, 또는 기대와 다른지를 한 줄로 검증할 수 있게 해 줘요.

출처: Jest 공식 문서 - Expect

expect(value)

expect는 값을 테스트할 때마다 쓰여요. 단독으로 호출하기보다는 매처(matcher) 함수와 함께 사용해요.

예를 들어 bestLaCroixFlavor()'grapefruit'를 반환해야 한다면 이렇게 테스트해요.

test('the best flavor is grapefruit', () => {
  expect(bestLaCroixFlavor()).toBe('grapefruit');
});

여기서 toBe가 매처에요. expect의 인자는 코드가 만들어낸 값, 매처의 인자는 '올바른 값'이에요. 둘을 뒤바꿔도 테스트는 돌지만 실패 메시지가 이상해지니 순서를 지켜요.

Modifiers (수식자)

expect와 매처 사이에는 동작을 바꾸는 수식자들이 들어갈 수 있어요.

.not — 반대를 테스트해요.

test('the best flavor is not coconut', () => {
  expect(bestLaCroixFlavor()).not.toBe('coconut');
});

.resolves — fulfilled 프로미스의 값을 풀어서 다른 매처를 이어서 쓸 수 있게 해요. 프로미스가 reject되면 단언이 실패해요.

test('resolves to lemon', async () => {
  await expect(Promise.resolve('lemon')).resolves.toBe('lemon');
});

.rejects — rejected 프로미스의 이유(reason)를 풀어서 다른 매처를 이어서 써요.

test('rejects to octopus', async () => {
  await expect(Promise.reject(new Error('octopus'))).rejects.toThrow('octopus');
});

Matchers (주요 매처)

테스트에서 자주 쓰이는 매처 몇 개를 살펴볼게요.

.toBe(value) — 원시 값 비교 또는 객체 인스턴스의 참조 동일성 검사. ===보다 낫다고 여겨지는 Object.is를 사용해 비교해요. 부동소수점은 쓰지 마세요 — 0.2 + 0.10.3과 엄밀히 같지 않아요. 부동소수점은 .toBeCloseTo를 쓰는 게 좋아요.

const can = {
  name: 'pamplemousse',
  ounces: 12,
};

describe('the can', () => {
  test('has 12 ounces', () => {
    expect(can.ounces).toBe(12);
  });

  test('has a sophisticated name', () => {
    expect(can.name).toBe('pamplemousse');
  });
});

.toHaveBeenCalled() — mock 함수가 호출됐는지 확인해요.

function drinkAll(callback, flavour) {
  if (flavour !== 'octopus') {
    callback(flavour);
  }
}

describe('drinkAll', () => {
  test('drinks something lemon-flavoured', () => {
    const drink = jest.fn();
    drinkAll(drink, 'lemon');
    expect(drink).toHaveBeenCalled();
  });
});

.toHaveBeenCalledTimes(number) — mock 함수가 정확히 N번 호출됐는지 확인해요.

const drink = jest.fn();
drinkEach(drink, ['lemon', 'octopus']);
expect(drink).toHaveBeenCalledTimes(2);

.toHaveBeenCalledWith(arg1, arg2, ...) — mock 함수가 특정 인자로 호출됐는지 확인해요. 인자 비교는 .toEqual과 같은 알고리즘을 사용해요.

.toHaveBeenLastCalledWith(...) — 마지막 호출의 인자를 확인해요. .toHaveBeenNthCalledWith(nthCall, ...) — n번째 호출의 인자를 확인해요 (n은 1부터 시작하는 양의 정수).

.toHaveLength(number) — 배열/문자열 길이 확인. .toHaveProperty(keyPath, value?) — 객체가 특정 속성을 갖는지 확인해요.

.toBeCloseTo(number, numDigits?) — 부동소수점 근사 비교. .toBeDefined(), .toBeUndefined(), .toBeNull(), .toBeNaN() — 값의 존재/종류 확인.

.toBeGreaterThan(number | bigint), .toBeGreaterThanOrEqual, .toBeLessThan, .toBeLessThanOrEqual — 크기 비교.

.toBeInstanceOf(Class) — 인스턴스 타입 확인. .toBeFalsy(), .toBeTruthy() — 불리언 진리성.

.toContain(item) — 배열/문자열이 항목 포함. .toEqual(value) — 객체 값의 깊은 동등 비교. .toMatch(regexp | string) — 정규식/문자열 매칭. .toMatchObject(object) — 부분 객체 매칭.

.toStrictEqual(value)undefined 속성, 배열 희소성 등까지 엄밀하게 비교하는 깊은 동등.

.toThrow(error?) — 함수가 예외를 던지는지 확인.

Asymmetric Matchers (비대칭 매처)

부분 일치를 위해 쓸 수 있는 매처들이에요.

expect.anything();          // null/undefined가 아닌 것
expect.any(constructor);    // 주어진 생성자의 인스턴스
expect.arrayContaining(array);
expect.objectContaining(object);
expect.stringContaining(string);
expect.stringMatching(regexp);
expect.closeTo(number, numDigits?);
test('contains lemon and octopus', () => {
  expect(['lemon', 'octopus']).toEqual(expect.arrayContaining(['lemon']));
});

Assertion Count

expect.assertions(number) — 테스트가 최소 N번 단언한다고 선언. expect.hasAssertions() — 최소 1번의 단언이 실행됨을 보장.

Extend Utilities (매처 확장)

expect.extend(matchers) — 커스텀 매처를 Jest에 추가해요.

import { expect } from '@jest/globals';

function toBeWithinRange(actual, floor, ceiling) {
  if (typeof actual !== 'number' || typeof floor !== 'number' || typeof ceiling !== 'number') {
    throw new TypeError('These must be of type number!');
  }
  const pass = actual >= floor && actual <= ceiling;
  if (pass) {
    return {
      message: () =>
        `expected ${this.utils.printReceived(actual)} not to be within range ${this.utils.printExpected(` ${floor} - ${ceiling} `)}`,
      pass: true,
    };
  } else {
    return {
      message: () =>
        `expected ${this.utils.printReceived(actual)} to be within range ${this.utils.printExpected(` ${floor} - ${ceiling} `)}`,
      pass: false,
    };
  }
}

expect.extend({ toBeWithinRange });

커스텀 매처는 this.utils 같은 컨텍스트 헬퍼를 쓰기 위해 일반 함수로 정의해야 해요 (화살표 함수 안 됨). expect.addEqualityTesters(testers), expect.addSnapshotSerializer(serializer)도 있어요. Jest 커뮤니티가 만든 추가 매처는 jest-extended를 참고해요.

더 알아보기