๐ŸŽฏ

testing

๐ŸŽฏSkill

from dalestudy/skills

VibeIndex|
What it does

Guides developers in writing robust React component tests using Testing Library, prioritizing user-centric, accessibility-focused testing approaches.

๐Ÿ“ฆ

Part of

dalestudy/skills(7 items)

testing

Installation

Quick InstallInstall with npx
npx skills add dalestudy/skills
Quick InstallInstall with npx
npx skills add dalestudy/skills --skill bun
Quick InstallInstall with npx
npx skills add dalestudy/skills --skill git
Quick InstallInstall with npx
npx skills add dalestudy/skills --skill github-actions
Quick InstallInstall with npx
npx skills add dalestudy/skills --skill storybook

+ 4 more commands

๐Ÿ“– Extracted from docs: dalestudy/skills
46Installs
-
AddedFeb 4, 2026

Skill Details

SKILL.md

"React Testing Library ๋ฐ Vitest ๊ธฐ๋ฐ˜ ํ…Œ์ŠคํŒ… ๋ชจ๋ฒ” ๊ด€๋ก€. ๋‹ค์Œ ์ƒํ™ฉ์—์„œ ์‚ฌ์šฉ: (1) .test.tsx, .test.ts, .spec.tsx, .spec.ts ํŒŒ์ผ ์ž‘์—… ์‹œ, (2) ์ปดํฌ๋„ŒํŠธ ํ…Œ์ŠคํŠธ ์ž‘์„ฑ ๋˜๋Š” ๋ฆฌํŒฉํ† ๋ง ์‹œ, (3) 'test', 'testing', 'vitest', 'RTL', 'getByRole', 'userEvent', 'waitFor', 'expect' ํ‚ค์›Œ๋“œ ํฌํ•จ ์ž‘์—… ์‹œ, (4) ๋น„๋™๊ธฐ ๋กœ์ง ๋˜๋Š” ์‚ฌ์šฉ์ž ์ƒํ˜ธ์ž‘์šฉ ํ…Œ์ŠคํŠธ ์ž‘์„ฑ ์‹œ"

Overview

# 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();

```

์ „์ฒด ์•ˆํ‹ฐํŒจํ„ด ๋ชฉ๋ก: [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 ํ•ธ๋“ค๋Ÿฌ ๋ฐ ์„œ๋ฒ„ ์„ค์ •