react-testing-library의 사용법이 아닌, 요 근래 해당 라이브러리로 테스트 코드를 작성하다가 겪은 문제를 공유하기 위해 글을 쓰기로 했습니다. 기본적인 사용법은 이미 벨로퍼트님이 작성하신 글이 있기 때문에 링크로 대체합니다.

패키지 이름이 변경되었습니다.

*-testing-library로 지어진 테스트 라이브러리는 @kentcdodds 를 주도로, 사용자가 앱을 사용하는 방법과 유사하게 (단위) 테스트 코드를 작성하자 는 목표 아래 DOM Testing Library 를 기반으로, 다양한 프론트엔드 라이브러리/프레임워크의 테스트 라이브러리를 제공하고 있습니다.

올해 어떤 트위터 이용자가, 훗날 패키지 이름 충돌을 방지하기 위해 관련 테스팅 라이브러리의 패키지들을 @testing-library/* 로 변경하는 것을 제안했습니다. 이 제안이 받아들여졌고, 2019년 5월부터 관련 테스트 라이브러리들의 이름에 스코프가 적용되었습니다. react-testing-library 를 설치하려면 이제 아래와 같이 입력해야 합니다.

$ yarn add @testing-library/react

현재 글을 쓰고있는 기준 최신 버전은 9.1.4 버전이고, 스코프가 적용된 최초의 버전은 8.0.0 입니다. react-testing-library 설치를 시도하면 @testing-library/react 를 설치 할 것을 권장하는 메세지가 출력이 됩니다.

getByValue 에서 getByDisplayValue 로 변경

벨로퍼트님이 작성해 주신 게시글에서는 이미 getByDisplayValue 로 소개가 되어 있습니다. 제가 처음으로(올해 초) 이 라이브러리를 사용해서 테스트 케이스를 작성 할 때는 5.4.4 버전이었고, 그때는 getByValue 라는 쿼리로 제공되고 있었습니다. 그리고 최근에, 릴리즈 된 버전과 비교하여 굉장히 예전 버전을 사용하고 있다는 사실을 알게 되었고, 업데이트를 진행했습니다. 그러자 거의 대부분의 테스트 케이스가 실패하는 상황이 발생했습니다.

원인은, 최신 버전에서는 getByDisplayValue 라는 쿼리로 제공을 하고 있기 때문입니다. 저는 단순히, export 하는 이름이 변경되었구나 싶어 프로젝트에 사용한 getByValue, getAllByValue 를 각각 getByDisplayValue, getAllByDisplayValue 로 변경했습니다. 이후 전체 테스트를 실행한 결과 대부분은 다시 정상적으로 통과했지만, 일부 테스트 케이스에서는 getByDisplayValue 를 사용해도 value 어트리뷰트를 가져오지 못했습니다. 즉, 이전에 사용했던 상황과 달리 내부적으로 DOM을 찾는 방법에도 변화가 생긴 것입니다. 언제, 어떤 배경으로 변경되었는지 확인하기 위해 릴리즈 내역을 확인하던 중 아래와 같이 논의가 진행되었음을 알게 되었습니다.

image.png

이 논의 끝에, 제가 확인 한 바로는 getByValue 가 변경된 것이 아닌, getByDisplayValue 라는 이름으로 쿼리가 추가되었음을 알게 되었습니다. 실제 사용자는 inputvalue 어트리뷰트나, element.value 를 보는 것이 아니라 화면에 표시되는 값, DisplayValue 를 보기 때문입니다. 이 기능의 히스토리를 훑어보니 testing-library 가 어떤 목표를 추구하는지 새삼 느끼게 되었습니다.

제가 그동안 getByValue 로 잘 사용했던 이유는, dom-testing-library 의 4.0.0 릴리즈 이전이었기 때문에 ByValue 쿼리가 남아 있었던 것입니다. 4.0.0 릴리즈의 주요 변경점은 ByValue 가 삭제되는 것 말고도 하나 더 있습니다.

getBy vs getAllBy

image.png

테스트 케이스를 작성할 때 저는 특히 주로 사용하는 쿼리는 getByTextgetByLabelText 입니다. 특정 컴포넌트에 전달 된 props 값이 잘 표시되는지를 위주로 사용하는데, 4.0.0 이후 getBy 로 시작하는 쿼리에 변경점이 있습니다. 이 릴리즈부터, getBy를 통해 찾은 결과가 하나 이상일 경우 오류를 Throw 합니다.

getBy-* 가 엘리먼트를 찾지 못할 경우 오류를 throw 한다는 사실을 알기 전에는 주로 아래와 같이 작성했습니다.

it('should display text as well', () => {
  const { getByText } = render(/* ... */);
  expect(getByText('Foo!')).toBeTruthy();
});

그 사실을 알게 된 후에는 아래와 같이 조금 더 줄여서 작성을 했습니다.

it('should display text as well', () => {
  const { getByText } = render(/* ... */);
  getByText('Foo!');
});

하지만 4.0.0 릴리즈 이후 (@testing-library/react 기준으로는 7.0.0) 아래와 같은 케이스에서는 오류가 발생합니다. 이전 버전에서는 발생하지 않습니다.

const Foo = () => (
  <div>
    <span>Foo!</span>
    <div>
      <p>Foo!</p>
    </div>
  </div>
);

// in test case
it('should display text as well?', () => {
  const { getByText } = render(<Foo/>);
  getByText('Foo!'); // ❌ Found multiple elements with the placeholder text of: Foo!
});

저는 이 때문에 기존의 테스트 케이스를 많이 수정해야 했지만, '이 컴포넌트에서 이 텍스트는 단 한번만 표시되는구나', '같은 텍스트가 여러개 표시되겠구나' 처럼 테스트를 더욱 직관적으로 이해하게 되었습니다.

이 글은 저처럼 react-testing-library 예전 버전에서 올라오시는 분을 위한 글이지만, 어떤 라이브러리가 릴리즈 되고 변경점이 있을 때 그 배경을 찾아 다닌 것은 처음입니다. 메인테이너와 기여자들이 어떤 관점과 목표를 갖고 라이브러리나 패키지를 유지하는지 이해하면, 더욱 잘 쓸 수 있다고 생각하게 되는 계기가 되었습니다.