티스토리 뷰

반응형

유저액션 중심의 컴포넌트 테스트를 위한 도구 testing library

Jest는 테스트러너로서 테스트를 돌리고 성공, 실패를 확인하거나 테스트에 대한 함수를 제공해주는데에 반면에 Testing library는 리액트 컴포넌트를 테스트하기위한 가상 DOM을 제공해준다.

이 둘은 각각 수행하는 역할이 다르다.

시작하기전에 읽어보면 좋은 포스팅

NHN 프론트엔드 테스트

초보자를 위한 React 어플리케이션 테스트 심층가이드(1)

초보자를 위한 React 어플리케이션 테스트 심층가이드(2)

Testing Library Docs

  • introduction, Guiding Principles 두 섹션 읽어보기

 

테스팅 원칙

  • 유저가 페이지랑 인터렉션하는 방식과 매우 유사하게 테스트하는 것에 중점을 둔다.
  • 컴포넌트 렌더링과 연관되어있다면 DOM노드를 다뤄라
  • 유저가 사용하는 것 처럼 테스트를 작성해라
  • API와 유틸리티 구현은 심플하고 유연해야한다.

 

1. Queries

DOM노드를 뽑아내는데에도 우선순위가 있는데 문서에는 다음과 같은 우선순위를 제안하고 있다.

  • 누구나 액세스할 수 있는 쿼리
    • getByRole
    • getByLabelText
    • getByPlaceholderText
    • getByText
    • getByDisplayValue
  • 시맨틱 쿼리
    • getByAltText
    • getByTitle
  • Test Ids
    • getByTestId

쿼리를 쓸 때는 screen 사용을 권장

알아두면 좋을 것

 

2. User Actions

유저 액션을 다음 메소드를 통해 재현할 수 있다.

이벤트를 생성하는 방법은 다음 3가지로 나뉘는데 사용하기 편하고 가독성이 좋은 쪽으로 작성하면 좋겠다.

 

2.1 fireEvent(node: HTMLElement, event: Event) - 예제

//Example
// <button>Submit</button>
fireEvent(
  getByText(container, 'Submit'),
  new MouseEvent('click', {
    bubbles: true,
    cancelable: true,
  }),
)

 

2.2 fireEvent[eventName] - 예제

fireEvent.change(input, {target: {value: '2020-05-24'}});
fireEvent.change(getByLabelText(/username/i), {target: {value: 'a'}});

 

2.3 createEvent[eventName] - 예제

const myEvent = createEvent.click(node, {button: 2});
fireEvent(node, myEvent);

 

3. Test Structure

테스트파일은 테스트 대상과 같이 붙어있는게 테스트 후 수정이나 테스트 작성면에서 편하나 분리가 되지않아 파일이 많아지게 되는 단점 존재하나 규모가 커질수록 좋을 것 같다.

components
    ├── some1ComponentDir
      └── Component.tsx
      └── Component.test.ts
// or
~/__test__
    └── components
          └── Component.test.ts
    └── pages
          └── page.test.ts
    └── utils
          └── util.test.ts
    └── containers
          └── Container.test.ts

 

4. 테스트 작성하기

폴더구조도 중요하지만 이보다 더 중요한 것은 테스트파일 구조

일관되지 않은 테스트는 가독성이 떨어지고 변경이 생겼을 때 개발도 아닌 테스트임에도 신속히 대응하지 못하는 문제가 생길 수 있음

테스트의 구조는 Toast UI Test를 참고하여 일관되게 작성하는 것이 좋고

테스트 작성은 test code 예제, test code 예제 (kentcdodds)를 참고하여 여러 케이스들을 여러 성격으로 나누어 세세히 작성하는 것이 좋다.

테스트는 개발을 위한 방법중 하나지 개발이 아니다. 테스트를 작성하는데 많은 시간을 들이지 않고 간단하게 작성하되 테스트케이스에 대해서는 시간을 들여도 좋다고 생각한다.

 

4.0 무엇을 테스트할 것인가 ?

수정사항이 있음을 염두에두고 세세하게 하지 않는 것이 좋다. 애써만든 테스트코드가 무의미해지기 때문이다.

변하지 않을 것, 예를들어 버튼을 클릭했을 때 어떤 액션이 일어날지를 먼저 보는 것이 좋다. - testing library에서 추구하는 원칙과 동일하다. 액션에 중점을 두자

 

4.1 어떻게 작성할 것인가 ?

