---
id: DaleStudy/skills/testing
version: "4c55af14"
license: MIT
install: manual
updated: 2026-04-07
---
# testing — Master React Testing Library fundamentals through accessibility-first query selection, user-centric event simulation, and async handling patterns. Learn when to use getByRole, userEvent, and findBy queries, plus common pitfalls to avoid in component testing.
Publisher: DaleStudy · Stars: 4 · Updated: 2026-04-07
Install (manual): `git clone https://github.com/DaleStudy/skills`
## SKILL.md
# Testing Library
React Testing Library 기반 테스트 작성 모범 관례 및 안티패턴 회피 가이드.
## 핵심 원칙
Testing Library의 철학: **사용자가 사용하는 방식대로 테스트하라**
1. **접근성 기반 쿼리 우선** - 실제 사용자가 요소를 찾는 방식 사용
2. **구현 세부사항 테스트 금지** - 컴포넌트 내부 상태/메서드 직접 접근 지양
3. **실제 사용자 행동 시뮬레이션** - userEvent 사용, fireEvent 지양
4. **비동기 처리 명시적 대기** - waitFor, findBy 활용
## 쿼리 우선순위
Testing Library는 다양한 쿼리를 제공하지만, **접근성과 사용자 경험을 반영하는 순서**로 사용해야 함.
### 권장 쿼리 순서 (높음 → 낮음)
1. **`getByRole`** (최우선) - 스크린 리더가 인식하는 방식
2. **`getByLabelText`** - 폼 요소 (label과 연결된 input)
3. **`getByPlaceholderText`** - placeholder가 명확한 경우
4. **`getByText`** - 텍스트 콘텐츠로 검색
5. **`getByDisplayValue`** - 현재 입력된 값으로 검색 (폼 요소)
6. **`getByAltText`** - 이미지 alt 속성
7. **`getByTitle`** - title 속성 (tooltip 등)
8. **`getByTestId`** (최후 수단) - 다른 방법이 불가능할 때만 사용
**상세 가이드**: [references/query-priority.md](references/query-priority.md)
## 사용자 상호작용 테스트
### userEvent 사용 (권장)
```typescript
import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
test('사용자가 폼을 제출할 수 있다', async () => {
const user = userEvent.setup();
render();
await user.type(screen.getByRole('textbox', { name: /이메일/i }), 'user@example.com');
await user.type(screen.getByLabelText(/비밀번호/i), 'password123');
await user.click(screen.getByRole('button', { name: /로그인/i }));
expect(await screen.findByText(/환영합니다/i)).toBeInTheDocument();
});
```
**핵심**:
- `userEvent.setup()` 호출 후 사용
- 모든 user 메서드는 `await` 필수
- 실제 브라우저 이벤트 순서 재현 (focus, keydown, keyup 등)
### fireEvent 지양
```typescript
// ❌ 나쁜 예 - fireEvent 사용
fireEvent.click(button);
fireEvent.change(input, { target: { value: "text" } });
// ✅ 좋은 예 - userEvent 사용
await user.click(button);
await user.type(input, "text");
```
**상세 가이드**: [references/user-events.md](references/user-events.md)
## 비동기 처리
### findBy 쿼리 (권장)
```typescript
// ✅ 좋은 예 - findBy 사용
const successMessage = await screen.findByText(/저장되었습니다/i);
expect(successMessage).toBeInTheDocument();
```
`findBy` = `getBy` + `waitFor` 조합 (자동으로 요소 나타날 때까지 대기)
### waitFor 사용
```typescript
// 복잡한 비동기 검증
await waitFor(() => {
expect(screen.getByRole("alert")).toHaveTextContent("성공");
});
// 여러 조건 검증
await waitFor(() => {
expect(mockFn).toHaveBeenCalledTimes(1);
expect(screen.queryByText(/로딩 중/i)).not.toBeInTheDocument();
});
```
### 안티패턴
```typescript
// ❌ 나쁜 예 - 임의의 timeout
await new Promise((resolve) => setTimeout(resolve, 1000));
// ❌ 나쁜 예 - act() 수동 사용 (보통 불필요)
await act(async () => {
// ...
});
// ✅ 좋은 예 - findBy 또는 waitFor
await screen.findByText(/완료/i);
```
**상세 가이드**: [references/async-patterns.md](references/async-patterns.md)
## 자주 하는 실수
### 1. 구현 세부사항 테스트
```typescript
// ❌ 나쁜 예 - 내부 상태 접근
expect(component.state.isOpen).toBe(true);
wrapper.find(".internal-class").simulate("click");
// ✅ 좋은 예 - 사용자 관점 검증
expect(screen.getByRole("dialog")).toBeVisible();
await user.click(screen.getByRole("button", { name: /열기/i }));
```
### 2. container 쿼리 사용
```typescript
// ❌ 나쁜 예 - container.querySelector
const { container } = render();
const button = container.querySelector('.my-button');
// ✅ 좋은 예 - screen 쿼리
const button = screen.getByRole('button', { name: /제출/i });
```
### 3. 불필요한 waitFor
```typescript
// ❌ 나쁜 예 - 동기 요소에 waitFor
await waitFor(() => {
expect(screen.getByText("Hello")).toBeInTheDocument();
});
// ✅ 좋은 예 - 동기 요소는 즉시 검증
expect(screen.getByText("Hello")).toBeInTheDocument();
```
### 4. getBy* + toBeInTheDocument() 제거
`getBy*`는 요소를 못 찾으면 throw하므로 `toBeInTheDocument()`는 기술적으로 중복이다. 하지만 **제거하지 마라** — 리팩토링 후 남은 쿼리가 아니라 의도적인 존재 검증임을 코드 독자에게 전달하는 역할을 한다.
```typescript
// ❌ 나쁜 예 - assertion 없이 쿼리만 남김
screen.getByRole("button", { name: /제출/i });
// ✅ 좋은 예 - 명시적 assertion으로 의도 전달
expect(screen.getByRole("button", { name: /제출/i })).toBeInTheDocument();
```
단, 존재 검증이 아닌 다른 속성을 검증할 때는 `toBeInTheDocument()`를 추가할 필요 없다:
```typescript
// ✅ 좋은 예 - 다른 matcher로 충분
expect(screen.getByRole("button", { name: /제출/i })).toBeDisabled();
// ❌ 나쁜 예 - 중복 assertion
expect(screen.getByRole("button", { name: /제출/i })).toBeInTheDocument();
expect(screen.getByRole("button", { name: /제출/i })).toBeDisabled();
```
**전체 안티패턴 목록**: [references/common-mistakes.md](references/common-mistakes.md)
## Vitest + MSW 설정
테스트 환경 설정이 필요한 경우 다음 템플릿 참조:
- `assets/vitest.config.ts` - Vitest 설정 (React 플러그인, 커버리지 포함)
- `assets/test-setup.ts` - Vitest 글로벌 설정
- `assets/msw-setup.ts` - MSW 핸들러 및 서버 설정
[View on SkillFed](https://skillfed.io/DaleStudy/skills/testing) · [View on GitHub](https://github.com/DaleStudy/skills)