frontend-testing-best-practices について
Testing Best Practicesは、フロントエンドプロジェクトのテスト作成、レビュー、リファクタリングに役立つ指針です。コンポーネント内部ではなくユーザーから見える動作を検証し、E2Eテストを優先してモックを抑える方針です。
主な機能
- 01E2Eテストを優先
- 02単体テストは純粋関数に限定
- 03モックを最小限に抑制
- 04ユーザーに見える動作を検証
- 05アクセシブルなテストセレクターを使用
- 06E2Eテストを指定ディレクトリに配置
活用シーン
- 01新機能に適したテスト種別の判断
- 02ユーザーフローのE2Eテスト作成
- 03Reactのテストコードレビュー
- 04モックに依存するテストのリファクタリング
インストール
コマンドをコピーしてターミナルで実行します。
$
npx skills add https:/ / github.com/ sergiodxa/ agent-skills --skill frontend-testing-best-practicesインストール後、Claude Code で/frontend-testing-best-practicesを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
rules/avoid-component-tests.mde2e-selectors.mde2e-test-structure.mdminimize-mocking.mdprefer-e2e-tests.mdunit-test-structure.md詳細
テストのベストプラクティス
実際の信頼につながる、効果的で保守しやすいテストを書くためのガイドラインです。E2E テストを優先すること、モックを最小限にすること、実装ではなく振る舞いをテストすることに焦点を当てた 6 つのルールが含まれています。
基本方針
- ユニットテストより E2E テストを優先する - 個々の部分ではなく、システム全体をテストします
- モックを最小限にする - 複雑なモックが必要なら、代わりに E2E テストを書きます
- 実装ではなく振る舞いをテストする - ユーザーに見えるものや、ユーザーが行う操作をテストします
- React コンポーネントを直接テストしない - E2E を通じてテストします
適用する場面
次のような場合に、これらのガイドラインを参照してください。
- どの種類のテストを書くかを決めるとき
- 新しい E2E テストまたはユニットテストを書くとき
- テストコードをレビューするとき
- テストをリファクタリングするとき
ルールの概要
テスト戦略(重要)
prefer-e2e-tests - @rules/prefer-e2e-tests.md
基本的に E2E テストを選びます。ユニットテストを書くのは、純粋関数に限ります。
// E2E test (PREFERRED) - tests real user flow
test("user can place an order", async ({ page }) => {
await createTestingAccount(page, { account_status: "active" });
await page.goto("/catalog");
await page.getByRole("heading", { name: "Example Item" }).click();
await page.getByRole("link", { name: "Buy" }).click();
// ... complete flow
await expect(page.getByAltText("Thank you")).toBeVisible();
});
// Unit test - ONLY for pure functions
test("formatCurrency formats with two decimals", () => {
expect(formatCurrency(1234.5)).toBe("$1,234.50");
});
avoid-component-tests - @rules/avoid-component-tests.md
React コンポーネントをユニットテストしないでください。E2E を通じてテストするか、テストしないでください。
// BAD: Component unit test
describe("OrderCard", () => {
test("renders amount", () => {
render(<OrderCard amount={100} />);
expect(screen.getByText("$100")).toBeInTheDocument();
});
});
// GOOD: E2E test covers the component naturally
test("order history shows orders", async ({ page }) => {
await page.goto("/orders");
await expect(page.getByText("$100")).toBeVisible();
});
minimize-mocking - @rules/minimize-mocking.md
モックはシンプルに保ちます。3 つ以上のモックが必要なら、代わりに E2E テストを書きます。
// BAD: Too many mocks = write E2E test
vi.mock("~/lib/auth");
vi.mock("~/lib/transactions");
vi.mock("~/hooks/useAccount");
// GOOD: Simple MSW mock for loader test
mockServer.use(
http.get("/api/user", () => HttpResponse.json({ name: "John" })),
);
E2E テスト(高)
e2e-test-structure - @rules/e2e-test-structure.md
E2E テストは e2e/tests/ に配置します。frontend/ には配置しません。
// e2e/tests/order.spec.ts
import { test, expect } from "@playwright/test";
import { addAccountBalance, createTestingAccount } from "./utils";
test.describe("Orders", () => {
test.beforeEach(async ({ page, context }) => {
await createTestingAccount(page, { account_status: "active" });
let cookies = await context.cookies();
let account_id = cookies.find((c) => c.name === "account_id").value;
await addAccountBalance({ account_id, amount: 10000, replaceBalance: true });
});
test("place order with default values", async ({ page }) => {
await page.goto("/catalog");
// ... user flow
});
});
e2e-selectors - @rules/e2e-selectors.md
アクセシブルなセレクターを使用します。優先順位は、role > label > text > testid です。
// GOOD: Role-based (preferred)
await page.getByRole("button", { name: "Submit" }).click();
await page.getByRole("heading", { name: "Dashboard" });
// GOOD: Label-based
await page.getByLabel("Email").fill("[email protected]");
// OK: Test ID when no accessible selector exists
await expect(page.getByTestId("balance")).toHaveText("$1,234");
// BAD: CSS selectors
await page.locator(".btn-primary").click();
ユニットテスト(中)
unit-test-structure - @rules/unit-test-structure.md
純粋関数のみをユニットテストします。ソースファイルと同じ場所に配置します。
// app/utils/format.test.ts
import { describe, test, expect } from "vitest";
import { formatCurrency } from "./format";
describe("formatCurrency", () => {
test("formats positive amounts", () => {
expect(formatCurrency(1234.5)).toBe("$1,234.50");
});
test("handles zero", () => {
expect(formatCurrency(0)).toBe("$0.00");
});
});
主なファイル
e2e/tests/- E2E テスト(Playwright)e2e/tests/utils.ts- E2E テストユーティリティvitest.config.ts- ユニットテストの設定vitest.setup.ts- MSW を使用したグローバルテストのセットアップapp/utils/test-utils.ts- ユニットテストユーティリティ
コメント
Markdown 対応