bkdragon's log

[Test] Jest, React-testing-library 본문

concept

[Test] Jest, React-testing-library

bkdragon 2023. 7. 7. 17:26

프론트엔드 테스트 코드를 작성할 때 Jest와 React-testing-library (이하 RTL)을 사용한다.

 

 

RTL

RTL은 CRA로 설치하면 기본적으로 설치되어있다. RTL을 사용하면 사용자가 컴포넌트를 사용하는 것처럼 테스트를 작성할 수 있다. 컴포넌트를 렌더링하고 쿼리를 이용해 원하는 요소를 찾고 클릭 등의 상호작용을 제어 할 수 있다. 간단한 예시를 살펴보자.

 

import {render, screen} from '@testing-library/react'
import userEvent from '@testing-library/user-event'
import '@testing-library/jest-dom'
import Fetch from './fetch'

test('loads and displays greeting', async () => {
  render(<Fetch url="/greeting" />)

  await userEvent.click(screen.getByText('Load Greeting'))
  await screen.findByRole('heading')

  expect(screen.getByRole('heading')).toHaveTextContent('hello there')
  expect(screen.getByRole('button')).toBeDisabled()
})
  • userEvent : 브라우저에서 상호작용이 일어났을 때 발생할 이벤트를 디스패치하여 사용자 상호작용을 시뮬레이션한다.
  • Fetch : 위 코드에서 테스트할 컴포넌트.
  • render : react-testing의 메서드 컴포넌트를 DOM에 렌더링한다.
  • getByText : 주어진 text와 일치하는 text를 가진 모든 요소를 검색한다. 위 코드에선 ‘Load Greeting’을 가진 노드를 찾고 userEvent를 이용해 클릭한다.
  • findByRole : 주어진 역할을 가진 요소를 찾는다. 위 코드에서 ‘heading’ 역할을 가진 노드를 찾을 때까지 기다린다. (비동기적으로 동작함)

예제에 나온 ByText, ByRole 함수들을 React Testing Library에선 쿼리라고 한다.

 

쿼리는 페이지에서 요소를 찾기 위해 제공되는 메서드이다. get, find, query 등의 유형이 있다. 각 타입의 쿼리를 분류하자면 다음과 같다.

Type of Query 일치하는 요소 x 1개 일치 두 개 이상 일치 재시도
getBy... Throw error Return element Throw error No
queryBy... Return null Return element Throw error No
findBy... Throw error Return element Throw error Yes
  • getBy : 쿼리에 대해 일치하는 노드를 반환하고, 없거나 두 개 이상 발견되면 에러를 발생시킨다. (두 개 이상의 요소가 예상되는 경우 getAllBy)
  • queryBy : 쿼리에 대해 일치하는 노드를 반환하고 일치하는 요소가 없으면 null을 반환한다. 일치하는 노드가 존재하지 않음을 확인하는데 유용하다.
  • findBy : 주어진 쿼리와 일치하는 엘리먼트가 발견될 때 해결되는 프로미스를 반환한다. 엘리먼트가 발견되지 않거나 기본 시간 초과 후에 엘리먼트가 두 개 이상 발견되면 프로미스가 거부된다. ( findAllBy )

 Jest

jest는 자바스크립트 테스팅 라이브러리로 테스트할 그룹을 묶고 예상결과를 비교하고 모의함수를 만드는 등 다양한 기능을 제공한다.

 

기본 사용법은 다음과 같다.

describe('계산 테스트', () => {
   const a = 1, b = 2;
 
   it('a + b는 3이다.', () => {
      expect(a + b).toEqual(3);
   });
});

describe는 테스트 그룹을 묶어주는 역할은 한다. 일반적으로는 특정 함수에 대해 묶지만 리액트에선 컴포넌트 단위로 묶는다. toEqual은 Matcher 라 불리는 메서드인데 Matcher 는 값을 비교할 때 사용한다. 다양한 Matcher 가 있다 아래 링크에서 확인 할 수 있다.

 

https://jestjs.io/docs/using-matchers

 

Using Matchers · Jest

Jest uses "matchers" to let you test values in different ways. This document will introduce some commonly used matchers. For the full list, see the expect API doc.

jestjs.io

 

Jest의 막강한 기능 중 하나가 모의 함수를 만들 수 있는 것인데 Mock 함수라고 한다. Mock 함수는 호출된 횟수나 어떤 인자로 호출 되었는지를 기억해서 Matcher와 함께 사용하면 테스트에 유연함을 더해준다.

 

예를 들어 forEach의 콜백이 잘 작동하는 지 테스트 해보려고 한다.(실제론 할 필요없다 이미 테스트 되어 있을 테니까)

이때 forEach의 콜백을 Mock함수로 사용하면 편하다. 

 

const mockCallback = jest.fn((x) => 42 + x);
forEach([0, 1], mockCallback);

 

사실 웬만한 JavaScript 개발자들은 forEach의 동작 방식을 안다. 우리는 forEach의 콜백이 2번 실행 될것이고, 0과 1을 인자로 받아 실행 될 것이고, 반환 값으로 42와 43이 나올 것을 예측할 수 있다.

 

describe('forEach', () => {
    const mockCallback = jest.fn((x) => 42 + x);
    
    forEach([0, 1], mockCallback);
    
    // 모의 함수 2번 호출됨
    expect(mockCallback.mock.calls.length).toBe(2);

    // 첫번째 호출된 모의 함수의 첫번째 인자는 0이다.
    expect(mockCallback.mock.calls[0][0]).toBe(0);

    // 두번째 호출된 모의 함수의 첫번째 인자는 1이다.
    expect(mockCallback.mock.calls[1][0]).toBe(1);

    // 첫번째 호출된 모의 함수의 반환 값은 42
    expect(mockCallback.mock.results[0].value).toBe(42);

    // 두번째 호출된 모의 함수의 반환 값은 43
    expect(mockCallback.mock.results[1].value).toBe(43);
})

Mock 함수에 mock 프로퍼티 내부에 호출 정보들이 담겨있어서 테스트하기 편해진다.

 

 

Jest와 RTL의 기본적인 사용법에 대해 알아보았다. 다음엔 직접 작성한 테스트 코드를 소개해보겠다.

'concept' 카테고리의 다른 글

Flux 패턴  (0) 2023.07.17
[TEST] MSW와 통합 테스트  (0) 2023.07.12
[TEST] 프론트의 단위 테스트와 BDD  (0) 2023.07.09
[JavaScript] Call Stack, Callback Queue  (0) 2023.06.24
react-daum-postcode, antd, react-hook-form 조합하기  (0) 2023.03.27