designing-tests
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseDesigning Tests
测试设计
When to Load
适用场景
- Trigger: Adding tests, test strategy planning, improving coverage, setting up testing infrastructure
- Skip: Non-test code changes where testing is not part of the task
- 触发条件:添加测试、测试策略规划、提升覆盖率、搭建测试基础设施
- 不适用场景:不涉及测试工作的非测试代码变更
Test Implementation Workflow
测试实现工作流
Copy this checklist and track progress:
Test Implementation Progress:
- [ ] Step 1: Identify what to test
- [ ] Step 2: Select appropriate test type
- [ ] Step 3: Write tests following templates
- [ ] Step 4: Run tests and verify passing
- [ ] Step 5: Check coverage meets targets
- [ ] Step 6: Fix any failing tests复制以下checklist跟踪进度:
Test Implementation Progress:
- [ ] Step 1: Identify what to test
- [ ] Step 2: Select appropriate test type
- [ ] Step 3: Write tests following templates
- [ ] Step 4: Run tests and verify passing
- [ ] Step 5: Check coverage meets targets
- [ ] Step 6: Fix any failing testsTesting Pyramid
测试金字塔
Apply the testing pyramid for balanced coverage:
/\
/ \ E2E Tests (10%)
/----\ - Critical user journeys
/ \ - Slow but comprehensive
/--------\ Integration Tests (20%)
/ \ - Component interactions
/------------\ - API contracts
/ \ Unit Tests (70%)
/________________\ - Fast, isolated
- Business logic focus遵循测试金字塔原则实现均衡的覆盖率:
/\
/ \ E2E Tests (10%)
/----\ - Critical user journeys
/ \ - Slow but comprehensive
/--------\ Integration Tests (20%)
/ \ - Component interactions
/------------\ - API contracts
/ \ Unit Tests (70%)
/________________\ - Fast, isolated
- Business logic focusFramework Selection
测试框架选型
JavaScript/TypeScript
JavaScript/TypeScript
| Type | Recommended | Alternative |
|---|---|---|
| Unit | Vitest | Jest |
| Integration | Vitest + MSW | Jest + SuperTest |
| E2E | Playwright | Cypress |
| Component | Testing Library | Enzyme |
| 测试类型 | 推荐方案 | 替代方案 |
|---|---|---|
| 单元测试 | Vitest | Jest |
| 集成测试 | Vitest + MSW | Jest + SuperTest |
| E2E测试 | Playwright | Cypress |
| 组件测试 | Testing Library | Enzyme |
Python
Python
| Type | Recommended | Alternative |
|---|---|---|
| Unit | pytest | unittest |
| Integration | pytest + httpx | pytest + requests |
| E2E | Playwright | Selenium |
| API | pytest + FastAPI TestClient | - |
| 测试类型 | 推荐方案 | 替代方案 |
|---|---|---|
| 单元测试 | pytest | unittest |
| 集成测试 | pytest + httpx | pytest + requests |
| E2E测试 | Playwright | Selenium |
| API测试 | pytest + FastAPI TestClient | - |
Go
Go
| Type | Recommended |
|---|---|
| Unit | testing + testify |
| Integration | testing + httptest |
| E2E | testing + chromedp |
| 测试类型 | 推荐方案 |
|---|---|
| 单元测试 | testing + testify |
| 集成测试 | testing + httptest |
| E2E测试 | testing + chromedp |
Test Structure Templates
测试结构模板
Unit Test
单元测试模板
javascript
describe("[Unit] ComponentName", () => {
describe("methodName", () => {
it("should [expected behavior] when [condition]", () => {
// Arrange
const input = createTestInput();
// Act
const result = methodName(input);
// Assert
expect(result).toEqual(expectedOutput);
});
it("should throw error when [invalid condition]", () => {
expect(() => methodName(invalidInput)).toThrow(ExpectedError);
});
});
});javascript
describe("[Unit] ComponentName", () => {
describe("methodName", () => {
it("should [expected behavior] when [condition]", () => {
// Arrange
const input = createTestInput();
// Act
const result = methodName(input);
// Assert
expect(result).toEqual(expectedOutput);
});
it("should throw error when [invalid condition]", () => {
expect(() => methodName(invalidInput)).toThrow(ExpectedError);
});
});
});Integration Test
集成测试模板
javascript
describe("[Integration] API /users", () => {
beforeAll(async () => {
await setupTestDatabase();
});
afterAll(async () => {
await teardownTestDatabase();
});
it("should create user and return 201", async () => {
const response = await request(app)
.post("/users")
.send({ name: "Test", email: "test@example.com" });
expect(response.status).toBe(201);
expect(response.body.id).toBeDefined();
});
});javascript
describe("[Integration] API /users", () => {
beforeAll(async () => {
await setupTestDatabase();
});
afterAll(async () => {
await teardownTestDatabase();
});
it("should create user and return 201", async () => {
const response = await request(app)
.post("/users")
.send({ name: "Test", email: "test@example.com" });
expect(response.status).toBe(201);
expect(response.body.id).toBeDefined();
});
});E2E Test
E2E测试模板
javascript
describe("[E2E] User Registration Flow", () => {
it("should complete registration successfully", async ({ page }) => {
await page.goto("/register");
await page.fill('[data-testid="email"]', "new@example.com");
await page.fill('[data-testid="password"]', "SecurePass123!");
await page.click('[data-testid="submit"]');
await expect(page.locator(".welcome-message")).toBeVisible();
await expect(page).toHaveURL("/dashboard");
});
});javascript
describe("[E2E] User Registration Flow", () => {
it("should complete registration successfully", async ({ page }) => {
await page.goto("/register");
await page.fill('[data-testid="email"]', "new@example.com");
await page.fill('[data-testid="password"]', "SecurePass123!");
await page.click('[data-testid="submit"]');
await expect(page.locator(".welcome-message")).toBeVisible();
await expect(page).toHaveURL("/dashboard");
});
});Coverage Strategy
覆盖率策略
What to Cover
需要覆盖的范围
- ✅ Business logic (100%)
- ✅ Edge cases and error handling (90%+)
- ✅ API contracts (100%)
- ✅ Critical user paths (E2E)
- ⚠️ UI components (snapshot + interaction)
- ❌ Third-party library internals
- ❌ Simple getters/setters
- ✅ 业务逻辑 (100%)
- ✅ 边界场景与错误处理 (90%+)
- ✅ API契约 (100%)
- ✅ 核心用户路径 (E2E)
- ⚠️ UI组件 (快照 + 交互测试)
- ❌ 第三方库内部逻辑
- ❌ 简单的getter/setter方法
Coverage Thresholds
覆盖率阈值配置
json
{
"coverageThreshold": {
"global": {
"branches": 80,
"functions": 80,
"lines": 80,
"statements": 80
},
"src/core/": {
"branches": 95,
"functions": 95
}
}
}json
{
"coverageThreshold": {
"global": {
"branches": 80,
"functions": 80,
"lines": 80,
"statements": 80
},
"src/core/": {
"branches": 95,
"functions": 95
}
}
}Test Data Management
测试数据管理
Factories/Builders
工厂/构造器
javascript
// factories/user.js
export const userFactory = (overrides = {}) => ({
id: faker.string.uuid(),
name: faker.person.fullName(),
email: faker.internet.email(),
createdAt: new Date(),
...overrides,
});
// Usage
const admin = userFactory({ role: "admin" });javascript
// factories/user.js
export const userFactory = (overrides = {}) => ({
id: faker.string.uuid(),
name: faker.person.fullName(),
email: faker.internet.email(),
createdAt: new Date(),
...overrides,
});
// Usage
const admin = userFactory({ role: "admin" });Fixtures
固定测试数据(Fixtures)
javascript
// fixtures/users.json
{
"validUser": { "name": "Test", "email": "test@example.com" },
"invalidUser": { "name": "", "email": "invalid" }
}javascript
// fixtures/users.json
{
"validUser": { "name": "Test", "email": "test@example.com" },
"invalidUser": { "name": "", "email": "invalid" }
}Mocking Strategy
Mock策略
When to Mock
适用Mock的场景
- ✅ External APIs and services
- ✅ Database in unit tests
- ✅ Time/Date for determinism
- ✅ Random values
- ❌ Internal modules (usually)
- ❌ The code under test
- ✅ 外部API与服务
- ✅ 单元测试中的数据库
- ✅ 用于保证确定性的时间/日期
- ✅ 随机值
- ❌ 内部模块(通常不需要)
- ❌ 被测试的代码本身
Mock Examples
Mock示例
javascript
// API mocking with MSW
import { http, HttpResponse } from "msw";
export const handlers = [
http.get("/api/users", () => {
return HttpResponse.json([{ id: 1, name: "John" }]);
}),
];
// Time mocking
vi.useFakeTimers();
vi.setSystemTime(new Date("2024-01-01"));javascript
// API mocking with MSW
import { http, HttpResponse } from "msw";
export const handlers = [
http.get("/api/users", () => {
return HttpResponse.json([{ id: 1, name: "John" }]);
}),
];
// Time mocking
vi.useFakeTimers();
vi.setSystemTime(new Date("2024-01-01"));Test Validation Loop
测试验证流程
After writing tests, run this validation:
Test Validation:
- [ ] All tests pass: `npm test`
- [ ] Coverage meets thresholds: `npm test -- --coverage`
- [ ] No flaky tests (run multiple times)
- [ ] Tests are independent (order doesn't matter)
- [ ] Test names clearly describe behaviorIf any tests fail, fix them before proceeding. If coverage is below target, add more tests for uncovered code paths.
bash
undefined编写完测试后,按以下步骤验证:
Test Validation:
- [ ] All tests pass: `npm test`
- [ ] Coverage meets thresholds: `npm test -- --coverage`
- [ ] No flaky tests (run multiple times)
- [ ] Tests are independent (order doesn't matter)
- [ ] Test names clearly describe behavior如果有测试失败,请先修复再继续。如果覆盖率低于目标,请为未覆盖的代码路径补充更多测试。
bash
undefinedRun tests
Run tests
npm test
npm test
Run with coverage
Run with coverage
npm test -- --coverage
npm test -- --coverage
Run specific test file
Run specific test file
npm test -- path/to/test.spec.ts
npm test -- path/to/test.spec.ts
Run in watch mode during development
Run in watch mode during development
npm test -- --watch
undefinednpm test -- --watch
undefined