React

React Testing Library 튜토리얼

오스타 2022. 3. 28. 20:59

React Testing Library 튜토리얼

본 포스트는 기초적인 React Testing Library 사용법에 중점을 두고 있습니다. 리액트 테스트 코드 설계에 관한 포스트는 향후 학습을 진행하고 작성해볼 예정입니다

 

 

Testing Library

테스팅 라이브러리는 UI 컴포넌트를 사용자 중심적인 방식으로 테스팅할 수 있게끔 하는 패키지이다

우리는 테스트에 구현 세부 사항이 포함되지 않도록 하여 컴포넌트 리팩토링이 진행되더라도 테스트에는 영향을 끼치지 않는 방법을 도입하고 싶어 한다. 그런 면에서 테스팅 라이브러리는 사용자가 애플리케이션을 어떻게 사용하는지에 최대한 가깝게 테스트를 작성할 수 있도록 도움을 주는 유틸리티를 제공한다

테스팅 라이브러리의 코어인 'DOM Testing Library'는 DOM 노드를 쿼리하고 상호작용하여 웹 페이지를 테스트하기 위한 경량 솔루션이다. 주요 유틸리티에는 유저가 페이지에서 엘리먼트를 찾는 것과 유사한 방식으로 노드에 대한 DOM 쿼리가 포함된다

이러한 방식의 테스트는 세부 구현 사항에 대한 테스팅이 아니라 실제 사용자가 애플리케이션을 사용할 때 제대로 작동할 것이라는 확신을 줄 수 있다

 

 

React Testing Library

리액트 테스팅 라이브러리는 유저가 보고 수행하는 작업에 대해 테스팅하도록 설계되었다. 그리고 우리는 이 패러다임을 잘 이해하고 사용할 필요가 있다

RTL은 테스트를 렌더링할 때 리액트 컴포넌트의 인스턴스가 아닌 실제 DOM 노드를 사용한다. 즉 유저가 애플리케이션을 실행하는 실제 환경과 유사한 환경에서 테스트 케이스가 실행되는 것이다. 우리는 이 점을 분명히 인식하고 사용자 인터랙션에 관해 테스팅을 설계해야 한다

 

npm install -D @testing-library/react

CRA(Create React App)으로 생성한 프로젝트에는 디폴트로 함께 설치된다. 그렇지 않은 경우에는 따로 패키지를 설치해준다

 

test('Can it be rendered?', () => {
  render(<Counter />)
})

render 메서드는 document.body에 해당 컴포넌트를 렌더링 한다

쿼리를 통해 해당 엘리먼트를 찾기 위해서는 우선 render 메서드를 통해 컴포넌트 렌더링이 진행되어야 한다

 

test('does increase button exist?', () => {
  render(<Counter />)
  screen.getByText('증가', { exact: false })
})

테스팅 라이브러리에서는 screen을 활용하여 쿼리를 요청할 수 있다. document.body를 쿼리할 때, screen을 사용한다

단, screen을 사용하려면 전역 DOM 환경이 필요하다. 따라서 jest 사용 시 testEnvironment를 jsdom으로 설정해준다

 

 

Core API

Queries

쿼리는 페이지에서 엘리먼트를 찾기 위해 테스트 라이브러리에서 제공하는 메서드이다.

쿼리 메서드는 세 가지 섹션으로 되어 있다. 따라서 내가 어떤 쿼리 타입이 필요하고 타겟 유형과 갯수가 어떻게 되는가에 따라 다른 쿼리 메서드를 활용해준다

example) getAllByRole
get(쿼리타입) / All(타겟개수) / ByRole(타겟유형)

3가지 타입의 쿼리(get, query, find)를 제공한다. 이들 간의 차이점은 엘리먼트를 찾을 수 없는 경우 쿼리에서 오류를 발생시키는지 혹은 Promise를 반환하고 다시 시도하는지의 여부이다. 선택하는 페이지 콘텐츠에 따라 다른 쿼리 타입이 적절할 수 있다

  • get - 해당 쿼리에 대해 일치하는 노드를 반환하고 일치하는 요소가 없다면 에러를 발생시킨다
  • query - 해당 쿼리에 대해 일치하는 노드를 반환하고 일치하는 요소가 없다면 null을 반환한다
  • find - 해당 쿼리에 일치하는 엘리먼트가 발견될 때 resolve되는 Promise를 반환한다. 찾지 못할 경우 reject (비동기)

타겟 개수의 경우 복수 개의 엘리먼트를 찾는 경우 'All'을 붙여준다

