pile·
프론트엔드·카카오 엔터테인먼트 FE카카오 엔터테인먼트 FE·

나만의 Jest Matchers 만들기

같은 검증을 여러 번 반복하게 만드는 테스트 코드를 Jest Custom Matcher 로 줄이는 방법을 정리한 글이다. 컴포넌트가 여러 class 를 포함하는지 확인하려고 `expect(...).toContain(...)` 을 백 줄 늘어놓는 상황을 예로 들고, `toContainAllClasses` 라는 매처를 직접 만들어 한 줄로 바꾼다.

핵심 포인트
  • Matcher 는 작업물의 결과와 예상 결과의 관계를 정의하는 부분이다. `expect(myFunc(...)).toEqual(expected)` 에서 `toEqual` 이 그것이다.
  • Custom Matcher 는 `pass` 와 `message` 를 키로 가진 객체를 반드시 반환해야 하며, `message` 는 pass 값에 따라 다른 문구를 내보내는 함수다.
  • 등록은 `jest.setup.js` 를 만들어 `setupFilesAfterEnv` 에 넣고 `expect.extend({ ... })` 를 호출하면 끝난다.
  • 매처 안에서 받은 값의 타입을 직접 검증해 잘못된 인자를 던지도록 하는 것을 권한다.
  • TypeScript 프로젝트라면 `jest.d.ts` 에 `declare global { namespace jest { interface Matchers<R> ... } }` 로 타입을 선언하고 `tsconfig.json` 의 include 에 추가해야 한다.
상세 정리
  • 문제 상황: `<Input disabled />` 가 특정 class 를 갖는지 확인하는 테스트에서 class 가 늘어날수록 `toContain` 이 그대로 늘어난다. 극단적으로 100개면 100줄이다.
  • 대안 인정: `toHaveClass` 매처나 forEach 로도 줄일 수 있다고 먼저 밝히고, 이 글의 목적은 Custom Matcher 설정 학습이라고 범위를 못 박는다.
  • Matcher 개념 정리: 테스트란 결국 "작업물이 주어진 조건에서 예상한 결과대로 동작하는지" 확인하는 일이고, `expect` 는 매처에 접근하는 함수라는 식으로 구성 요소를 하나씩 대응시킨다.
  • 기본 매처 예시로 `toBeNull`, `toBeUndefined`, `toContain`, `toThrow` 를 든다.
  • 구현: `toContainAllClasses(element, expected)` 는 element 가 `window.HTMLElement` 인지, expected 가 배열인지 먼저 검사해 아니면 Error 를 던진다.
  • 판정 로직은 `element.getAttribute("class")?.split(" ") ?? []` 로 실제 class 목록을 만들고 `expected.every(item => received.includes(item))` 로 pass 를 계산한다.
  • 실패 메시지는 실제 class 목록과 기대 목록을 나열하고, pass 여부에 따라 "not" 을 끼워 넣어 부정형 사용(`not.toContainAllClasses`)에도 문장이 맞게 한다.
  • 결과: 도입부의 백 줄짜리 테스트가 기대 class 배열 하나와 `expect(input).toContainAllClasses(expectedClassList)` 한 줄로 정리된다.
  • 마무리 조언: 테스트 코드에서 반복되는 패턴이 보이면 Custom Matcher 로 묶어 보라고 권한다.
왜 읽나Jest 로 컴포넌트 테스트를 쓰다 같은 단언이 계속 늘어나는 걸 겪은 프론트엔드 개발자에게 매처 하나 만드는 최소 절차를 준다.
카카오 엔터테인먼트 FE
카카오 엔터테인먼트 FE 블로그
원문은 여기서 이어서 읽을 수 있어요
원문 읽기
읽음 (0)

이 글과 비슷한

  1. 프론트엔드·여기어때 (GC컴퍼니)여기어때 (GC컴퍼니)·

    항공 프론트엔드 구축기 (7/10): 창구를 하나만 두었습니다

    여기어때 항공 서비스 프론트엔드가 웹과 앱 웹뷰 두 환경에서 동일한 함수 호출로 동작하는 앱 브릿지 추상화 레이어를 설계한 과정을 다룬다. iOS·안드로이드 규약 차이와 "웹에 존재하지 않는 브릿지를 어떻게 호출하나"라는 문제를 단일 추상화 층으로 해결한 구현 사례다.

    요약 이어보기
    #react#typescript#webview+2