Contents
see ListJest와 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("이메일"), "test@test.com");
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: "test@test.com",
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로 초기화)