Contents
see List작성일 2025. 11. 30.
Jest와 React Testing Library
Jest는 Facebook에서 만든 JavaScript 테스트 프레임워크이고, React Testing Library는 사용자 관점에서 React 컴포넌트를 테스트하는 라이브러리입니다. 함께 사용하면 효과적인 프론트엔드 테스트를 작성할 수 있습니다.
언제 사용하나요?
- React 컴포넌트 렌더링 및 동작 테스트
- 사용자 인터랙션 시뮬레이션
- API 호출 모킹
- CI/CD 파이프라인에서 자동화된 테스트
설치
# Create React App에는 이미 포함됨
npm install --save-dev jest @testing-library/react @testing-library/jest-dom @testing-library/user-event기본 테스트 구조
// Button.test.js
import { render, screen, fireEvent } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import Button from "./Button";
describe("Button 컴포넌트", () => {
it("버튼 텍스트가 렌더링된다", () => {
render(<Button>클릭</Button>);
expect(screen.getByText("클릭")).toBeInTheDocument();
});
it("클릭 시 onClick이 호출된다", async () => {
const handleClick = jest.fn();
render(<Button onClick={handleClick}>클릭</Button>);
await userEvent.click(screen.getByRole("button"));
expect(handleClick).toHaveBeenCalledTimes(1);
});
});쿼리 메서드
// getBy - 요소가 없으면 에러
screen.getByText("Hello");
screen.getByRole("button");
screen.getByLabelText("이메일");
screen.getByPlaceholderText("입력하세요");
screen.getByTestId("submit-btn");
// queryBy - 요소가 없으면 null (없음 확인용)
expect(screen.queryByText("에러")).not.toBeInTheDocument();
// findBy - 비동기, Promise 반환
const item = await screen.findByText("로딩 완료");
// 복수형 - 여러 요소
const buttons = screen.getAllByRole("button");사용자 이벤트 테스트
import userEvent from "@testing-library/user-event";
it("폼 입력 및 제출 테스트", async () => {
const user = userEvent.setup();
const onSubmit = jest.fn();
render(<LoginForm onSubmit={onSubmit} />);
// 입력
await user.type(screen.getByLabelText("이메일"), "[email protected]");
await user.type(screen.getByLabelText("비밀번호"), "password123");
// 체크박스
await user.click(screen.getByLabelText("자동 로그인"));
// 선택 박스
await user.selectOptions(
screen.getByRole("combobox"),
["option1"]
);
// 제출
await user.click(screen.getByRole("button", { name: "로그인" }));
expect(onSubmit).toHaveBeenCalledWith({
email: "[email protected]",
password: "password123",
});
});비동기 테스트
import { render, screen, waitFor } from "@testing-library/react";
it("데이터 로딩 후 목록이 표시된다", async () => {
render(<UserList />);
// 로딩 상태 확인
expect(screen.getByText("로딩 중...")).toBeInTheDocument();
// 데이터 로드 대기
await waitFor(() => {
expect(screen.getByText("사용자1")).toBeInTheDocument();
});
// 또는 findBy 사용
const user = await screen.findByText("사용자1");
expect(user).toBeInTheDocument();
});API 모킹
// api.js 모킹
jest.mock("./api");
import { fetchUsers } from "./api";
beforeEach(() => {
fetchUsers.mockResolvedValue([
{ id: 1, name: "홍길동" },
{ id: 2, name: "김철수" },
]);
});
it("API에서 사용자 목록을 가져온다", async () => {
render(<UserList />);
await screen.findByText("홍길동");
expect(fetchUsers).toHaveBeenCalledTimes(1);
});
// fetch 직접 모킹
global.fetch = jest.fn(() =>
Promise.resolve({
json: () => Promise.resolve({ data: "test" }),
})
);커스텀 훅 테스트
import { renderHook, act } from "@testing-library/react";
import useCounter from "./useCounter";
it("useCounter 훅 테스트", () => {
const { result } = renderHook(() => useCounter(0));
expect(result.current.count).toBe(0);
act(() => {
result.current.increment();
});
expect(result.current.count).toBe(1);
});스냅샷 테스트
it("컴포넌트 스냅샷 일치", () => {
const { container } = render(<Button>테스트</Button>);
expect(container).toMatchSnapshot();
});주요 매처
// jest-dom 매처
expect(element).toBeInTheDocument();
expect(element).toBeVisible();
expect(element).toBeDisabled();
expect(element).toHaveClass("active");
expect(element).toHaveValue("test");
expect(element).toHaveAttribute("type", "submit");
expect(element).toHaveTextContent("Hello");팁
- 구현 상세보다 사용자 행동 테스트
- getByRole을 우선 사용 (접근성 향상)
- act() 경고 시 waitFor 사용
- 테스트 간 격리 (beforeEach로 초기화)