타겟 유형은 아래와 같이 다양한 종류들을 가진다. 개발에서 요소를 찾는데 활용되는 id, class 프로퍼티보다는 유저들이 화면에서 확인할 수 있는 label, text와 같은 프로퍼티로 엘리먼트를 탐색한다

getByLabelText - label의 텍스트 값으로 label과 연결된 input 엘리먼트를 찾아준다. id, class 값이 아닌 유저가 화면으로 확인하는 label 텍스트로 엘리먼트를 찾는 본 메서드는 사용자 인터랙션 테스트를 추구하는 모습을 엿볼 수 있다

getByPlaceholderText - 엘리먼트의 placeholder 값으로 탐색하는 쿼리

getByText - 엘리먼트가 가진 text로 탐색하는 쿼리이다. 간편하게 자주 쓰이는 쿼리 메서드

getByTitle - 엘리먼트의 'title' 프로퍼티에 부여된 값으로 탐색하는 쿼리

getByRole - 엘리먼트의 'role' 프로퍼티 값으로 탐색하는 쿼리. role은 html 태그가 무슨 역할을 하는지 추가적인 설명을 위해 부여된 속성으로 많은 요소들이 디폴트 값을 가지고 있다

 

Queries Priority

리액트 테스팅 라이브러리 쿼리는 많은 타겟 유형을 가진다. 어떤 것을 기준으로 쿼리를 던지느냐에 따라 유형이 다양하지만 하나의 요소를 쿼리하는데 다양한 타겟 유형을 사용할 수 있기 때문에 개발자는 때때로 정확한 가이드를 원하기도 한다

그래서 테스팅 라이브러리 공식문서에서는 쿼리 유형 중 우선순위를 제공하여 우선적으로 사용할 쿼리들을 추천하고 있다

테스팅 라이브러리는 유저가 접하는 화면을 토대로 테스트를 진행하길 원한다. 따라서 화면에 직접 출력될 text를 이용하는 getByLabelText, getByPlaceholderText, getByText 혹은 유저가 인식하는 요소의 역할을 토대로 탐색하는 getByRole을 우선적으로 사용할 것을 추천한다

이 쿼리들이 마땅치 않을 경우에는 유저가 인식할 수 없는 alt, title과 같은 속성을 탐색하는 getByAltText, getByTitle 쿼리 사용을 권유한다

이 마저도 여의치 않을 경우에는 실제 컴포넌트 코드에 testId 속성을 부여해주어야 하는 getByTestId 쿼리를 사용한다

 

Actions

fireEvent - 유저가 발생시키는 이벤트를 점화(fire)하는 간편한 API이다

fireEvent[eventName](node: HTMLElement, eventProperties: Object)
​
example) fireEvent.click(screen.getByText('증가'))

위 코드와 같이 테스트 코드에서 임의로 이벤트를 발생시킬 수 있다

fireEvent에서 제공하는 이벤트 종류는 다음 페이지에서 확인이 가능하다

 

mock 함수를 사용한 이벤트 핸들러 테스팅

test('Button Test', () => {
  const mockBtnHandler = jest.fn()
​
  render(<Button onClick={mockBtnHandler} title={'Test Button'} />)
  fireEvent.click(screen.getByText('Test Button'))
​
  expect(mockBtnHandler).toHaveBeenCalled()
})

jest mock 함수를 사용하여 이벤트 핸들러의 실행 여부를 테스트하는 코드이다

onClick 콜백 함수에 mock 함수를 생성하여 전달해주면 우리는 Button 컴포넌트 클릭 이벤트를 점화해서 해당 이벤트 핸들러가 호출되는지 확인해볼 수 있다

 

 

Supplement

React Testing Procedure

리액트 테스트는 다음의 순서로 진행된다

render(<Counter />)
  1. 테스트할 컴포넌트를 렌더링한다
const increaseBtn = screen.getByRole('button', { name: '증가' })
  1. 인터랙션할 엘리먼트를 찾는다
fireEvent.click(increaseBtn)
  1. 해당 엘리먼트로 인터랙션을 진행한다
expect(screen.getByText(/Count : 1/i)).toBeInTheDocument()
  1. 결과가 예상대로 나왔는지 Assertion 진행

 

testing-library/jest-dom

DOM 상태에 대한 Assertion을 돕는 다양한 matcher 세트를 제공하는 라이브러리이다

리액트 테스팅을 진행할 때, 보다 선언적이고 UI 중심적인 테스트 코드를 짜기 용이해진다

구체적인 matcher들을 살펴보려면 다음 링크를 참조하자

 

 

Reference

 

 

LIST

'React' 카테고리의 다른 글

고차 컴포넌트(HOC)와 Hooks  (3) 2022.05.29
React Router v6 정리  (0) 2022.04.04