Jest 매처로 값 검증하기

Jest 매처로 값 검증하기 (Using Matchers)

테스트라고 해서 처음부터 복잡한 프레임워크 지식이 필요하진 않아요. Jest에서는 expect()로 값을 잡고, 그 위에 **매처(matcher)**라는 검증 도구만 얹으면 대부분의 단언(assertion)을 끝낼 수 있죠. 이 글에서는 가장 자주 쓰는 매처들을 상황별로 정리해 볼게요. 모든 매처의 전체 목록은 expect API 문서에서 확인할 수 있어요.

출처: Jest 공식 문서 — Using Matchers

흔히 쓰는 매처

값을 검증하는 가장 간단한 방법은 완전 일치를 비교하는 거예요. 아래 코드에서 expect(2 + 2)expectation 객체를 돌려주는데, 우리는 이 객체에 매처를 호출하는 것 외에는 별로 할 일이 없어요. .toBe(4)가 바로 매처죠. Jest는 실행하면서 실패한 매처를 모두 기억해 두었다가 나중에 보기 좋은 에러 메시지로 출력해 줘요.

test('two plus two is four', () => {
  expect(2 + 2).toBe(4);
});

toBeObject.is를 사용해 완전 일치를 검사해요. 객체의 까지 비교하고 싶다면 toEqual을 사용하면 되는데, 이 매처는 객체나 배열의 모든 필드를 재귀적으로 확인해요.

test('object assignment', () => {
  const data = {one: 1};
  data['two'] = 2;
  expect(data).toEqual({one: 1, two: 2});
});

:::tip

toEqualundefined 값을 가진 키, undefined 배열 요소, 배열의 빈 틈(sparse), 객체 타입 불일치는 무시해요. 이런 부분까지 정확히 잡아내고 싶다면 toStrictEqual을 쓰면 돼요.

:::

매처 앞에 not을 붙이면 반대 조건을 검사할 수 있어요.

test('adding positive numbers is not zero', () => {
  for (let a = 1; a < 10; a++) {
    for (let b = 1; b < 10; b++) {
      expect(a + b).not.toBe(0);
    }
  }
});

진위값 (Truthiness)

테스트를 짜다 보면 undefinednull, false를 구분해야 하는 경우가 있는 반면, 이 셋을 굳이 다르게 취급하고 싶지 않은 경우도 있어요. Jest는 내가 원하는 기준을 분명히 드러낼 수 있도록 도와주는 매처들을 갖추고 있죠.

  • toBeNull: null만 매치해요
  • toBeUndefined: undefined만 매치해요
  • toBeDefined: toBeUndefined의 반대예요
  • toBeTruthy: if 문이 참으로 평가하는 모든 값을 매치해요
  • toBeFalsy: if 문이 거짓으로 평가하는 모든 값을 매치해요

예를 들어 이런 식으로 써요.

test('null', () => {
  const n = null;
  expect(n).toBeNull();
  expect(n).toBeDefined();
  expect(n).not.toBeUndefined();
  expect(n).not.toBeTruthy();
  expect(n).toBeFalsy();
});

test('zero', () => {
  const z = 0;
  expect(z).not.toBeNull();
  expect(z).toBeDefined();
  expect(z).not.toBeUndefined();
  expect(z).not.toBeTruthy();
  expect(z).toBeFalsy();
});

이름에서 알 수 있듯, 코드가 하려는 일을 가장 정확하게 묘사하는 매처를 골라 쓰는 게 좋아요.

숫자

숫자 비교도 대부분 매처로 치환할 수 있어요.

test('two plus two', () => {
  const value = 2 + 2;
  expect(value).toBeGreaterThan(3);
  expect(value).toBeGreaterThanOrEqual(3.5);
  expect(value).toBeLessThan(5);
  expect(value).toBeLessThanOrEqual(4.5);

  // toBe and toEqual are equivalent for numbers
  expect(value).toBe(4);
  expect(value).toEqual(4);
});

부동소수점을 비교할 때는 toEqual 대신 toBeCloseTo를 쓰는 게 안전해요. 아주 작은 반올림 오차 하나 때문에 테스트가 깨지길 원하는 사람은 없으니까요.

test('adding floating point numbers', () => {
  const value = 0.1 + 0.2;
  //expect(value).toBe(0.3);           This won't work because of rounding error
  expect(value).toBeCloseTo(0.3); // This works.
});

문자열

정규식으로 문자열을 검사하고 싶다면 toMatch를 쓰면 돼요.

test('there is no I in team', () => {
  expect('team').not.toMatch(/I/);
});

test('but there is a "stop" in Christoph', () => {
  expect('Christoph').toMatch(/stop/);
});

배열과 이터러블

배열이나 이터러블이 특정 항목을 포함하는지 확인할 때는 toContain을 사용해요.

const shoppingList = [
  'diapers',
  'kleenex',
  'trash bags',
  'paper towels',
  'milk',
];

test('the shopping list has milk on it', () => {
  expect(shoppingList).toContain('milk');
  expect(new Set(shoppingList)).toContain('milk');
});

예외

특정 함수가 호출될 때 에러를 던지는지 확인하고 싶다면 toThrow를 사용해요.

function compileAndroidCode() {
  throw new Error('you are using the wrong JDK!');
}

test('compiling android goes as expected', () => {
  expect(() => compileAndroidCode()).toThrow();
  expect(() => compileAndroidCode()).toThrow(Error);

  // You can also use a string that must be contained in the error message or a regexp
  expect(() => compileAndroidCode()).toThrow('you are using the wrong JDK');
  expect(() => compileAndroidCode()).toThrow(/JDK/);

  // Or you can match an exact error message using a regexp like below
  expect(() => compileAndroidCode()).toThrow(/^you are using the wrong JDK$/); // Test fails
  expect(() => compileAndroidCode()).toThrow(/^you are using the wrong JDK!$/); // Test pass
});

:::tip

예외를 던지는 함수는 반드시 감싸는 함수 안에서 호출해야 해요. 그렇지 않으면 toThrow 단언이 제대로 동작하지 않아요.

:::

더 알아보기

이제 매처의 맛을 보았으니, 다음 단계로는 Jest가 비동기 코드를 테스트하는 방법을 살펴보는 걸 추천해요. 매처 하나하나의 전체 규칙이 궁금하다면 expect API 레퍼런스를 참고하세요.