full-coverage
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseFull Coverage — Test Generation
全覆盖率——测试生成
Generate the right tests for every layer of the testing pyramid: unit, integration, API, and E2E. The skill analyzes the code, selects the correct layers, and implements each one following strict guidelines.
为测试金字塔的每一层生成合适的测试:单元、集成、API和E2E。本技能会分析代码,选择正确的测试层,并遵循严格的规范实现每一层的测试。
Testing Pyramid
测试金字塔
╔═══════════════════════╗
║ E2E Tests ║ Playwright browser · user journeys
╠═══════════════════════╣
║ API Tests ║ Playwright HTTP · HTTP contracts
╠═══════════════════════╣
║Integration ║ Vitest + Prisma · every branch with real DB
╠═══════════════════════╣
║ Unit Tests ║ Vitest · isolated logic, no I/O
╚═══════════════════════╝ ╔═══════════════════════╗
║ E2E Tests ║ Playwright browser · user journeys
╠═══════════════════════╣
║ API Tests ║ Playwright HTTP · HTTP contracts
╠═══════════════════════╣
║Integration ║ Vitest + Prisma · every branch with real DB
╠═══════════════════════╣
║ Unit Tests ║ Vitest · isolated logic, no I/O
╚═══════════════════════╝Layer Responsibility Matrix
层职责矩阵
Each concern is owned by exactly one layer. Do not duplicate responsibilities.
| Concern | Unit | Integration | API | E2E |
|---|---|---|---|---|
| Zod field exhaustive validation | Owner | 1–2 wiring checks | 1 per endpoint | No |
| Service method branches | Owner (mocked deps) | Owner (real DB) | No | No |
| Pure utility functions | Owner | No | No | No |
| Mock call argument verification | Owner | No | No | No |
| HTTP contract (status codes, body) | No | No | Owner | No |
| Write verification (POST→GET, DELETE→404) | No | No | Owner | No |
| Authorization enforcement (401, 403) | No | Owner | Owner | No |
| External service calls (mocked) | Owner (vi.mock) | Owner (Wiremock) | No | No |
| DB persistence verification | No | Owner | No | No |
| Complete user journey through UI | No | No | No | Owner |
| Navigation and routing | No | No | No | Owner |
每个测试点仅归属一个测试层,不要重复覆盖职责。
| 测试点 | 单元测试 | 集成测试 | API测试 | E2E测试 |
|---|---|---|---|---|
| Zod字段全量校验 | 责任层 | 1-2项连通性检查 | 每个接口1项检查 | 无 |
| 服务方法分支 | 责任层(依赖mock) | 责任层(真实数据库) | 无 | 无 |
| 纯工具函数 | 责任层 | 无 | 无 | 无 |
| Mock调用参数校验 | 责任层 | 无 | 无 | 无 |
| HTTP契约(状态码、响应体) | 无 | 无 | 责任层 | 无 |
| 写入操作校验(POST→GET、DELETE→404) | 无 | 无 | 责任层 | 无 |
| 鉴权规则校验(401、403) | 无 | 责任层 | 责任层 | 无 |
| 外部服务调用(mock) | 责任层(vi.mock) | 责任层(Wiremock) | 无 | 无 |
| 数据库持久化校验 | 无 | 责任层 | 无 | 无 |
| 完整UI用户流程 | 无 | 无 | 无 | 责任层 |
| 导航与路由 | 无 | 无 | 无 | 责任层 |
Step 1: Analyze the Code
步骤1:分析代码
Before selecting layers or writing any tests, read the target code.
Read these files:
- Zod schemas / DTOs ()
<domain>.dto.ts - Service class ()
<domain>.service.ts - Route handlers ()
app/api/<path>/route.ts - Utility functions ()
<domain>.utils.ts - DB schema ()
prisma/schema.prisma - Error definitions ()
src/lib/server/errors.ts - External service clients (if any)
- UI components / pages (if any — look for attributes)
data-testid
Build an inventory:
- Schemas found: list field names and rules
- Service methods found: list method names and their dependencies
- HTTP endpoints found: list METHOD /path
- Utility functions found: list exported functions
- External service calls found: list service names
- UI pages found: list page URLs
Write a one-paragraph summary: "This module has X schemas, Y service methods, Z endpoints... Recommended layers: [list]."
在选择测试层或编写任何测试之前,先读取目标代码。
读取以下文件:
- Zod模式 / DTO()
<domain>.dto.ts - 服务类()
<domain>.service.ts - 路由处理函数()
app/api/<path>/route.ts - 工具函数()
<domain>.utils.ts - 数据库模式()
prisma/schema.prisma - 错误定义()
src/lib/server/errors.ts - 外部服务客户端(如有)
- UI组件/页面(如有——查找属性)
data-testid
梳理资产清单:
- 找到的模式:列出字段名和校验规则
- 找到的服务方法:列出方法名及其依赖
- 找到的HTTP接口:列出 请求方法 / 路径
- 找到的工具函数:列出导出的函数
- 找到的外部服务调用:列出服务名
- 找到的UI页面:列出页面URL
编写一段摘要:"本模块包含X个模式、Y个服务方法、Z个接口... 推荐测试层:[列表]。"
Step 2: Select Layers
步骤2:选择测试层
Apply this decision tree after completing Step 1:
Does the code have Zod schemas or pure utility functions?
YES → Unit tests required (always the baseline layer)
Does the code have service methods with branches (if/else, switch, throw)?
YES → Unit tests (mocked) + Integration tests (real DB) required
Why: unit tests prove logic; integration tests prove wiring with real DB.
Together they are not redundant — they cover different failure modes.
Does the code expose HTTP endpoints?
YES → API tests required (one spec file per endpoint)
Does the code have browser UI with data-testid attributes?
YES → E2E tests required (one spec per feature area)Common combinations:
- Pure utility library → Unit only
- Backend service, no UI → Unit + Integration + API (if endpoints exist)
- Full-stack feature → All four layers
State explicitly which layers are selected and which are skipped with a reason:
"Skipping E2E: no browser UI found in this module."
完成步骤1后按照以下决策树选择测试层:
代码是否包含Zod模式或纯工具函数?
是 → 必须编写单元测试(始终是基础层)
代码是否包含带分支逻辑的服务方法(if/else、switch、throw)?
是 → 必须编写单元测试(依赖mock)+ 集成测试(真实数据库)
原因:单元测试验证逻辑正确性;集成测试验证与真实数据库的连通性。
两者并不冗余——覆盖的故障模式不同。
代码是否暴露HTTP接口?
是 → 必须编写API测试(每个接口对应一个测试用例文件)
代码是否包含带`data-testid`属性的浏览器UI?
是 → 必须编写E2E测试(每个功能域对应一个测试用例文件)常见组合:
- 纯工具库 → 仅单元测试
- 无UI的后端服务 → 单元 + 集成 + API测试(如果有接口)
- 全栈功能 → 全部四层测试
明确说明选择了哪些测试层、跳过了哪些层并给出原因:
"跳过E2E测试:本模块中未找到浏览器UI。"
Step 3: Generate Tests, Layer by Layer
步骤3:逐层生成测试
Work bottom-up. Generate unit tests first, then integration, then API, then E2E. Complete and self-validate each layer before moving to the next.
自底向上工作。先生成单元测试,然后是集成、API,最后是E2E测试。每一层完成并自校验后再进入下一层。
If Unit tests are selected:
如果选择了单元测试:
Read before writing any code.
references/unit-testing.mdKey steps:
- Read schemas, service, utils, errors
- Map every field × rule and every service branch as a case tree (write as comments first)
- Create and
schema.helper.ts<domain>.unit-factory.ts - Write schema tests (one per field, one
describeper rule)it() - Write service tests (all branches mocked, exact mock argument verification)
- Write utility tests (if applicable)
- Run:
vitest run src/modules/<domain>/test/unit/ - Run self-validation checklist from the reference file
编写代码前先阅读。
references/unit-testing.md关键步骤:
- 读取模式、服务、工具、错误定义
- 将每个字段×规则、每个服务分支映射为用例树(先写为注释)
- 创建和
schema.helper.ts<domain>.unit-factory.ts - 编写模式测试(每个字段对应一个块,每条规则对应一个
describe用例)it() - 编写服务测试(所有分支依赖mock,严格校验mock调用参数)
- 编写工具测试(如有)
- 运行:
vitest run src/modules/<domain>/test/unit/ - 运行参考文件中的自校验检查清单
If Integration tests are selected:
如果选择了集成测试:
Read before writing any code.
references/integration-testing.mdKey steps:
- Read source + auth implementation + error response shape (determines assertion format)
- List every execution path as comments inside blocks
describe - Create ,
DatabaseHelper, fixtures, api-utilsAuthHelper - Write handler tests (1–2 validation wiring tests per endpoint — no exhaustive Zod rules)
- Write service layer tests (direct calls, real DB)
- Run: (or equivalent)
yarn test:integration - Run self-validation checklist from the reference file
编写代码前先阅读。
references/integration-testing.md关键步骤:
- 读取源代码 + 鉴权实现 + 错误响应格式(决定断言格式)
- 在块内将所有执行路径列为注释
describe - 创建、
DatabaseHelper、fixtures、api-utilsAuthHelper - 编写处理函数测试(每个接口1-2项校验连通性测试——不做全量Zod规则校验)
- 编写服务层测试(直接调用,真实数据库)
- 运行:(或等效命令)
yarn test:integration - 运行参考文件中的自校验检查清单
If API tests are selected:
如果选择了API测试:
Read before writing any code.
references/api-testing.mdKey steps:
- Read route handlers, DTOs, error definitions
- Verify/create response helpers for all error codes
- Create test-owned factory (never import backend DTOs)
- Create API utilities (one function per endpoint)
- Create cleanup utilities
- Write test specs: plan scenarios as comments first, then implement
- Run:
npx playwright test --project=api - Run self-validation checklist from the reference file
编写代码前先阅读。
references/api-testing.md关键步骤:
- 读取路由处理函数、DTO、错误定义
- 验证/创建所有错误码的响应辅助工具
- 创建测试专属工厂函数(不要导入后端DTO)
- 创建API工具(每个接口对应一个函数)
- 创建清理工具
- 编写测试用例:先将场景规划为注释,再实现
- 运行:
npx playwright test --project=api - 运行参考文件中的自校验检查清单
If E2E tests are selected:
如果选择了E2E测试:
Read before writing any code.
references/e2e-testing.mdKey steps:
- Read PRD, page components (attributes), existing fixtures
data-testid - Identify user flows (one spec file per feature area)
- Create or update Page Object Models (locators via only)
data-testid - Create test data factory with pattern
counter + timestamp + random - Create API-based setup helper and cleanup helper
- Write test specs (API setup, inject auth, navigate, UI interaction, assert)
- Run:
npx playwright test --project=chromium - Run self-validation checklist from the reference file
编写代码前先阅读。
references/e2e-testing.md关键步骤:
- 读取PRD、页面组件(属性)、现有fixtures
data-testid - 识别用户流程(每个功能域对应一个测试用例文件)
- 创建或更新页面对象模型(仅通过定位元素)
data-testid - 用「计数器 + 时间戳 + 随机数」模式创建测试数据工厂
- 创建基于API的设置辅助工具和清理辅助工具
- 编写测试用例(API设置、注入鉴权、导航、UI交互、断言)
- 运行:
npx playwright test --project=chromium - 运行参考文件中的自校验检查清单
Universal Patterns
通用规范
These apply at every layer. Do not repeat in reference files.
这些规范适用于所有测试层,无需在参考文件中重复。
Unique test data
唯一测试数据
typescript
let counter = 0;
function uniqueSuffix(): string {
counter++;
return `${counter}-${Date.now()}-${Math.floor(Math.random() * 100000)}`;
}
// Usage: `test-user-${uniqueSuffix()}@test.com`Why: parallel test runs share a database. Non-unique data causes false failures when one test's cleanup deletes another test's records.
typescript
let counter = 0;
function uniqueSuffix(): string {
counter++;
return `${counter}-${Date.now()}-${Math.floor(Math.random() * 100000)}`;
}
// 用法:`test-user-${uniqueSuffix()}@test.com`原因:并行测试运行会共享数据库。如果数据不唯一,当一个测试的清理操作删除了另一个测试的记录时,会导致误报失败。
Arrange-Act-Assert
安排-执行-断言
Every test follows this exact structure:
typescript
// Arrange — set up state
const cookie = await authenticateUser(request);
const dto = generateCreateNoteDto();
// Act — execute the behavior
const response = await executePostNoteRequest(request, dto, cookie);
// Assert — verify the result
expect201Created(response);
expect(response.data.title).toBe(dto.title);每个测试都遵循以下严格结构:
typescript
// 安排 — 设置状态
const cookie = await authenticateUser(request);
const dto = generateCreateNoteDto();
// 执行 — 运行目标行为
const response = await executePostNoteRequest(request, dto, cookie);
// 断言 — 验证结果
expect201Created(response);
expect(response.data.title).toBe(dto.title);Cleanup in afterEach
在afterEach中做清理
typescript
// ✓ Correct — runs even when test throws
test.afterEach(async ({ request }) => {
for (const cookie of cookiesToCleanup) {
await cleanupNotes(request, cookie);
await cleanupUser(request, cookie);
}
cookiesToCleanup.length = 0;
});
// ✗ Wrong — skipped when test throws before reaching cleanup
test("...", async ({ request }) => {
const cookie = await authenticateUser(request);
// ...
await cleanupUser(request, cookie); // This line may never run
});typescript
// ✓ 正确 — 即使测试抛出异常也会运行
test.afterEach(async ({ request }) => {
for (const cookie of cookiesToCleanup) {
await cleanupNotes(request, cookie);
await cleanupUser(request, cookie);
}
cookiesToCleanup.length = 0;
});
// ✗ 错误 — 当测试在到达清理代码前抛出异常时会跳过清理
test("...", async ({ request }) => {
const cookie = await authenticateUser(request);
// ...
await cleanupUser(request, cookie); // 这行可能永远不会运行
});No shared state between tests
测试之间无共享状态
- No for shared data
beforeAll - No shared auth sessions
- Each test creates its own user and test entities
- 不要用创建共享数据
beforeAll - 不要共享鉴权会话
- 每个测试都创建自己的用户和测试实体
No static waits
不要使用静态等待
typescript
// ✗ Forbidden at all layers
await new Promise(resolve => setTimeout(resolve, 1000));
await page.waitForTimeout(5000);
// ✓ Use explicit conditions instead
// API/Integration: poll with 400ms interval, 15s timeout
// E2E: locator.waitFor(), page.waitForURL(), expect(locator).toBeVisible()typescript
// ✗ 所有层都禁止使用
await new Promise(resolve => setTimeout(resolve, 1000));
await page.waitForTimeout(5000);
// ✓ 改用显式条件
// API/集成测试:间隔400ms轮询,超时时间15s
// E2E测试:locator.waitFor(), page.waitForURL(), expect(locator).toBeVisible()No snapshot tests
不要使用快照测试
.toMatchSnapshot().toMatchInlineSnapshot()所有层都禁止使用和。快照会隐藏测试意图,且生成的差异不透明。
.toMatchSnapshot().toMatchInlineSnapshot()No hardcoded URLs
不要硬编码URL
Always use relative paths. Playwright's from config handles environment switching.
baseURL始终使用相对路径。Playwright配置中的会处理环境切换。
baseURLReference File Guide
参考文件指南
When generating tests for a layer, read the corresponding reference file first. Each file includes the full workflow, verbatim templates, and a self-validation checklist.
| File | Contains | Key templates |
|---|---|---|
| 8-step workflow, Zod test patterns, mocking rules | |
| 5-step workflow, parallel-safe cleanup, validation wiring | |
| 8-step workflow, test-owned interfaces, write verification | |
| 9-step workflow, POM rules, waiting strategy | |
为某一层生成测试时,先阅读对应的参考文件。每个文件都包含完整工作流、可直接使用的模板和自校验检查清单。
| 文件 | 包含内容 | 核心模板 |
|---|---|---|
| 8步工作流、Zod测试模式、mock规则 | |
| 5步工作流、并行安全清理、校验连通性 | |
| 8步工作流、测试专属接口、写入校验 | |
| 9步工作流、POM规则、等待策略 | |
Cross-Layer Checklist
跨层检查清单
After all layers are complete, verify the pyramid is correctly shaped:
No layer duplication:
- Unit tests cover Zod field validation exhaustively — integration/API/E2E do not
- Integration tests have only 1–2 validation wiring tests per endpoint
- API tests have only 1 validation test per endpoint
- E2E tests cover user journeys and navigation — no field validation
Pyramid shape (count tests per layer):
- Unit tests cover the most cases — exhaustive per field and per branch
- Integration tests are fewer — only real DB wiring and auth paths
- API tests are fewer still — one spec per endpoint
- E2E tests are the fewest — one spec per user journey
All self-validation checklists were run:
- Unit layer checklist ✓
- Integration layer checklist ✓
- API layer checklist ✓
- E2E layer checklist ✓
所有层完成后,验证测试金字塔的结构是否正确:
无层间重复覆盖:
- 单元测试全量覆盖Zod字段校验 —— 集成/API/E2E测试不覆盖
- 集成测试每个接口仅做1-2项校验连通性测试
- API测试每个接口仅做1项校验测试
- E2E测试覆盖用户流程和导航 —— 不做字段校验
金字塔结构(每层测试数量):
- 单元测试覆盖最多场景 —— 全量覆盖每个字段和每个分支
- 集成测试数量更少 —— 仅覆盖真实数据库连通性和鉴权路径
- API测试数量更少 —— 每个接口对应一个测试用例
- E2E测试数量最少 —— 每个用户流程对应一个测试用例
所有自校验检查清单都已运行:
- 单元层检查清单 ✓
- 集成层检查清单 ✓
- API层检查清单 ✓
- E2E层检查清单 ✓
Cross-Layer Examples
跨层示例
The same "notes" domain — showing exactly how coverage is divided, not duplicated.
以相同的「笔记」域为例——明确展示覆盖率如何拆分,而非重复覆盖。
Testing a title
field (min 1, max 255, required)
title测试title
字段(最小长度1,最大长度255,必填)
title| Layer | What to test | Test count |
|---|---|---|
| Unit | missing, undefined, null, empty string, min boundary (1 char ✓), max boundary (255 chars ✓), 256 chars ✗, wrong type (number) | 8+ tests |
| Integration | one representative: missing title → 400 VALIDATION_ERROR (proves Zod is wired) | 1 test |
| API | one representative: missing title → 400 (verifies HTTP contract) | 1 test |
| E2E | nothing — E2E tests do not test field validation | 0 tests |
| 测试层 | 测试内容 | 测试数量 |
|---|---|---|
| 单元测试 | 缺失、undefined、null、空字符串、最小边界(1个字符 ✓)、最大边界(255个字符 ✓)、256个字符 ✗、类型错误(数字) | 8+ 个测试 |
| 集成测试 | 一个代表性场景:缺失title → 返回400 VALIDATION_ERROR(证明Zod已连通) | 1个测试 |
| API测试 | 一个代表性场景:缺失title → 返回400(验证HTTP契约) | 1个测试 |
| E2E测试 | 无 —— E2E测试不测试字段校验 | 0个测试 |
Testing a notFound
service branch
notFound测试notFound
服务分支
notFound| Layer | What to test |
|---|---|
| Unit | mock |
| Integration | request with a real non-existent ID in DB → verify 404 response with correct |
| API | |
| E2E | nothing — E2E tests don't test error branches |
| 测试层 | 测试内容 |
|---|---|
| 单元测试 | mock |
| 集成测试 | 用数据库中真实不存在的ID发起请求 → 验证返回404响应,携带正确的 |
| API测试 | |
| E2E测试 | 无 —— E2E测试不测试错误分支 |
Testing an authorization rule
测试鉴权规则
| Layer | What to test |
|---|---|
| Unit | mock |
| Integration | user B requests note owned by user A's organization → verify 403 FORBIDDEN response and DB unchanged |
| API | user B requests note owned by user A → verify 403 response |
| E2E | verify user B cannot see user A's notes in the UI (data isolation test) |
| 测试层 | 测试内容 |
|---|---|
| 单元测试 | mock |
| 集成测试 | 用户B请求属于用户A组织的笔记 → 验证返回403 FORBIDDEN响应,数据库未变更 |
| API测试 | 用户B请求属于用户A的笔记 → 验证返回403响应 |
| E2E测试 | 验证用户B在UI中看不到用户A的笔记(数据隔离测试) |
Completion
完成
After all layers are generated, provide:
File inventory per layer:
| Layer | Files created | Run command |
|---|---|---|
| Unit | | |
| Integration | | |
| API | | |
| E2E | | |
所有层生成完成后,提供以下内容:
每层文件清单:
| 测试层 | 创建的文件 | 运行命令 |
|---|---|---|
| 单元测试 | | |
| 集成测试 | | |
| API测试 | | |
| E2E测试 | | |