vitest について
vitestは、VitestとReact Testing Libraryでユニットテストと統合テストを書くためのスキルです。テスト構成、クエリ、操作、非同期アサーション、モック、ファイル整理の規則を示します。
主な機能
- 01Given/When/Thenのテスト構成
- 02振る舞い別のdescribeブロック
- 03アクセシブルなクエリの優先順位
- 04userEventによる操作指針
- 05非同期アサーションとモックの規則
活用シーン
- 01Reactコンポーネントのテスト
- 02ユニットテストと統合テストの作成
- 03テストファイルの整理とレビュー
インストール
コマンドをコピーしてターミナルで実行します。
$
npx skills add https:/ / github.com/ prowler-cloud/ prowler --skill vitestインストール後、Claude Code で/vitestを入力して読み込まれたか確認します。
詳細
E2E テストの場合:
prowler-test-uiスキル(Playwright)を使用します。 このスキルでは、Vitest と React Testing Library を使ったユニットテスト/統合テストを扱います。
テスト構成(必須)
コメントを使ったGiven/When/Then(AAA)パターンを使用します。
it("should update user name when form is submitted", async () => {
// Given - Arrange
const user = userEvent.setup();
const onSubmit = vi.fn();
render(<UserForm onSubmit={onSubmit} />);
// When - Act
await user.type(screen.getByLabelText(/name/i), "John");
await user.click(screen.getByRole("button", { name: /submit/i }));
// Then - Assert
expect(onSubmit).toHaveBeenCalledWith({ name: "John" });
});
Describe ブロックの構成
describe("ComponentName", () => {
describe("when [condition]", () => {
it("should [expected behavior]", () => {});
});
});
メソッドではなく、動作ごとにグループ化します。
クエリの優先順位(必須)
| 優先順位 | クエリ | 使用例 |
|---|---|---|
| 1 | getByRole | ボタン、入力欄、見出し |
| 2 | getByLabelText | フォームフィールド |
| 3 | getByPlaceholderText | ラベルのない入力欄 |
| 4 | getByText | 静的テキスト |
| 5 | getByTestId | 最終手段のみ |
// ✅ GOOD
screen.getByRole("button", { name: /submit/i });
screen.getByLabelText(/email/i);
// ❌ BAD
container.querySelector(".btn-primary");
fireEvent より userEvent を優先(必須)
// ✅ ALWAYS use userEvent
const user = userEvent.setup();
await user.click(button);
await user.type(input, "hello");
// ❌ NEVER use fireEvent for interactions
fireEvent.click(button);
非同期テストのパターン
// ✅ findBy for elements that appear async
const element = await screen.findByText(/loaded/i);
// ✅ waitFor for assertions
await waitFor(() => {
expect(screen.getByText(/success/i)).toBeInTheDocument();
});
// ✅ ONE assertion per waitFor
await waitFor(() => expect(mockFn).toHaveBeenCalled());
await waitFor(() => expect(screen.getByText(/done/i)).toBeVisible());
// ❌ NEVER multiple assertions in waitFor
await waitFor(() => {
expect(mockFn).toHaveBeenCalled();
expect(screen.getByText(/done/i)).toBeVisible(); // Slower failures
});
モック
// Basic mock
const handleClick = vi.fn();
// Mock with return value
const fetchUser = vi.fn().mockResolvedValue({ name: "John" });
// Always clean up
afterEach(() => {
vi.restoreAllMocks();
});
vi.spyOn と vi.mock
| メソッド | 使用する場面 |
|---|---|
vi.spyOn | 置き換えずに監視する場合(推奨) |
vi.mock | モジュール全体を置き換える場合(控えめに使用) |
よく使うマッチャー
// Presence
expect(element).toBeInTheDocument();
expect(element).toBeVisible();
// State
expect(button).toBeDisabled();
expect(input).toHaveValue("text");
expect(checkbox).toBeChecked();
// Content
expect(element).toHaveTextContent(/hello/i);
expect(element).toHaveAttribute("href", "/home");
// Functions
expect(fn).toHaveBeenCalledWith(arg1, arg2);
expect(fn).toHaveBeenCalledTimes(2);
テストしてはいけないもの
// ❌ Internal state
expect(component.state.isLoading).toBe(true);
// ❌ Third-party libraries
expect(axios.get).toHaveBeenCalled();
// ❌ Static content (unless conditional)
expect(screen.getByText("Welcome")).toBeInTheDocument();
// ✅ User-visible behavior
expect(screen.getByRole("button")).toBeDisabled();
ファイル構成
components/
├── Button/
│ ├── Button.tsx
│ ├── Button.test.tsx # Co-located
│ └── index.ts
コマンド
pnpm test # Watch mode
pnpm test:run # Single run
pnpm test:coverage # With coverage
pnpm test Button # Filter by name
コメント
Markdown 対応