> ## Documentation Index
> Fetch the complete documentation index at: https://grit03.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# [CH5] 리액트 테스팅

## 테스트

![](https://mintlify.s3.us-west-1.amazonaws.com/grit03/images/react/test/%EB%B0%9C%EC%A0%84%EB%90%9C%20%ED%85%8C%EC%8A%A4%ED%8A%B8%20%ED%94%BC%EB%9D%BC%EB%AF%B8%EB%93%9C.png)

발전된 테스트 피라미드

## 단위 테스팅

### 테스트 그룹 묶기

`describe` 함수를 이용하여 테스트 그룹을 묶을 수 있다.

* 테스트를 묶어서 가독성이 향상되면, **어떤 기능을 검증하고자 하는지 이해**하는데 크게 도움이 된다.
* `describe` 블록을 중첩하여, 테스트 스위트를 구조화할 수 있다.

> [!NOTE]
>
> **테스트 스위트(Suite)란?**
>
> 테스트 케이스의 집합

## 리액트 컴포넌트 테스트

Jest 단독으로도 리액트 애플리케이션을 테스트 할 수 있지만, 리액트 테스팅 라이브러리(React Testing Libraray)와 같은 전용 라이브러리를 사용하는 것보다 코드가 번잡해질 수 있다.

> [!NOTE]
>
> **리액트 테스팅 라이브러리 (React Testing Libraray)**
>
> * **리액트 컴포넌트 테스팅**을 위한 강력한 경량 라이브러리
> * Jest 기반으로 **리액트 컴포넌트를 손쉽게 다루는** 유틸리티 함수가 추가됨
> * 테스트 코드를 사용자의 소프트웨어 사용 행태와 유사하게 작성하기를 권장\
>   (→ 세부 구현에 대한 것이 아닌 기능에 대한 테스트를 하는 것)

```tsx theme={null}
import React from "react";
import "@testing-library/jest-dom";
import { render, screen } from "@testing-library/react";

import { Section } from "../component/Section";

describe("Section", () => {
  it("renders a section with heading and content", () => {
    render(<Section heading="Basic" content="Hello world" />);

    expect(screen.getByText("Basic")).toBeInTheDocument();
    expect(screen.getByText("Hello world")).toBeInTheDocument();
  });
});
```

## 통합 테스트

통합 테스트는 **여러 단위 코드들 사이의 상호작용**에 대해 검증한다.

> [!NOTE]
>
> **통합 테스트에서 상호작용이란?**
>
> * 컴포넌트 간의 상호작용
> * 서버와 클라이언트의 상호작용

통합 테스트의 목적은 **시스템의 각기 다른 부분들을 통합하면서 생길 수 있는 문제들을 발견**하는 것

* **UI 컴포넌트 레벨에서의 통합 테스트**:\
  2개의 개별 컴포넌트 사이의 상호작용을 테스트해서 함께 올바르게 동작하는지 확인
* **프론트엔드 코드와 백엔드 서비스가 자연스럽게 연결되는지 확인하고자 한다면?**:\
  애플리케이션의 다른 계층들이 함께 올바르게 동작하는지 보는 것이기 때문에, 이것 역시 통합 테스트

### 실제 테스트 구현 예제

![](https://mintlify.s3.us-west-1.amazonaws.com/grit03/images/react/test/%ED%86%B5%ED%95%A9%20%ED%85%8C%EC%8A%A4%ED%8A%B8%20%EC%82%AC%EB%A1%80%20%EC%98%88%EC%8B%9C.png)

법적인 정보를 담고 있는 긴 텍스트와 사용자 동의 여부를 표시하는 체크 박스가 있다.

1. **\[Next] 버튼**은 처음에 **비활성화 버튼**이다.
2. 사용자가 **동의 체크 박스를 체크**하면, **\[Next] 버튼은 활성화**되어 다음으로 넘어갈 수 있다.

```tsx highlight={3,5} theme={null}
import React from "react";
import "@testing-library/jest-dom";
import { render, screen } from "@testing-library/react";
import { TermsAndConditions } from "../component/TermsAndConditions";
import userEvent from "@testing-library/user-event";

describe("Terms and Conditions", () => {
  it("Enables the next button upon accepting terms and conditions", async () => {
    render(<TermsAndConditions />);
    const button = screen.getByText("Next");
    expect(button).toBeDisabled();

    const checkbox = screen.getByRole("checkbox");

    await userEvent.click(checkbox);

    expect(button).toBeEnabled();
  });
});
```

1. `describe` 함수는 `TermsAndConditions` 컴포넌트와 관련된 모든 테스트들을 테스트 스위트라는 그룹으로 묶는다.

2. 테스트 스위트 안에는 `it`으로 표시한 단일 테스트 케이스가 있다.

   * `Enables the next button upon accepting terms and conditions`\
     (이용약관과 조건에 동의하면 \[NEXT] 버튼을 활성화하기) 설명이 적힌 테스트가 있다.

3. **초기에는 `TermsAndConditions` 컴포넌트를 표시하기 위해 `render` 함수를 호출한다.**
   * `render`함수는 컴포넌트가 예상대로 동작하는지 평가하기 위해, 다양한 방법으로 조회할 수 있는 **일련의 출력**이나, **렌더링 결과물을 생성**한다.

4. `screen.getByText` 함수를 이용하여, 페이지 안에서 검색한 요소를 반환받을 수 있다.
   * 이를 이용하여, **\[Next]라는 텍스트를 통해 버튼을 찾는다.**

5. `expect(button).toBeDisabled()`를 통해 찾은 버튼 요소가 비활성화 상태인지 확인할 수 있다.

6. `screen.getByRole` 함수를 통해, 체크박스를 `checkbox`라는 역할로 요소를 찾을 수 있다.

7. `act` 함수로 `userEvent.click` 함수를 통해, 사용자의 클릭 이벤트를 시뮬레이션 할 수 있다.
   * `act` 함수는 **동작으로 인한 모든 UI 업데이트가 완료될 때가지 기다린 후**에 다음 단계로 넘어간다.
   * 따라서, 테스트 과정에서 업데이트 된 상태를 정확하게 확인할 수 있따.

8. `expect(button).toBeEnabled()`를 통해 **체크박스를 클릭한 후 버튼이 활성화 되는 것을 확인**할 수 있다.

실제 구현된 컴포넌트를 보면 아래와 같다.

```tsx theme={null}
//... 다른 내부 컴포넌트 구현
const TermsAndConditions = () => {
  return (
    <div>
      <h2>Terms and Conditions</h2>
      <TheLegalContent />
      <UserConsent />
    </div>
  );
};
export { TermsAndConditions };
```

실제 외부로 노출되는 컴포넌트는 `TermsAndCondtions` 하나 이다. 그리고 테스트에서 `jsdom` 환경에서 클릭 이벤트를 시작하기 위해 `userEvent.click`을 사용한다.

> 중요한 것은 개별 리액트 컴포넌트에 대한 단위 테스트를 수행하는 것이 아니라, **DOM 요소간의 상호 작용에 초점을 맞추는 것**\
> → 이를 위해, 브라우저를 사용하지 않고, 유사한 모든 기능을 지원하는 jsdom을 사용한다.\
> **jsdom은 메모리 위에서 동작하는 헤드리스 브라우저이다.**

> [!TIP]
>
> **`jsdom`은 자바스크립트 기반의 헤드리스 브라우저이다.**\
> 실제 웹 브라우저와 유사한 테스팅 환경을 만들 때 사용한다. 자바스크립트로 HTML, DOM, CSS와 같은 웹 표준을 구현한다.
>
> **브라우저는 DOM을 제공**하지만, `Node.js` 환경에서 `jest`와 같은 프레임워크를 사용할 때는 **DOM을 기본적으로 제공하지 않는다.**
>
> * `jsdom`은 가상의 DOM을 제공하기 때문에, 테스트가 `Node.js` 환경에서 실행되더라도 브라우저와 유사한 환경을 만들어준다.
> * `jsdom`은 브라우저를 띄우지 않고도 DOM과의 상호작용을 묘사할 수 있다.

> [!NOTE]
>
> **AI 답변**
>
> 'Headless'<sup>
> [1](https://puddingcamp.com/topics/headless-component-in-puddingcamp#:~:text=UI%20%EC%BB%B4%ED%8F%AC%EB%84%8C%ED%8A%B8%EB%8A%94%20%ED%81%AC%EA%B2%8C%20%EB%A1%9C%EC%A7%81%EA%B3%BC%20%EC%83%81%ED%83%9C%2C%20%EA%B7%B8%EB%A6%AC%EA%B3%A0%20UI,Component%EB%8A%94%20UI%EA%B0%80%20%EC%97%86%EC%9D%B4%20%EB%A1%9C%EC%A7%81%EC%9D%B4%EB%82%98%20%EC%83%81%ED%83%9C%20%EA%B4%80%EB%A6%AC%EB%A7%8C%EC%9D%84%20%EB%8B%B4%EB%8B%B9%ED%95%98%EB%8A%94)</sup> <sup>[2](https://www.howdy-mj.me/design/headless-components)</sup>는 '머리가 없는'이라는 뜻으로, **UI(사용자 인터페이스)를
> 직접 제어하지 않는 상태를 의미**합니다.\
> 이는 주로 소프트웨어 개발 분야에서 기능(로직)과 화면(UI)을 분리한 구조나, GUI 없이 동작하는 시스템을 가리킬 때
> 사용됩니다.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.