testing
57
总安装量
57
周安装量
#3834
全站排名
安装命令
npx skills add https://github.com/dalestudy/skills --skill testing
Agent 安装分布
claude-code
57
opencode
39
antigravity
39
cursor
39
neovate
38
Skill 文档
Testing Library
React Testing Library ê¸°ë° í ì¤í¸ ìì± ëª¨ë² ê´ë¡ ë° ìí°í¨í´ íí¼ ê°ì´ë.
íµì¬ ìì¹
Testing Libraryì ì² í: ì¬ì©ìê° ì¬ì©íë ë°©ìëë¡ í ì¤í¸íë¼
- ì ê·¼ì± ê¸°ë° ì¿¼ë¦¬ ì°ì – ì¤ì ì¬ì©ìê° ìì를 ì°¾ë ë°©ì ì¬ì©
- 구í ì¸ë¶ì¬í í ì¤í¸ ê¸ì§ – ì»´í¬ëí¸ ë´ë¶ ìí/ë©ìë ì§ì ì ê·¼ ì§ì
- ì¤ì ì¬ì©ì íë ì뮬ë ì´ì – userEvent ì¬ì©, fireEvent ì§ì
- ë¹ë기 ì²ë¦¬ ëª ìì ë기 – waitFor, findBy íì©
쿼리 ì°ì ìì
Testing Libraryë ë¤ìí 쿼리를 ì ê³µíì§ë§, ì ê·¼ì±ê³¼ ì¬ì©ì ê²½íì ë°ìíë ììë¡ ì¬ì©í´ì¼ í¨.
ê¶ì¥ 쿼리 ìì (ëì â ë®ì)
getByRole(ìµì°ì ) – ì¤í¬ë¦° 리ëê° ì¸ìíë ë°©ìgetByLabelText– í¼ ìì (labelê³¼ ì°ê²°ë input)getByPlaceholderText– placeholderê° ëª íí ê²½ì°getByText– í ì¤í¸ ì½í ì¸ ë¡ ê²ìgetByDisplayValue– íì¬ ì ë ¥ë ê°ì¼ë¡ ê²ì (í¼ ìì)getByAltText– ì´ë¯¸ì§ alt ìì±getByTitle– title ìì± (tooltip ë±)getByTestId(ìµí ìë¨) – ë¤ë¥¸ ë°©ë²ì´ ë¶ê°ë¥í ëë§ ì¬ì©
ìì¸ ê°ì´ë: references/query-priority.md
ì¬ì©ì ìí¸ìì© í ì¤í¸
userEvent ì¬ì© (ê¶ì¥)
import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
test('ì¬ì©ìê° í¼ì ì ì¶í ì ìë¤', async () => {
const user = userEvent.setup();
render(<LoginForm />);
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 ì§ì
// â ëì ì - fireEvent ì¬ì©
fireEvent.click(button);
fireEvent.change(input, { target: { value: "text" } });
// â
ì¢ì ì - userEvent ì¬ì©
await user.click(button);
await user.type(input, "text");
ìì¸ ê°ì´ë: references/user-events.md
ë¹ë기 ì²ë¦¬
findBy 쿼리 (ê¶ì¥)
// â
ì¢ì ì - findBy ì¬ì©
const successMessage = await screen.findByText(/ì ì¥ëììµëë¤/i);
expect(successMessage).toBeInTheDocument();
findBy = getBy + waitFor ì¡°í© (ìëì¼ë¡ ìì ëíë ëê¹ì§ ë기)
waitFor ì¬ì©
// ë³µì¡í ë¹ë기 ê²ì¦
await waitFor(() => {
expect(screen.getByRole("alert")).toHaveTextContent("ì±ê³µ");
});
// ì¬ë¬ ì¡°ê±´ ê²ì¦
await waitFor(() => {
expect(mockFn).toHaveBeenCalledTimes(1);
expect(screen.queryByText(/ë¡ë© ì¤/i)).not.toBeInTheDocument();
});
ìí°í¨í´
// â ëì ì - ììì timeout
await new Promise((resolve) => setTimeout(resolve, 1000));
// â ëì ì - act() ìë ì¬ì© (ë³´íµ ë¶íì)
await act(async () => {
// ...
});
// â
ì¢ì ì - findBy ëë waitFor
await screen.findByText(/ìë£/i);
ìì¸ ê°ì´ë: references/async-patterns.md
ì주 íë ì¤ì
1. 구í ì¸ë¶ì¬í í ì¤í¸
// â ëì ì - ë´ë¶ ìí ì ê·¼
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 쿼리 ì¬ì©
// â ëì ì - container.querySelector
const { container } = render(<MyComponent />);
const button = container.querySelector('.my-button');
// â
ì¢ì ì - screen 쿼리
const button = screen.getByRole('button', { name: /ì ì¶/i });
3. ë¶íìí waitFor
// â ëì ì - ë기 ììì waitFor
await waitFor(() => {
expect(screen.getByText("Hello")).toBeInTheDocument();
});
// â
ì¢ì ì - ë기 ììë ì¦ì ê²ì¦
expect(screen.getByText("Hello")).toBeInTheDocument();
ì ì²´ ìí°í¨í´ 목ë¡: references/common-mistakes.md
Vitest + MSW ì¤ì
í ì¤í¸ íê²½ ì¤ì ì´ íìí ê²½ì° ë¤ì í í릿 참조:
assets/vitest.config.ts– Vitest ì¤ì (React íë¬ê·¸ì¸, 커ë²ë¦¬ì§ í¬í¨)assets/test-setup.ts– Vitest ê¸ë¡ë² ì¤ì assets/msw-setup.ts– MSW í¸ë¤ë¬ ë° ìë² ì¤ì