frontend-testing-best-practices by sergiodxa/agent-skills
npx skills add https://github.com/sergiodxa/agent-skills --skill frontend-testing-best-practices编写有效、可维护且能提供真实信心的测试指南。包含 6 条规则,侧重于优先使用 E2E 测试、最小化模拟以及测试行为而非实现。
在以下情况时参考本指南:
默认使用 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");
});
广告位招租
在这里展示您的产品或服务
触达数万 AI 开发者,精准高效
不要对 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();
});
保持模拟简单。如果需要 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/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
});
});
使用可访问的选择器: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("test@example.com");
// 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();
单元测试仅用于纯函数。与源文件放在一起。
// 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 - 单元测试工具函数每周安装量
1.4K
代码仓库
GitHub 星标数
79
首次出现
2026年1月28日
安全审计
安装于
opencode1.2K
codex1.2K
github-copilot1.2K
gemini-cli1.2K
kimi-cli1.2K
amp1.1K
Guidelines for writing effective, maintainable tests that provide real confidence. Contains 6 rules focused on preferring E2E tests, minimizing mocking, and testing behavior over implementation.
Reference these guidelines when:
Default to E2E tests. Only write unit tests for pure functions.
// 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");
});
Don't unit test React components. Test them through E2E or not at all.
// 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();
});
Keep mocks simple. If you need 3+ mocks, write an E2E test instead.
// 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 tests go in e2e/tests/, not 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
});
});
Use accessible selectors: 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("test@example.com");
// 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 tests for pure functions only. Co-locate with source files.
// 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 tests (Playwright)e2e/tests/utils.ts - E2E test utilitiesvitest.config.ts - Unit test configurationvitest.setup.ts - Global test setup with MSWapp/utils/test-utils.ts - Unit test utilitiesWeekly Installs
1.4K
Repository
GitHub Stars
79
First Seen
Jan 28, 2026
Security Audits
Gen Agent Trust HubPassSocketPassSnykPass
Installed on
opencode1.2K
codex1.2K
github-copilot1.2K
gemini-cli1.2K
kimi-cli1.2K
amp1.1K
React 组合模式指南:Vercel 组件架构最佳实践,提升代码可维护性
102,200 周安装