테스트는 명세를 코드로 번역해놓는 작업이자 잘 동작하는지 개발자가 확신을 갖기 위한 작업이다.

가독성이 좋아야하고 의도가 명확해야한다.

공통 로직과 Mock 등은 분리하자.

테스트코드는 G-W-T 순서로 작성한다.

테스트코드도 코드이므로 가독성을 고려하며 작성 - 나만 보는 코드가 아님

  • Given : 검증할 대상들을 준비하는 단계 - 랜더링 시킨다.
  • When : 검증 대상을 실행하는 단계 - 유저가 클릭을 해본다.
  • Then : 검증하는 단계 - 클릭 이후의 값이 일치하는지 확인한다.
test('should $1', () => {
  // Given
  const data = $4
​
  // When
  const result = $3
​
  // Then
  expect(result).toEqual($2)
})
//출처 https://jbee.io/react/testing-2-react-testing/

테스트 작성에 익숙하지 않다면 위 스니펫을 사용해보는 것도 좋다.

 

4.2 테스트코드 구조

테스트코드 구조 예시

팀 간에 템플릿화 해놓는게 좋다

test('should $1', () => {
  // Given
  const data = $4
​
  // When
  const result = $3
​
  // Then
  expect(result).toEqual($2)
})
//출처 https://jbee.io/react/testing-2-react-testing/

 

4.3 MSW + Testing library

docs 예제

msw를 사용할 때 handler에다가 사용할 mock api를 등록해주어하는데 이 handler는 사용할 api들을 원소로 갖는 배열의 형태를 가진다.

import { rest } from "msw";
​
export const handlers = [
  rest.post("http://localhost:8080/todos/", (req, res, ctx) => {
    return res(ctx.status(200), ctx.json({ data: "hello" }));
  }),
​
  rest.post("/", (req, res, ctx) => res(ctx.json({}))),
];
​
export default handlers;

각 Auth, Project 등 배열을 이루는 원소들은 각각이 가진 성격이 있다.

언제든지 도메인별로 분리될 수 있도록 구현하기로 했고 구조는 다음과 같다.

​
export const Auth = {
  login: ()=>{},
  logout: ()=>{}
};
​
​
export const Project = {
  getList: ()=>{},
  deleteProject: ()=>{}
};
​
const intergrateAPIs = (...apis) => {
  return apis.map((apiObject) => Object.keys(apiObject).map((apiKey) => apiObject[apiKey])).flat();
}
​
export const handler = [
  ...intergrateAPIs(Auth, Project)
];
​
console.log(handler);
/*
[
  ƒ login(),
  ƒ logout(),
  ƒ getList(),
  ƒ deleteProject()
]
*/
​

추가될 API들을 갖고있는 Object를 만들고 intergrateAPIs에 인자로 넣어주면 handler에 등록된다.

파일로 분리되더라도 Object만 import해서 넣어주면 되므로 확장, 관리에도 용이할 것으로 기대된다.

 

결론

유저액션을 중점으로 컴포넌트 테스트를 할 때 테스팅라이브러리를 사용하고

로직검증을 할 땐 jest에서 제공하는 mock함수를 사용하자.

jest로도 둘 다 할 수는 있지만 유저 행동중심으로 테스팅하고자 할 땐 RTL을 사용하자

기본적으로 jest는 CRA할 때 설정이되어서 *.test.ts 에서 jest에서 제공하는 mock함수를 쓸 수 있다.

Testing library로 작성되는 테스트코드는 유저중심으로 간결하게 규격에 맞게 작성하도록 한다.

테스트는 뭘 테스트할지 정해진 게 없다. 매우 간단한거부터 조금은 복잡한 부분까지 검증이 필요한 대상이면 수행하는게 맞는 것 같다.

예를들어 B페이지가서 어떤 버튼 클릭 시 데이터를 fetch해온다거나 색이 바뀌었다거나하는 테스트를 개발자도구를 열고, 콘솔을 찍지 않아도 test파일 내에서 알 수 있어서 유저의 액션 중심으로 테스트 진행시 이러한 반복 작업이 많이 덜어질 것으로 기대된다.

반응형

'프로그래밍 > React' 카테고리의 다른 글

브라우저에서 대용량 파일 block없이 읽기  (0) 2022.10.28
React Query - useQuery  (0) 2022.09.14
요즘 핫한 React Query  (0) 2022.09.14
useLayoutEffect(2) SSR  (0) 2022.07.10
Image progressive load  (0) 2022.06.14
댓글
반응형
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
농담곰의 고군분투 개발기