design-testing-strategy
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseDesign Testing Strategy
测试策略设计
A reference manual for designing a fit-for-purpose, fit-for-criticality testing strategy.
This skill is decision-oriented, not philosophical: every gate is deterministic (ON when X / OFF when Y), every schema is enforced (field ordering matters), every example is worked end-to-end.
这是一份设计适配需求、适配关键程度的测试策略的参考手册。
本技能以决策为导向,而非理论性的:每个决策门都是确定性的(满足X条件启用/满足Y条件禁用),每个模式都有强制要求(字段顺序至关重要),每个示例都是端到端完整实现的。
How To Use This Skill
如何使用本技能
- Read Decision Gates in order (Gate 0 -> Gate 6). Each gate is independent — you may finish with any subset of test types ON.
- Apply Strategic Skip Heuristics to remove ON gates that would yield low ROI for this artifact.
- For each ON gate, fill the Test Matrix Schema (entry) — the field order is load-bearing.
selected_types - List rejected types in and deliberate skips in
rejected_types.deliberately_skipped - Produce a Test Cases to Cover markdown bullet list using ISTQB techniques from Case Design Techniques.
- Cross-check against the matching Worked Example (A pure function / B HTTP+DB endpoint / C UI component).
- 按顺序阅读决策门(从Gate 0到Gate 6)。每个决策门相互独立——最终你可能只启用部分测试类型。
- 应用策略性跳过启发规则,移除对当前工件投资回报率低的已启用决策门。
- 对每个已启用的决策门,填写测试矩阵模式(条目)——字段顺序是固定要求。
selected_types - 在中列出未选中的测试类型,在
rejected_types中列出主动跳过的内容。deliberately_skipped - 使用用例设计方法中的ISTQB技术,生成一份需覆盖的测试用例Markdown无序列表。
- 对照匹配的完整示例(纯函数/B HTTP+DB端点/C UI组件)进行交叉检查。
Decision Gates
决策门
Apply gates in numeric order. Each gate produces an independent boolean (). Gates do NOT veto each other — a single artifact may have unit + integration + contract + property-based all ON.
applies: true|false| # | Type | ON when | OFF when | Source |
|---|---|---|---|---|
| 0 | Skip All | Criticality is | Anything with branching, computed output, side effects, or user-visible behavior | Pragmatic Programmer — "Test ruthlessly and effectively" implies effective skipping when ROI is zero |
| 1 | Unit | Code contains any logic: branches, loops, conditionals, computation, transformation, parsing, validation, formatting | Pure declarative wiring (DI registration, route table) with no behavior | Test Pyramid (Vocke) base layer + Beck TDD Red-Green-Refactor unit |
| 2 | Integration | Boundary crossing: HTTP call, DB query, external SDK, message queue, filesystem I/O, OR collaboration with >=2 distinct collaborators where unit doubles distort behavior | Pure function with no I/O and 0-1 stable collaborators | Testing Trophy (Dodds) — integration is the highest-ROI layer; Google "Follow the User" |
| 3 | Component or E2E | UI surface AND criticality >= MEDIUM-HIGH AND user-facing critical path (signup, checkout, auth, payment, primary CTA) | Internal admin-only screens, dev tooling, or non-critical UI | Test Pyramid top + ISO/IEC/IEEE 29119 risk ranking + Google e2e principles |
| 4 | Contract | Public API consumed by >=1 distinct clients (mobile + web, multiple internal services, external partners) AND independent deploy cadence | API where consumer and provider deploy together | Pact / CDC + Pactflow CDC explainer |
| 5 | Smoke | Deployable surface (web app, API, service) AND a deploy/CI pipeline exists where post-deploy validation is meaningful | Library, internal helper, or no deploy pipeline | Google "What Makes a Good End-to-End Test" — smoke = minimal e2e for deploy gate |
| 6 | Property-Based | Input domain is large or unbounded (numeric ranges, strings, lists, parsers, serializers, encoders, math) AND invariants are stable (round-trip, idempotency, monotonicity, commutativity) AND criticality >= MEDIUM-HIGH | Small finite input domain, unstable invariants, or LOW criticality | Hypothesis / QuickCheck |
按数字顺序应用决策门。每个决策门会生成独立的布尔值()。决策门之间不会相互否决——一个工件可以同时启用单元测试+集成测试+契约测试+基于属性的测试。
applies: true|false| # | 类型 | 启用条件 | 禁用条件 | 资料来源 |
|---|---|---|---|---|
| 0 | 全部跳过 | 关键程度为 | 任何包含分支、计算输出、副作用或用户可见行为的内容 | 《程序员修炼之道》(20周年版)——“无情且高效地测试”意味着在投资回报率为零时要高效地跳过测试 |
| 1 | 单元测试 | 代码包含任何逻辑:分支、循环、条件判断、计算、转换、解析、验证、格式化 | 纯声明式 wiring(依赖注入注册、路由表)无任何行为 | 测试金字塔(Vocke)基础层 + Beck TDD红-绿-重构单元测试 |
| 2 | 集成测试 | 跨边界操作:HTTP调用、数据库查询、外部SDK、消息队列、文件系统I/O,或与≥2个不同协作方协作且单元模拟会扭曲行为 | 无I/O且仅有0-1个稳定协作方的纯函数 | 测试奖杯(Dodds)——集成测试是投资回报率最高的层级;Google“跟随用户”原则 |
| 3 | 组件或端到端测试 | 有UI界面且关键程度≥MEDIUM-HIGH,同时属于用户关键路径(注册、结账、认证、支付、主要CTA) | 内部仅管理员可见的界面、开发工具或非关键UI | 测试金字塔顶层 + ISO/IEC/IEEE 29119风险评级 + Google端到端测试原则 |
| 4 | 契约测试 | 被≥1个独立客户端(移动端+网页端、多个内部服务、外部合作伙伴)消费的公开API,且客户端与服务端部署节奏独立 | 消费者与提供者同步部署的API | Pact/消费者驱动契约(CDC) + Pactflow CDC说明 |
| 5 | 冒烟测试 | 可部署的应用(网页应用、API、服务),且存在部署/CI流水线,部署后验证有意义 | 类库、内部辅助工具或无部署流水线 | Google“优质端到端测试的标准”——冒烟测试是用于部署校验的最小化端到端测试 |
| 6 | 基于属性的测试 | 输入域庞大或无界(数值范围、字符串、列表、解析器、序列化器、编码器、数学运算),且不变量稳定(往返一致性、幂等性、单调性、交换性),同时关键程度≥MEDIUM-HIGH | 输入域小且有限、不变量不稳定或关键程度为LOW | Hypothesis/QuickCheck |
Gate Application Algorithm
决策门应用算法
for gate in [Gate 0, Gate 1, ..., Gate 6]:
if gate.ON_condition_met(artifact):
result[gate.type] = applies: true
else:
result[gate.type] = applies: false
if Gate 0 is true:
short-circuit: emit empty selected_types, document criticality=NONE, stopCriticality Scale (used by Gates 3 and 6):
| Level | Definition |
|---|---|
| Docs, formatting, generated code, throwaway code, configs without logic |
| Internal dev tooling, admin-only screens, logging formatters |
| Standard CRUD, internal APIs with a single team consumer, non-critical UI, helpers and utilities |
| User-facing UI on critical paths, public APIs with multiple consumers, business workflows |
| Money movement, auth/authz decisions, security-critical validation, data integrity, regulated domains |
for gate in [Gate 0, Gate 1, ..., Gate 6]:
if gate.ON_condition_met(artifact):
result[gate.type] = applies: true
else:
result[gate.type] = applies: false
if Gate 0 is true:
short-circuit: emit empty selected_types, document criticality=NONE, stop关键程度量表(Gate 3和Gate 6使用):
| 级别 | 定义 |
|---|---|
| 文档、格式代码、生成代码、一次性代码、无逻辑配置 |
| 内部开发工具、仅管理员可见界面、日志格式化工具 |
| 标准CRUD操作、仅单个团队消费的内部API、非关键UI、辅助工具 |
| 用户关键路径上的UI、多消费者的公开API、业务工作流 |
| 资金流转、认证/授权决策、安全关键验证、数据完整性、受监管领域 |
Test Type Reference
测试类型参考
| Type | Use when | Do NOT use when | Frameworks | Typical dependencies | Google Size |
|---|---|---|---|---|---|
| unit | Pure logic, single function/method/class, deterministic inputs | Code is just I/O orchestration with no logic | vitest, jest, pytest, go test, JUnit, xUnit, RSpec | None (or in-memory fakes) | Small |
| integration | Boundary crossing (DB, HTTP, queue, FS); multiple collaborators where mocking distorts behavior | Pure function with no boundary | vitest, jest, pytest, go test, JUnit + Testcontainers, supertest, TestRestTemplate | Real Postgres/Redis/Kafka via Testcontainers, in-process HTTP server, real FS in tmpdir | Medium (single machine, localhost OK) |
| component | UI rendering + interaction within a single component, no full app context | Backend-only logic; multi-page user flow | React Testing Library, Vue Test Utils, Angular TestBed, Storybook interaction tests | jsdom or happy-dom, mocked network at fetch/axios level | Small to Medium |
| e2e | Full user path through running app: real browser, real backend, real DB | Internal helper, single component, non-critical UI | Playwright, Cypress, Selenium | Real running app + Testcontainers-backed DB or seeded staging | Large (multi-process, possibly multi-machine) |
| smoke | Post-deploy go/no-go: hit / health, key endpoints respond, login works | Detailed correctness; smoke is shallow by design | Playwright (1-3 critical paths), HTTP probe scripts, k6 minimal scenarios | Real deployed environment | Large |
| contract | Public API consumed by 2+ distinct clients with independent deploy cadence | Single-consumer internal API; provider and consumer deploy together | Pact, Spring Cloud Contract, OpenAPI schema validators | Pact broker or contract files in repo | Medium |
| property-based | Large/unbounded input domain with stable invariants (parser, serializer, encoder, math) | Small finite input space; unstable invariants | Hypothesis (Python), fast-check (TS), QuickCheck (Haskell), jqwik (Java), proptest (Rust) | Same as unit | Small |
| 类型 | 使用场景 | 禁用场景 | 框架 | 典型依赖 | Google规模 |
|---|---|---|---|---|---|
| unit | 纯逻辑、单一函数/方法/类、确定性输入 | 代码仅为I/O编排,无逻辑 | vitest, jest, pytest, go test, JUnit, xUnit, RSpec | 无(或内存模拟对象) | 小型 |
| integration | 跨边界操作(数据库、HTTP、队列、文件系统);多个协作方且模拟会扭曲行为 | 无边界的纯函数 | vitest, jest, pytest, go test, JUnit + Testcontainers, supertest, TestRestTemplate | 通过Testcontainers使用真实Postgres/Redis/Kafka、进程内HTTP服务器、临时目录中的真实文件系统 | 中型(单机器,本地环境即可) |
| component | 单个组件内的UI渲染+交互,无需完整应用上下文 | 仅后端逻辑;多页面用户流程 | React Testing Library, Vue Test Utils, Angular TestBed, Storybook交互测试 | jsdom或happy-dom、fetch/axios层的网络模拟 | 小型到中型 |
| e2e | 运行中应用的完整用户路径:真实浏览器、真实后端、真实数据库 | 内部辅助工具、单个组件、非关键UI | Playwright, Cypress, Selenium | 运行中的真实应用 + Testcontainers支撑的数据库或预填充的 staging环境 | 大型(多进程,可能多机器) |
| smoke | 部署后校验:访问/health、关键端点响应、登录功能正常 | 详细正确性校验;冒烟测试设计为浅层次 | Playwright(1-3条关键路径)、HTTP探测脚本、k6最小场景 | 真实部署环境 | 大型 |
| contract | 被2+个独立部署节奏的客户端消费的公开API | 仅单个消费者的内部API;提供者与消费者同步部署 | Pact, Spring Cloud Contract, OpenAPI schema validators | Pact broker或仓库中的契约文件 | 中型 |
| property-based | 输入域庞大/无界且不变量稳定的场景(解析器、序列化器、编码器、数学运算) | 输入域小且有限;不变量不稳定 | Hypothesis (Python), fast-check (TS), QuickCheck (Haskell), jqwik (Java), proptest (Rust) | 与单元测试相同 | 小型 |
Google Test Size Mapping
Google测试规模映射
Google Test Sizes (Bland) and SWE at Google Ch.11 classify tests by resources (size), independent of scope (paths covered):
| Size | Process model | Network | Filesystem | Time budget | Notes |
|---|---|---|---|---|---|
| Single process, single thread | None | None (in-memory only) | < 100ms | Fast, hermetic, parallelizable |
| Single machine, multiple processes allowed | localhost only | tmpdir allowed | < 1s | Testcontainers fits here |
| Multi-machine | External network allowed | Persistent FS allowed | < 15min | Full e2e |
| Distributed | Wide network | Anywhere | longer | Cluster / chaos |
A test's type (unit/integration/e2e) and size (small/medium/large) are orthogonal: a small integration test (Testcontainers Postgres in same process via JDBC) is legitimate.
Google测试规模(Bland)和《Google软件工程》第11章根据资源消耗(规模)对测试进行分类,与覆盖范围(路径覆盖)无关:
| 规模 | 进程模型 | 网络 | 文件系统 | 时间预算 | 说明 |
|---|---|---|---|---|---|
| 单进程、单线程 | 无 | 无(仅内存) | < 100ms | 快速、封闭、可并行 |
| 单机器,允许多进程 | 仅本地网络 | 允许临时目录 | < 1s | Testcontainers属于此类 |
| 多机器 | 允许外部网络 | 允许持久化文件系统 | < 15min | 完整端到端测试 |
| 分布式 | 广域网络 | 任意位置 | 更长 | 集群/混沌测试 |
测试的类型(单元/集成/端到端)和规模(小/中/大)是正交的:小型集成测试(通过JDBC在同一进程中使用Testcontainers Postgres)是合理的。
Playwright vs Cypress (UI e2e)
Playwright vs Cypress(UI端到端测试)
| Dimension | Playwright | Cypress |
|---|---|---|
| Browsers | Chromium, Firefox, WebKit | Chromium, Firefox, WebKit (limited) |
| Multi-tab / multi-origin | Yes | Limited |
| Parallelism | Built-in shards | Paid dashboard or external |
| Network interception | Robust route-level | cy.intercept |
| Default | Choose Playwright for new projects unless team already standardized on Cypress | Choose Cypress when team has heavy investment |
| 维度 | Playwright | Cypress |
|---|---|---|
| 浏览器支持 | Chromium、Firefox、WebKit | Chromium、Firefox、WebKit(有限支持) |
| 多标签/多源 | 支持 | 有限支持 |
| 并行测试 | 内置分片 | 需付费仪表盘或外部工具 |
| 网络拦截 | 强大的路由级拦截 | cy.intercept |
| 选择建议 | 新项目优先选Playwright,除非团队已标准化使用Cypress | 当团队对Cypress有大量投入时选择 |
Case Design Techniques
测试用例设计方法
Use ISTQB Foundation Level black-box techniques to derive what to test inside each chosen test type. References: ISTQB BVA white paper, ASTQB black-box techniques.
使用ISTQB基础级黑盒技术,推导每个选中测试类型中需要测试的内容。参考资料:ISTQB边界值分析白皮书、ASTQB黑盒技术。
1. Equivalence Partitioning (EP)
1. 等价类划分(EP)
Divide input domain into partitions where the system is expected to behave the same way; ONE test per partition is sufficient.
Worked example — :
discount(orderTotal: number) -> number| Partition | Range | Representative test input | Expected |
|---|---|---|---|
| Below threshold | | | |
| Mid tier | | | |
| Top tier | | | |
| Invalid (negative) | | | |
Four tests cover all partitions. EP alone misses boundaries — combine with BVA.
将输入域划分为系统预期行为一致的分区;每个分区只需一个测试用例。
示例 — :
discount(orderTotal: number) -> number| 分区 | 范围 | 代表性测试输入 | 预期结果 |
|---|---|---|---|
| 低于阈值 | | | |
| 中间层级 | | | |
| 最高层级 | | | |
| 无效输入(负数) | | | |
四个测试用例覆盖所有分区。仅使用等价类划分会遗漏边界——需结合边界值分析。
2. Boundary Value Analysis (BVA)
2. 边界值分析(BVA)
Bugs cluster at boundaries. For every boundary value , test , , (or for floats, the smallest representable step).
BB-1BB+1Worked example — same function, boundary at :
discount100| Test input | Why | Expected |
|---|---|---|
| Last value of "below threshold" partition | |
| First value of "mid tier" partition | |
| Confirms not off-by-two | |
Repeat for boundary at : test , , . Total: 6 boundary tests + 4 EP tests = 10 cases.
500499500501The triplet has the same shape across boundaries (vary input, vary expected output, identical assertion); this is a natural fit for a table-driven test (see sub-section 5 below).
B-1 / B / B+1缺陷集中在边界处。对于每个边界值,测试**、、**(浮点数则测试最小可表示步长)。
BB-1BB+1示例 — 同一函数,边界为:
discount100| 测试输入 | 原因 | 预期结果 |
|---|---|---|
| “低于阈值”分区的最后一个值 | |
| “中间层级”分区的第一个值 | |
| 确认没有差一错误 | |
对边界重复此操作:测试、、。总计:6个边界测试 + 4个等价类测试 = 10个用例。
500499500501B-1 / B / B+13. Decision Tables
3. 决策表
When output depends on combinations of conditions. Each column is a rule.
Worked example — :
canCheckout(cartHasItems, paymentValid, addressOnFile)| Condition / Rule | R1 | R2 | R3 | R4 |
|---|---|---|---|---|
| cartHasItems | T | T | T | F |
| paymentValid | T | T | F | * |
| addressOnFile | T | F | * | * |
| Result | allow | block:address | block:payment | block:cart |
Four tests, one per rule ( = don't care, dropped via merging).
*当输出取决于多个条件的组合时使用。每一列是一条规则。
示例 — :
canCheckout(cartHasItems, paymentValid, addressOnFile)| 条件/规则 | R1 | R2 | R3 | R4 |
|---|---|---|---|---|
| cartHasItems | 是 | 是 | 是 | 否 |
| paymentValid | 是 | 是 | 否 | * |
| addressOnFile | 是 | 否 | * | * |
| 结果 | 允许结账 | 阻止:缺少地址 | 阻止:支付无效 | 阻止:购物车为空 |
四个测试用例,对应每条规则( = 无关项,可合并省略)。
*4. State Transition
4. 状态转换
When behavior depends on history. Identify states, events, and forbidden transitions.
Worked example — Order state machine with states :
{draft, submitted, paid, shipped, cancelled}| From | Event | To | Test |
|---|---|---|---|
| draft | submit | submitted | happy path |
| submitted | pay | paid | happy path |
| paid | ship | shipped | happy path |
| draft | cancel | cancelled | early cancel |
| paid | cancel | reject | forbidden — refund flow required, NOT direct cancel |
| shipped | submit | reject | forbidden |
Cover one test per legal transition + one per forbidden transition (negative path).
当行为取决于历史状态时使用。识别状态、事件和禁止的转换。
示例 — 订单状态机,状态包括:
{draft, submitted, paid, shipped, cancelled}| 起始状态 | 事件 | 目标状态 | 测试说明 |
|---|---|---|---|
| draft | submit | submitted | 正常路径 |
| submitted | pay | paid | 正常路径 |
| paid | ship | shipped | 正常路径 |
| draft | cancel | cancelled | 提前取消 |
| paid | cancel | 拒绝 | 禁止操作——需走退款流程,不可直接取消 |
| shipped | submit | 拒绝 | 禁止操作 |
每个合法转换对应一个测试用例,每个禁止转换对应一个测试用例(负向路径)。
5. Table-Driven Tests
5. 表格驱动测试
When EP, BVA, or decision-table analysis yields 3+ cases with the same shape (same setup, same assertion, only inputs and expected outputs differ — e.g., parsing valid/invalid date formats; computing tax across brackets; routing rules) collapse them into a single table-driven test. The cases become rows in a data table; the test body iterates the rows and runs one assertion per row. References: Dave Cheney, Prefer table-driven tests; Go wiki: TableDrivenTests.
Do NOT force a table when setup, framework calls, or the assertion shape varies substantially across cases. Forced uniformity hides real differences behind a single name and produces obscure failure messages — keep those as separate, individually named tests.
Worked example — six EP+BVA cases for (boundary at ) collapsed into one table-driven unit test (TS / vitest syntax; the same pattern applies to Go , JUnit , pytest ):
discount(orderTotal)100t.Run@ParameterizedTestparametrizets
describe("discount", () => {
const cases: Array<{ name: string; input: number; expected: number }> = [
{ name: "EP: below threshold (typical)", input: 50, expected: 0 },
{ name: "BVA: B-1 at boundary 100", input: 99, expected: 0 },
{ name: "BVA: B at boundary 100", input: 100, expected: 0.05 },
{ name: "BVA: B+1 at boundary 100", input: 101, expected: 0.05 },
{ name: "EP: mid tier (typical)", input: 250, expected: 0.05 },
{ name: "EP: top tier (typical)", input: 1000, expected: 0.10 },
];
for (const c of cases) {
it(c.name, () => {
expect(discount(c.input)).toBe(c.expected);
});
}
});The column is mandatory: each row must produce an individually addressable test so failures point to the specific case, not "row 3 of 6". Rows that need a different assertion (e.g., the negative-input case throws) stay as separate tests outside the table.
name当等价类划分、边界值分析或决策表分析产生3+个模式相同的用例(相同的前置条件、相同的断言,仅输入和预期输出不同——例如解析有效/无效日期格式、计算不同区间的税费、路由规则)时,将它们合并为一个表格驱动测试。用例成为数据表中的行;测试主体遍历行并为每行执行一次断言。参考资料:Dave Cheney,优先使用表格驱动测试;Go wiki: TableDrivenTests。
如果用例的前置条件、框架调用或断言模式差异较大,不要强行合并为表格。强行统一会掩盖真实差异,导致模糊的失败信息——应将这些用例保留为单独的、命名明确的测试。
示例 — 将的6个等价类+边界值用例(边界为100)合并为一个表格驱动单元测试(TS/vitest语法;同样的模式适用于Go 、JUnit 、pytest ):
discount(orderTotal)t.Run@ParameterizedTestparametrizets
describe("discount", () => {
const cases: Array<{ name: string; input: number; expected: number }> = [
{ name: "EP: 低于阈值(典型值)", input: 50, expected: 0 },
{ name: "BVA: 边界100的B-1", input: 99, expected: 0 },
{ name: "BVA: 边界100的B", input: 100, expected: 0.05 },
{ name: "BVA: 边界100的B+1", input: 101, expected: 0.05 },
{ name: "EP: 中间层级(典型值)", input: 250, expected: 0.05 },
{ name: "EP: 最高层级(典型值)", input: 1000, expected: 0.10 },
];
for (const c of cases) {
it(c.name, () => {
expect(discount(c.input)).toBe(c.expected);
});
}
});nameDependency Decision
依赖决策
For Gate 2 (Integration) and Gate 3 (Component/E2E), choose dependencies deliberately. The goal is maximum realism that still runs deterministically in CI.
| Dependency style | Use when | Avoid when | Notes |
|---|---|---|---|
| Real infra via Testcontainers | DB/Redis/Kafka/Browser, dev needs real driver behavior, hermetic CI required | Cold-start budget < 1s, no Docker available | Default for integration tests on Postgres / Redis / Kafka / Localstack |
| In-memory fake | Owned interface, semantics are simple (key-value, list), test speed critical | Fake diverges from real — silent bugs at integration boundary | Acceptable for repository ports in hexagonal architectures, IF the port has its own contract test against real infra |
| Mock (test double) | Single collaborator with pure interface; test focuses on protocol (was X called with Y) | You're mocking >2 collaborators or mocking data structures (anti-pattern: incomplete mocks) | Mocks are tools to isolate, not things to test |
| Stubbed HTTP | Calling external SaaS where Testcontainers / Localstack option doesn't exist | When Pact / CDC is needed (use contract tests instead) | nock (Node), responses (Python), WireMock (JVM) |
| Real external service | Smoke test in staging only | Unit / integration / CI — always non-deterministic | Reserve for smoke tests against staging |
Tradeoff summary: Testcontainers > in-memory fake > mock, but cost goes the same direction. Pick the cheapest level that doesn't lie about the boundary's behavior.
对于Gate 2(集成测试)和Gate 3(组件/端到端测试),需谨慎选择依赖。目标是在CI中仍能确定性运行的最大程度真实环境。
| 依赖类型 | 使用场景 | 避免场景 | 说明 |
|---|---|---|---|
| 通过Testcontainers使用真实基础设施 | 数据库/Redis/Kafka/浏览器,开发需要真实驱动行为,CI需要封闭环境 | 冷启动预算<1s,无Docker可用 | Postgres/Redis/Kafka/Localstack集成测试的默认选择 |
| 内存模拟对象 | 自有接口,语义简单(键值对、列表),测试速度至关重要 | 模拟对象与真实实现差异大——集成边界处出现隐性缺陷 | 六边形架构中的仓储端口可接受,前提是该端口有针对真实基础设施的契约测试 |
| Mock(测试替身) | 单个协作方且接口纯粹;测试聚焦于协议(是否以Y参数调用X) | 模拟>2个协作方或模拟数据结构(反模式:不完整的Mock) | Mock是隔离工具,而非测试对象 |
| Stubbed HTTP | 调用外部SaaS且无Testcontainers/Localstack选项 | 需要Pact/CDC时(改用契约测试) | nock(Node)、responses(Python)、WireMock(JVM) |
| 真实外部服务 | 仅在staging环境的冒烟测试中使用 | 单元/集成/CI测试——始终非确定性 | 仅用于staging环境的冒烟测试 |
权衡总结:Testcontainers > 内存模拟对象 > Mock,但成本也随之升高。选择不会扭曲边界行为的最低成本方案。
Strategic Skip Heuristics
策略性跳过启发规则
Explicit "don't bother" rules. Skipping these is not laziness — it is risk-adjusted ROI per ISO/IEC/IEEE 29119 risk-based testing and Risk-Based Testing.
| Skip | Rule |
|---|---|
| No e2e for internal helpers | If artifact has no UI surface and no user-facing path, skip e2e. Unit + integration is sufficient. |
| No contract test for bound by deploy consumer API | If only one client consumes the API and they deploy together, contract testing adds maintenance with no decoupling benefit. |
| No property-based on small finite domains | If input space is |
| No integration test for pure functions | Adding a Postgres container to test a |
| No component test for static markup | If the component has no state, no events, no conditional rendering, a snapshot is enough — or skip entirely. |
| No unit test for declarative wiring | DI bindings, route registration, schema declarations: assert at integration level (does the route serve the right handler) instead. |
| No e2e for things integration covers reliably | Per Google e2e principles: the smaller the test you can use to cover a behavior, the better. e2e is the exception, not the default. |
| No tests for spike/throwaway code | Per Beck TDD: if the artifact will be deleted within hours, document the exception with the human partner. Then write tests on the kept version. |
| No "and" tests | If a test name contains "and", split it into separate tests (one assertion per behavior). |
明确的“无需费心”规则。跳过这些测试不是懒惰——而是基于ISO/IEC/IEEE 29119风险测试和基于风险的测试进行的风险调整投资回报率决策。
| 跳过规则 | 说明 |
|---|---|
| 内部辅助工具无需端到端测试 | 如果工件无UI界面且无用户可见路径,跳过端到端测试。单元+集成测试已足够。 |
| 与消费者同步部署的API无需契约测试 | 如果仅一个客户端消费API且双方同步部署,契约测试只会增加维护成本,无解耦收益。 |
| 小而有限的输入域无需基于属性的测试 | 如果输入域是 |
| 纯函数无需集成测试 | 为测试 |
| 静态标记无需组件测试 | 如果组件无状态、无事件、无条件渲染,快照测试已足够——或完全跳过。 |
| 声明式wiring无需单元测试 | 依赖注入绑定、路由注册、模式声明:在集成层面断言(路由是否指向正确的处理器)即可。 |
| 集成测试能可靠覆盖的内容无需端到端测试 | 根据Google端到端测试原则:能覆盖行为的测试越小越好。端到端测试是例外,而非默认选择。 |
| 探索性/一次性代码无需测试 | 根据Beck TDD:如果工件将在数小时内删除,与团队伙伴记录此例外情况。然后在保留的版本上编写测试。 |
| 避免“和”测试 | 如果测试名称包含“和”,将其拆分为单独的测试(每个行为对应一个断言)。 |
Test Matrix Schema
测试矩阵模式
Every test strategy MUST be expressed as the YAML block below. Field ordering inside each list entry is load-bearing — judges and downstream tools parse the first key as the critical one (rationale / reason / why), and the second key as the categorical one (type / what).
每个测试策略必须以下面的YAML块表示。每个列表条目中的字段顺序是固定要求——评审人员和下游工具会将第一个键视为关键项(理由/原因/为什么),第二个键视为分类项(类型/是什么)。
Schema
模式
yaml
test_strategy:
artifact: "<path or short identifier>"
rationale: "Why this test strategy is being applied to this artifact (specific, evidence-based)"
criticality: "NONE | LOW | MEDIUM | MEDIUM-HIGH | HIGH"
selected_types:
- rationale: "Why this type is being applied to this artifact (specific, evidence-based)"
type: "unit | integration | component | e2e | smoke | contract | property-based"
size: "small | medium | large | enormous"
framework: "vitest | jest | pytest | go test | JUnit | playwright | cypress | pact | hypothesis | ..."
dependencies:
- "List of dependencies: real Postgres via Testcontainers, in-memory fake, mocked HTTP via nock, etc."
gate: "Gate N (the gate that triggered this selection)"
rejected_types:
- reason: "Why this type does NOT apply to this artifact (cite Strategic Skip Heuristic or gate that did not trigger)"
type: "unit | integration | component | e2e | smoke | contract | property-based"
deliberately_skipped:
- why: "Cost / risk justification for skipping despite a partial signal"
what: "A specific category of test cases being skipped (e.g., 'browser compatibility on IE11', 'load testing beyond 100 RPS')"yaml
test_strategy:
artifact: "<路径或短标识符>"
rationale: "为什么此测试策略应用于该工件(具体、基于证据)"
criticality: "NONE | LOW | MEDIUM | MEDIUM-HIGH | HIGH"
selected_types:
- rationale: "为什么此测试类型应用于该工件(具体、基于证据)"
type: "unit | integration | component | e2e | smoke | contract | property-based"
size: "small | medium | large | enormous"
framework: "vitest | jest | pytest | go test | JUnit | playwright | cypress | pact | hypothesis | ..."
dependencies:
- "依赖列表:通过Testcontainers使用真实Postgres、内存模拟对象、通过nock模拟HTTP等"
gate: "Gate N(触发此选择的决策门)"
rejected_types:
- reason: "为什么此测试类型不适用于该工件(引用策略性跳过启发规则或未触发的决策门)"
type: "unit | integration | component | e2e | smoke | contract | property-based"
deliberately_skipped:
- why: "跳过的成本/风险理由(尽管有部分信号)"
what: "被跳过的特定测试用例类别(例如:'IE11浏览器兼容性'、'超过100 RPS的负载测试')"Worked YAML Example
YAML示例
yaml
test_strategy:
artifact: "POST /users (user registration endpoint)"
rationale: "User registration is a critical user-facing path; can be used by web and mobile apps independently of each other."
criticality: "MEDIUM-HIGH"
selected_types:
- rationale: "Endpoint contains validation logic (email format, password rules, uniqueness) — Gate 1 ON for branch coverage"
type: "unit"
size: "small"
framework: "vitest"
dependencies: ["in-memory user repository fake"]
gate: "Gate 1"
- rationale: "Endpoint writes to Postgres and emits user.created event to Kafka — Gate 2 ON, real boundary behavior matters"
type: "integration"
size: "medium"
framework: "vitest + supertest + Testcontainers"
dependencies: ["Postgres 15 via Testcontainers", "Kafka via Testcontainers"]
gate: "Gate 2"
- rationale: "Consumed by mobile app and web app on independent deploy cadences — Gate 4 ON, prevents drift"
type: "contract"
size: "medium"
framework: "Pact"
dependencies: ["Pact broker"]
gate: "Gate 4"
rejected_types:
- reason: "No UI surface in this artifact — Gate 3 OFF"
type: "component"
- reason: "No UI surface — Gate 3 OFF; e2e covered by web/mobile apps separately"
type: "e2e"
- reason: "Input domain (email, password) is large but invariants are well-covered by EP+BVA at unit level — property-based ROI is low at MEDIUM-HIGH criticality, only triggers Gate 6 partially"
type: "property-based"
deliberately_skipped:
- why: "Project does not have post-deploy probe pipeline yet; smoke would be no-op"
what: "Smoke test for /users after deploy"
- why: "Non-functional load testing is out of scope for this task; tracked separately in performance backlog"
what: "Load test verifying p99 < 200ms at 1000 RPS"Field ordering checklist (judges check this verbatim):
- :
test_strategyBEFOREartifactBEFORErationale.criticality - :
selected_types[*]BEFORErationaleBEFOREtypeBEFOREsizeBEFOREframeworkBEFOREdependencies.gate - :
rejected_types[*]BEFOREreason.type - :
deliberately_skipped[*]BEFOREwhy.what
yaml
test_strategy:
artifact: "POST /users(用户注册端点)"
rationale: "用户注册是关键用户路径;可被网页和移动应用独立使用。"
criticality: "MEDIUM-HIGH"
selected_types:
- rationale: "端点包含验证逻辑(邮箱格式、密码规则、唯一性)——Gate 1启用以覆盖分支"
type: "unit"
size: "small"
framework: "vitest"
dependencies: ["内存用户仓储模拟对象"]
gate: "Gate 1"
- rationale: "端点写入Postgres并向Kafka发送user.created事件——Gate 2启用,真实边界行为至关重要"
type: "integration"
size: "medium"
framework: "vitest + supertest + Testcontainers"
dependencies: ["通过Testcontainers使用Postgres 15", "通过Testcontainers使用Kafka"]
gate: "Gate 2"
- rationale: "被移动应用和网页应用以独立部署节奏消费——Gate 4启用,防止漂移"
type: "contract"
size: "medium"
framework: "Pact"
dependencies: ["Pact broker"]
gate: "Gate 4"
rejected_types:
- reason: "该工件无UI界面——Gate 3禁用"
type: "component"
- reason: "无UI界面——Gate 3禁用;端到端测试由网页/移动应用单独覆盖"
type: "e2e"
- reason: "输入域(邮箱、密码)庞大,但等价类+边界值分析在单元测试层面已充分覆盖——基于属性的测试在MEDIUM-HIGH关键程度下投资回报率低,仅部分触发Gate 6"
type: "property-based"
deliberately_skipped:
- why: "项目尚无部署后探测流水线;冒烟测试无实际作用"
what: "部署后对/users的冒烟测试"
- why: "非功能性负载测试不在本次任务范围内;在性能待办事项中单独跟踪"
what: "负载测试验证1000 RPS下p99 < 200ms"字段顺序检查清单(评审人员会严格检查):
- :
test_strategy在artifact之前,rationale在rationale之前。criticality - :
selected_types[*]在rationale之前,type在type之前,size在size之前,framework在framework之前,dependencies在dependencies之前。gate - :
rejected_types[*]在reason之前。type - :
deliberately_skipped[*]在why之前。what
Case Listing Schema
测试用例列表模式
After the matrix, produce a flat markdown bullet list of test cases to be implemented. This is separate from the YAML matrix because:
- a. it lists what to test, not how
- b. it links back to acceptance criteria
在矩阵之后,生成一份扁平的Markdown无序列表,列出要实现的测试用例。这与YAML矩阵分离,因为:
- a. 它列出测试内容,而非测试方式
- b. 它关联回验收标准
Format
格式
markdown
undefinedmarkdown
undefinedTest Cases to Cover
需覆盖的测试用例
AC-N: [criterion title]
AC-N: [标准标题]
- [type] description
- [type] description
- [type] 描述
- [type] 描述
AC-N: [criterion title]
AC-N: [标准标题]
- [type] description
- [type] description
Where:
- `type` matches one of `selected_types[*].type` from the matrix
- `description` follows AAA / [Given-When-Then (Dan North BDD)](https://dannorth.net/introducing-bdd/) shape — see [Bill Wake AAA (2001)](https://xp123.com/articles/3a-arrange-act-assert/)
- `AC-N` references the acceptance criterion the case verifies (omit if non-AC-bound, e.g., infrastructure smoke)- [type] 描述
- [type] 描述
其中:
- `type` 与矩阵中 `selected_types[*].type` 匹配
- `description` 遵循AAA / [Given-When-Then(Dan North BDD)](https://dannorth.net/introducing-bdd/)格式——参考[Bill Wake AAA(2001)](https://xp123.com/articles/3a-arrange-act-assert/)
- `AC-N` 引用该用例验证的验收标准(非验收标准绑定的用例可省略,例如基础设施冒烟测试)Worked Example
示例
markdown
undefinedmarkdown
undefinedTest Cases to Cover
需覆盖的测试用例
AC-1: Discount returns the correct percentage based on the total
AC-1: 折扣根据总额返回正确的百分比
- [unit] discount returns 0% when total = 0 [EP partition: below threshold]
- [unit] discount returns 0% when total = 99 [BVA: B-1 at boundary 100]
- [unit] discount returns 5% when total = 100 [BVA: B at boundary 100]
- [unit] discount returns 5% when total = 101 [BVA: B+1 at boundary 100]
- [unit] 当总额=0时,折扣返回0% [等价类:低于阈值]
- [unit] 当总额=99时,折扣返回0% [边界值分析:边界100的B-1]
- [unit] 当总额=100时,折扣返回5% [边界值分析:边界100的B]
- [unit] 当总额=101时,折扣返回5% [边界值分析:边界100的B+1]
AC-2: Discount fails when total is invalid
AC-2: 总额无效时折扣失败
- [unit] discount throws when total = -1 [EP partition: invalid]
- [unit] 当总额=-1时,折扣抛出异常 [等价类:无效输入]
AC-3: /orders saves the order to the database
AC-3: /orders将订单保存到数据库
- [integration] POST /orders persists order to Postgres and returns 201 with order id
- [integration] POST /orders将订单持久化到Postgres并返回201和订单ID
AC-4: /orders rejects duplicate idempotency key
AC-4: /orders拒绝重复的幂等键
- [integration] POST /orders rejects duplicate idempotency key with 409
- [integration] POST /orders拒绝重复的幂等键并返回409
AC-5: /orders/:id returns order by id
AC-5: /orders/:id根据ID返回订单
- [contract] GET /orders/:id returns schema matching mobile-app pact
---- [contract] GET /orders/:id返回与移动应用契约匹配的模式
---Sources & Further Reading
资料与延伸阅读
These 14 sources back every gate and rule above. When in doubt, consult the source linked at that gate.
- Test Pyramid — Mike Cohn (2009, Succeeding with Agile) + Ham Vocke, The Practical Test Pyramid, martinfowler.com.
- Testing Trophy — Kent C. Dodds (2018), The Testing Trophy and Testing Classifications and Write Tests.
- Google Test Sizes — Mike Bland (2011), Small / Medium / Large; Software Engineering at Google Ch.11; Test Sizes (Google Testing Blog).
- Google Testing on the Toilet — What Makes a Good End-to-End Test, Testing UI Logic - Follow the User, Origins (Mike Bland).
- ISTQB Foundation Level — Black-box techniques: Boundary Value Analysis white paper; ASTQB Black-Box Techniques.
- ISO/IEC/IEEE 29119 — Risk-based test process standard. Wikipedia overview.
- Kent Beck — Test Driven Development: By Example (Addison-Wesley, 2002). Publisher page. ISBN 978-0321146533.
- The Pragmatic Programmer (20th Anniversary Edition) — Hunt & Thomas (2019). pragprog.com.
- AAA pattern — Bill Wake (2001), 3A — Arrange, Act, Assert. Given-When-Then — Dan North, Introducing BDD.
- Property-based testing — Hypothesis: What is property-based testing?; QuickCheck (Haskell), fast-check (TS).
- Contract testing / Consumer-Driven Contracts — Pact docs; Pactflow CDC explainer.
- Testcontainers — testcontainers.com.
- Table-driven tests — Dave Cheney, Prefer table-driven tests; Go wiki: TableDrivenTests.
- Risk-based testing — Risk Management During Test Planning (softwaretestinghelp.com).
上述每个决策门和规则都基于以下14个资料。有疑问时,参考对应决策门链接的资料。
- 测试金字塔——Mike Cohn(2009,《敏捷成功之道》)+ Ham Vocke,《实用测试金字塔》,martinfowler.com。
- 测试奖杯——Kent C. Dodds(2018),《测试奖杯与测试分类》和《编写测试》。
- Google测试规模——Mike Bland(2011),《小型/中型/大型》;《Google软件工程》第11章;《测试规模》(Google测试博客)。
- Google测试厕所读物——《优质端到端测试的标准》、《测试UI逻辑——跟随用户》、《起源(Mike Bland)》。
- ISTQB基础级——黑盒技术:《边界值分析白皮书》;《ASTQB黑盒技术》。
- ISO/IEC/IEEE 29119——基于风险的测试流程标准。维基百科概述。
- Kent Beck——《测试驱动开发:实例解析》(Addison-Wesley,2002)。出版社页面。ISBN 978-0321146533。
- 《程序员修炼之道》(20周年版)——Hunt & Thomas(2019)。pragprog.com。
- AAA模式——Bill Wake(2001),《3A——Arrange, Act, Assert》。Given-When-Then——Dan North,《介绍BDD》。
- 基于属性的测试——《Hypothesis:什么是基于属性的测试?》;QuickCheck(Haskell)、fast-check(TS)。
- 契约测试/消费者驱动契约——《Pact文档》;《Pactflow CDC说明》。
- Testcontainers——testcontainers.com。
- 表格驱动测试——Dave Cheney,《优先使用表格驱动测试》;Go wiki: TableDrivenTests。
- 基于风险的测试——《测试规划期间的风险管理》(softwaretestinghelp.com)。
Worked Examples
完整示例
Each example shows:
- a. the artifact and acceptance criteria
- b. gate-by-gate walkthrough
- c. YAML following the schema
test_strategy - d. list
Test Cases to Cover - e. commentary on rejected types
每个示例包含:
- a. 工件和验收标准
- b. 逐个决策门的分析
- c. 遵循模式的YAML
test_strategy - d. 列表
需覆盖的测试用例 - e. 未选中测试类型的说明
Example A — Pure Helper Function: formatCurrency(amount: number, code: string): string
formatCurrency(amount: number, code: string): string示例A——纯辅助函数:formatCurrency(amount: number, code: string): string
formatCurrency(amount: number, code: string): stringArtifact
ts
function formatCurrency(amount: number, code: string): string;
// e.g. formatCurrency(1234.5, "USD") -> "$1,234.50"
// formatCurrency(1234.5, "EUR") -> "€1.234,50"Acceptance criteria:
- AC-1: USD output uses prefix, comma thousands, period decimal, two decimal places.
$ - AC-2: EUR output uses prefix, period thousands, comma decimal, two decimal places.
€ - AC-3: Throws for unsupported codes.
Error("Unknown currency code") - AC-4: formats as
amount = 0/"$0.00"."€0,00"
Criticality: (helper used in display only, no money movement here).
LOWGate Walkthrough
| Gate | Decision | Reason |
|---|---|---|
| 0 Skip | OFF | Has logic |
| 1 Unit | ON | Pure logic with branches per currency code — Test Pyramid base |
| 2 Integration | OFF | No I/O, no boundary — Skip Heuristic: no integration for pure functions |
| 3 Component/E2E | OFF | No UI surface |
| 4 Contract | OFF | Not a public API |
| 5 Smoke | OFF | Not deployable |
| 6 Property-Based | ON (partial) | Numeric input is unbounded, but invariants exist (round-trip via parse, monotonicity in amount) — Hypothesis. Promote at MEDIUM-HIGH; here LOW criticality means we apply it sparingly (1-2 properties) |
test_strategyyaml
test_strategy:
artifact: "src/util/formatCurrency.ts"
rationale: "Pure helper function used in display only; no money movement here."
criticality: "LOW"
selected_types:
- rationale: "Pure logic with currency-specific branches and number formatting; EP+BVA on amount, decision table on currency code"
type: "unit"
size: "small"
framework: "vitest"
dependencies: []
gate: "Gate 1"
- rationale: "Amount domain is unbounded floats; invariant 'parseCurrency(formatCurrency(x, c)) ~= x' is stable; sparingly applied (1-2 properties) at LOW criticality"
type: "property-based"
size: "small"
framework: "fast-check"
dependencies: []
gate: "Gate 6"
rejected_types:
- reason: "No I/O, no boundary, no collaborators - Gate 2 OFF"
type: "integration"
- reason: "No UI surface - Gate 3 OFF"
type: "component"
- reason: "No UI surface - Gate 3 OFF"
type: "e2e"
- reason: "Internal helper, not consumed across deploys - Gate 4 OFF"
type: "contract"
- reason: "Library helper, no deploy pipeline target - Gate 5 OFF"
type: "smoke"
deliberately_skipped:
- why: "Locale list is finite (USD, EUR); exhaustive enumeration via decision table is sufficient and more maintainable than i18n property tests"
what: "Property-based fuzzing of currency code beyond known list"Test Cases to Cover
markdown
undefined工件
ts
function formatCurrency(amount: number, code: string): string;
// 例如 formatCurrency(1234.5, "USD") -> "$1,234.50"
// formatCurrency(1234.5, "EUR") -> "€1.234,50"验收标准:
- AC-1: USD输出使用前缀、千位逗号、小数点、两位小数。
$ - AC-2: EUR输出使用前缀、千位点、小数点逗号、两位小数。
€ - AC-3: 对不支持的代码抛出。
Error("Unknown currency code") - AC-4: 格式化为
amount = 0/"$0.00"。"€0,00"
关键程度: (仅用于显示的辅助工具,无资金流转)。
LOW决策门分析
| 决策门 | 决策 | 理由 |
|---|---|---|
| 0 跳过 | 禁用 | 包含逻辑 |
| 1 单元测试 | 启用 | 包含按货币代码分支的纯逻辑——测试金字塔基础层 |
| 2 集成测试 | 禁用 | 无I/O、无边界——跳过启发规则:纯函数无需集成测试 |
| 3 组件/端到端测试 | 禁用 | 无UI界面 |
| 4 契约测试 | 禁用 | 非公开API |
| 5 冒烟测试 | 禁用 | 不可部署 |
| 6 基于属性的测试 | 部分启用 | 数值输入无界,但存在不变量(解析往返、金额单调性)——Hypothesis。MEDIUM-HIGH关键程度下可推广;此处LOW关键程度意味着谨慎应用(1-2个属性) |
test_strategyyaml
test_strategy:
artifact: "src/util/formatCurrency.ts"
rationale: "仅用于显示的纯辅助函数;无资金流转。"
criticality: "LOW"
selected_types:
- rationale: "包含货币特定分支和数值格式化的纯逻辑;对金额使用等价类+边界值分析,对货币代码使用决策表"
type: "unit"
size: "small"
framework: "vitest"
dependencies: []
gate: "Gate 1"
- rationale: "金额域是无界浮点数;不变量'parseCurrency(formatCurrency(x, c)) ~= x'稳定;LOW关键程度下谨慎应用(1-2个属性)"
type: "property-based"
size: "small"
framework: "fast-check"
dependencies: []
gate: "Gate 6"
rejected_types:
- reason: "无I/O、无边界、无协作方——Gate 2禁用"
type: "integration"
- reason: "无UI界面——Gate 3禁用"
type: "component"
- reason: "无UI界面——Gate 3禁用"
type: "e2e"
- reason: "内部辅助工具,跨部署无消费——Gate 4禁用"
type: "contract"
- reason: "类库辅助工具,无部署流水线目标——Gate 5禁用"
type: "smoke"
deliberately_skipped:
- why: "区域列表有限(USD、EUR);通过决策表穷举枚举已足够,比国际化属性测试更易维护"
what: "超出已知列表的货币代码的基于属性的模糊测试"需覆盖的测试用例
markdown
undefinedAC-1: USD output uses $
prefix, comma thousands, period decimal, two decimal places.
$AC-1: USD输出使用$
前缀、千位逗号、小数点、两位小数。
$- [unit] formatCurrency(1234.5, "USD") returns "$1,234.50" [EP: typical USD]
- [unit] formatCurrency(0.01, "USD") returns "$0.01" [BVA: B+1 smallest non-zero]
- [unit] formatCurrency(-0.01, "USD") returns "-$0.01" [BVA: B-1 negative side]
- [unit] formatCurrency(1234.5, "USD")返回"$1,234.50" [等价类:典型USD]
- [unit] formatCurrency(0.01, "USD")返回"$0.01" [边界值分析:最小非零值B+1]
- [unit] formatCurrency(-0.01, "USD")返回"-$0.01" [边界值分析:负值侧B-1]
AC-2: EUR output uses €
prefix, period thousands, comma decimal, two decimal places.
€AC-2: EUR输出使用€
前缀、千位点、小数点逗号、两位小数。
€- [unit] formatCurrency(1234.5, "EUR") returns "€1.234,50" [EP: typical EUR]
- [property-based] for any non-NaN finite x in [-1e9, 1e9] and code in {USD, EUR}: parseCurrency(formatCurrency(x, code)) is within 0.005 of x [round-trip invariant]
- [unit] formatCurrency(1234.5, "EUR")返回"€1.234,50" [等价类:典型EUR]
- [property-based] 对于[-1e9, 1e9]内的任何非NaN有限值x和{USD, EUR}中的代码:parseCurrency(formatCurrency(x, code))与x的误差在0.005以内 [往返不变量]
AC-3: Throws Error("Unknown currency code")
for unsupported codes.
Error("Unknown currency code")AC-3: 对不支持的代码抛出Error("Unknown currency code")
。
Error("Unknown currency code")- [unit] formatCurrency(1, "XYZ") throws Error("Unknown currency code") [Decision table: unknown code]
- [unit] formatCurrency(1, "XYZ")抛出Error("Unknown currency code") [决策表:未知代码]
AC-4: amount = 0
formats as "$0.00"
/ "€0,00"
.
amount = 0"$0.00""€0,00"AC-4: amount = 0
格式化为"$0.00"
/ "€0,00"
。
amount = 0"$0.00""€0,00"- [unit] formatCurrency(0, "USD") returns "$0.00" [BVA: B at amount=0]
- [unit] formatCurrency(0, "EUR") returns "€0,00" [BVA: B at amount=0 for EUR]
**Why types were rejected**: Helper has no boundaries (no integration), no UI (no component/e2e), is internal and library-style (no contract/smoke), and at LOW criticality the cost of additional test types far exceeds the benefit.
---- [unit] formatCurrency(0, "USD")返回"$0.00" [边界值分析:金额=0的B]
- [unit] formatCurrency(0, "EUR")返回"€0,00" [边界值分析:EUR金额=0的B]
**未选中测试类型的原因**:辅助工具无边界(无需集成测试)、无UI(无需组件/端到端测试)、是内部类库(无需契约/冒烟测试),且LOW关键程度下额外测试类型的成本远超过收益。
---Example B — HTTP POST Endpoint with DB and Multi-Consumer: POST /users
POST /users示例B——带数据库和多消费者的HTTP POST端点:POST /users
POST /usersArtifact
A user-registration endpoint that:
- Validates request body (email format, password complexity, age >= 13).
- Checks email uniqueness against Postgres.
- Inserts user record (transactional).
- Emits event to Kafka.
user.created - Returns with
201.{id, email, createdAt} - Returns for invalid input,
400for duplicate email.409
Consumed by: mobile app (iOS/Android) and web app on independent deploy cadences.
Acceptance criteria:
- AC-1: Valid request returns and persists user.
201 - AC-2: Invalid email format returns with field-level error.
400 - AC-3: Password not meeting policy returns .
400 - AC-4: Duplicate email returns .
409 - AC-5: Successful registration emits exactly one event.
user.created - AC-6: Response schema is stable for mobile + web consumers.
Criticality: (auth surface, identity domain, multi-consumer public API).
MEDIUM-HIGHGate Walkthrough
| Gate | Decision | Reason |
|---|---|---|
| 0 Skip | OFF | Has substantial logic |
| 1 Unit | ON | Validators (email, password, age) are pure logic — Test Pyramid base |
| 2 Integration | ON | Boundary crossing: HTTP, Postgres, Kafka — Testing Trophy ROI sweet spot |
| 3 Component/E2E | OFF (here) | No UI in this artifact; UI lives in mobile + web repos and tests itself |
| 4 Contract | ON | Two distinct consumers (mobile + web) on independent deploy cadences — Pact CDC |
| 5 Smoke | ON | Deployable HTTP service; post-deploy probe of |
| 6 Property-Based | OFF | Input domain (email, password, age) is constrained and well-covered by EP+BVA at unit; criticality is MEDIUM-HIGH but Gate 6 OFF on bounded inputs — Skip Heuristic |
test_strategyyaml
test_strategy:
artifact: "POST /users (user registration endpoint)"
rationale: "User registration is a critical user-facing path; can be used by web and mobile apps independently of each other."
criticality: "MEDIUM-HIGH"
selected_types:
- rationale: "Validators (email, password, age) are pure logic; EP+BVA on each field; one test per partition"
type: "unit"
size: "small"
framework: "vitest"
dependencies: ["in-memory user repository fake (for service-level unit if needed)"]
gate: "Gate 1"
- rationale: "Endpoint writes to Postgres and emits to Kafka; mocking these distorts transactional and ordering behavior - Testcontainers gives real boundary fidelity"
type: "integration"
size: "medium"
framework: "vitest + supertest + Testcontainers"
dependencies: ["Postgres 15 via Testcontainers", "Kafka via Testcontainers"]
gate: "Gate 2"
- rationale: "Public API consumed by mobile + web on independent deploy cadences; contract testing prevents schema drift breaking either consumer"
type: "contract"
size: "medium"
framework: "Pact (provider verification)"
dependencies: ["Pact broker", "consumer-published pacts from mobile and web"]
gate: "Gate 4"
- rationale: "Deployable HTTP service with a post-deploy pipeline; one minimal smoke verifies /users responds 201 in the deployed environment"
type: "smoke"
size: "large"
framework: "Playwright (1 critical path)"
dependencies: ["deployed environment URL", "test account seeding"]
gate: "Gate 5"
rejected_types:
- reason: "No UI surface in this artifact - Gate 3 OFF; mobile and web repos own their own component tests"
type: "component"
- reason: "No UI surface - Gate 3 OFF; consumer e2e lives in mobile/web repos"
type: "e2e"
- reason: "Input domain is bounded and EP+BVA at unit level covers it; property-based on this glue endpoint adds infra without finding more bugs - Gate 6 OFF"
type: "property-based"
deliberately_skipped:
- why: "Performance/load testing is out of scope here; tracked in dedicated performance backlog"
what: "Load test verifying p99 < 200ms at 1000 RPS"
- why: "Cross-region failover is owned by infrastructure team, not this endpoint"
what: "Multi-region availability test"Test Cases to Cover
markdown
undefined工件
用户注册端点,功能包括:
- 验证请求体(邮箱格式、密码复杂度、年龄≥13)。
- 检查邮箱在Postgres中的唯一性。
- 插入用户记录(事务性)。
- 向Kafka发送事件。
user.created - 返回和
201。{id, email, createdAt} - 无效输入返回,重复邮箱返回
400。409
消费者: 移动应用(iOS/Android)和网页应用,部署节奏独立。
验收标准:
- AC-1: 有效请求返回并持久化用户。
201 - AC-2: 无效邮箱格式返回和字段级错误。
400 - AC-3: 密码不符合规则返回。
400 - AC-4: 重复邮箱返回。
409 - AC-5: 注册成功时恰好发送一个事件。
user.created - AC-6: 响应模式对移动+网页消费者稳定。
关键程度: (认证界面、身份领域、多消费者公开API)。
MEDIUM-HIGH决策门分析
| 决策门 | 决策 | 理由 |
|---|---|---|
| 0 跳过 | 禁用 | 包含大量逻辑 |
| 1 单元测试 | 启用 | 验证器(邮箱、密码、年龄)是纯逻辑——测试金字塔基础层 |
| 2 集成测试 | 启用 | 跨边界操作:HTTP、Postgres、Kafka——测试奖杯投资回报率最佳点 |
| 3 组件/端到端测试 | 禁用(此处) | 该工件无UI;UI位于移动+网页仓库中并自行测试 |
| 4 契约测试 | 启用 | 两个独立消费者(移动+网页)部署节奏独立——Pact CDC |
| 5 冒烟测试 | 启用 | 可部署的HTTP服务;部署后探测/users注册有意义——Google端到端测试原则 |
| 6 基于属性的测试 | 禁用 | 输入域(邮箱、密码、年龄)受限,单元测试的等价类+边界值分析已覆盖;关键程度为MEDIUM-HIGH但Gate 6对有界输入禁用——跳过启发规则 |
test_strategyyaml
test_strategy:
artifact: "POST /users(用户注册端点)"
rationale: "用户注册是关键用户路径;可被网页和移动应用独立使用。"
criticality: "MEDIUM-HIGH"
selected_types:
- rationale: "验证器(邮箱、密码、年龄)是纯逻辑;对每个字段使用等价类+边界值分析;每个分区一个测试用例"
type: "unit"
size: "small"
framework: "vitest"
dependencies: ["内存用户仓储模拟对象(如需服务级单元测试)"]
gate: "Gate 1"
- rationale: "端点写入Postgres并发送到Kafka;模拟这些会扭曲事务和顺序行为 - Testcontainers提供真实边界保真度"
type: "integration"
size: "medium"
framework: "vitest + supertest + Testcontainers"
dependencies: ["通过Testcontainers使用Postgres 15", "通过Testcontainers使用Kafka"]
gate: "Gate 2"
- rationale: "公开API被移动+网页以独立部署节奏消费;契约测试防止模式漂移导致消费者崩溃"
type: "contract"
size: "medium"
framework: "Pact(提供者验证)"
dependencies: ["Pact broker", "来自移动和网页的消费者发布契约"]
gate: "Gate 4"
- rationale: "可部署的HTTP服务有部署后流水线;一个最小冒烟测试验证部署环境中/users返回201"
type: "smoke"
size: "large"
framework: "Playwright(1条关键路径)"
dependencies: ["部署环境URL", "测试账号预填充"]
gate: "Gate 5"
rejected_types:
- reason: "该工件无UI界面——Gate 3禁用;移动和网页仓库负责各自的组件测试"
type: "component"
- reason: "无UI界面——Gate 3禁用;消费者端到端测试位于移动/网页仓库"
type: "e2e"
- reason: "输入域有界,单元测试的等价类+边界值分析已覆盖;对该粘合端点使用基于属性的测试只会增加基础设施成本,不会发现更多缺陷——Gate 6禁用"
type: "property-based"
deliberately_skipped:
- why: "性能/负载测试不在本次范围内;在专用性能待办事项中跟踪"
what: "负载测试验证1000 RPS下p99 < 200ms"
- why: "跨区域故障转移由基础设施团队负责,与该端点无关"
what: "多区域可用性测试"需覆盖的测试用例
markdown
undefinedAC-1: Valid request returns 201
and persists user.
201AC-1: 有效请求返回201
并持久化用户。
201- [unit] validateEmail accepts "alice@example.com" [EP: well-formed]
- [integration] POST /users with valid body returns 201 and persists row in Postgres
- [smoke] POST /users in deployed environment returns 201 for a synthetic test account
- [unit] validateEmail接受"alice@example.com" [等价类:格式正确]
- [integration] 向/users发送有效请求返回201并在Postgres中持久化记录
- [smoke] 在部署环境中向/users发送请求,测试账号返回201
AC-2: Invalid email format returns 400
with field-level error.
400AC-2: 无效邮箱格式返回400
和字段级错误。
400- [unit] validateEmail rejects "alice@" [EP: missing domain]
- [unit] validateEmail rejects "" [BVA: empty boundary]
- [integration] POST /users with invalid email returns 400 and does NOT persist
- [unit] validateEmail拒绝"alice@" [等价类:缺少域名]
- [unit] validateEmail拒绝"" [边界值分析:空边界]
- [integration] 向/users发送无效邮箱返回400且不持久化
AC-3: Password not meeting policy returns 400
.
400AC-3: 密码不符合规则返回400
。
400- [unit] validatePassword rejects 7-char password [BVA: B-1 at min length 8]
- [unit] validatePassword accepts 8-char password meeting policy [BVA: B at min length]
- [unit] validatePassword accepts 9-char password [BVA: B+1]
- [unit] validateAge rejects 12 [BVA: B-1 at boundary 13]
- [unit] validateAge accepts 13 [BVA: B at boundary 13]
- [unit] validatePassword拒绝7位密码 [边界值分析:最小长度8的B-1]
- [unit] validatePassword接受符合规则的8位密码 [边界值分析:最小长度的B]
- [unit] validatePassword接受9位密码 [边界值分析:B+1]
- [unit] validateAge拒绝12 [边界值分析:边界13的B-1]
- [unit] validateAge接受13 [边界值分析:边界13的B]
AC-4: Duplicate email returns 409
.
409AC-4: 重复邮箱返回409
。
409- [integration] POST /users with duplicate email returns 409 and does NOT emit event
- [integration] 向/users发送重复邮箱返回409且不发送事件
AC-5: Successful registration emits exactly one user.created
event.
user.createdAC-5: 注册成功时恰好发送一个user.created
事件。
user.created- [integration] POST /users emits exactly one user.created event to Kafka on success
- [integration] POST /users transaction rolls back when Kafka publish fails [State Transition: failure path]
- [integration] 向/users发送请求成功时,向Kafka恰好发送一个user.created事件
- [integration] Kafka发布失败时,向/users发送请求的事务回滚 [状态转换:失败路径]
AC-6: Response schema is stable for mobile + web consumers.
AC-6: 响应模式对移动+网页消费者稳定。
- [contract] Provider satisfies mobile pact: POST /users response shape matches mobile contract
- [contract] Provider satisfies web pact: POST /users response shape matches web contract
**Why types were rejected**: No UI surface (component/e2e belong to consumer apps), bounded input space (property-based ROI low), out-of-scope concerns (load, multi-region) deliberately skipped with rationale.
---- [contract] 提供者满足移动契约:/users响应结构匹配移动契约
- [contract] 提供者满足网页契约:/users响应结构匹配网页契约
**未选中测试类型的原因**:无UI界面(组件/端到端测试属于消费者应用)、输入域有界(基于属性的测试投资回报率低)、超出范围的关注点(负载、多区域)被主动跳过并说明理由。
---Example C — UI Form Component: <RegistrationForm />
(web)
<RegistrationForm />示例C——UI表单组件:<RegistrationForm />
(网页)
<RegistrationForm />Artifact
A React form component:
- Fields: email, password, confirmPassword, age.
- Client-side validation: email format, password >= 8 chars with mixed case + digit, passwords match, age >= 13.
- Submits to .
POST /users - Shows inline field errors and submit-level errors (network, 409 duplicate).
- Disables submit button while pending; re-enables on response.
- WCAG 2.1 AA: labels bound to inputs, errors announced via , focus moves to first error on validation failure.
aria-live
Acceptance criteria:
- AC-1: User can submit a valid form and is navigated to .
/welcome - AC-2: Invalid email shows inline .
"Enter a valid email" - AC-3: Mismatched passwords show inline .
"Passwords must match" - AC-4: Submit is disabled while request is in flight.
- AC-5: 409 response from server shows at form level.
"This email is already registered" - AC-6: Form is keyboard navigable; focus moves to first error on validation failure.
- AC-7: All inputs have programmatic labels; errors are announced via .
aria-live="polite"
Criticality: (registration is a critical user-facing path; accessibility is regulated in many jurisdictions).
MEDIUM-HIGHGate Walkthrough
| Gate | Decision | Reason |
|---|---|---|
| 0 Skip | OFF | Behavior + accessibility logic |
| 1 Unit | ON | Validation helpers ( |
| 2 Integration | OFF (here) | The component itself does not cross a real boundary; network is mocked at fetch level. Network integration is owned by |
| 3 Component/E2E | ON (component) + ON (e2e for the registration path) | UI surface, criticality MEDIUM-HIGH, user-facing critical path — Test Pyramid top + Follow the User |
| 4 Contract | OFF | UI consumes API; provider-side contract tests live in Example B |
| 5 Smoke | ON | Web app is deployed; smoke for "registration page renders and submits" is meaningful |
| 6 Property-Based | OFF | Bounded form inputs; EP+BVA covers them |
test_strategyyaml
test_strategy:
artifact: "src/components/RegistrationForm.tsx"
rationale: "React form component used in web app; registration is a business-critical user-facing path."
criticality: "MEDIUM-HIGH"
selected_types:
- rationale: "Validation helpers (validateEmail, passwordsMatch, parseAge) are pure logic; EP+BVA per field"
type: "unit"
size: "small"
framework: "vitest"
dependencies: []
gate: "Gate 1"
- rationale: "UI rendering + interaction within a single component; network mocked at fetch level - tests focus on user-facing behavior per Follow the User"
type: "component"
size: "small"
framework: "vitest + React Testing Library"
dependencies: ["happy-dom", "msw (mock service worker) for fetch"]
gate: "Gate 3"
- rationale: "Registration is a critical user-facing path; one e2e covers the full happy path with real backend (Testcontainers-backed)"
type: "e2e"
size: "large"
framework: "Playwright"
dependencies: ["app server running locally", "Postgres via Testcontainers", "Kafka via Testcontainers"]
gate: "Gate 3"
- rationale: "Web app deploys to staging/prod; smoke verifies /register page loads and form submits in deployed env"
type: "smoke"
size: "large"
framework: "Playwright (1 critical path)"
dependencies: ["deployed environment URL", "test account seeding"]
gate: "Gate 5"
rejected_types:
- reason: "Component does not own a real boundary; network integration is owned by POST /users (provider) - Gate 2 OFF for this artifact"
type: "integration"
- reason: "UI consumes the API; provider contract tests live with the provider (POST /users) - Gate 4 OFF for the consumer"
type: "contract"
- reason: "Bounded input space; EP+BVA at unit level is sufficient - Gate 6 OFF"
type: "property-based"
deliberately_skipped:
- why: "Cross-browser e2e on legacy browsers (IE11) is out of support per project browser matrix"
what: "Browser compatibility e2e on IE11 / Edge Legacy"
- why: "Visual regression (pixel diff) is owned by a separate Storybook chromatic pipeline"
what: "Pixel-level visual regression assertions"Test Cases to Cover
markdown
undefined工件
React表单组件,功能包括:
- 字段:邮箱、密码、确认密码、年龄。
- 客户端验证:邮箱格式、密码≥8位且包含大小写+数字、密码匹配、年龄≥13。
- 提交到。
POST /users - 显示内联字段错误和提交级错误(网络、409重复)。
- 请求处理中禁用提交按钮;响应后重新启用。
- WCAG 2.1 AA:标签绑定到输入、错误通过宣布、验证失败时焦点移到第一个错误字段。
aria-live
验收标准:
- AC-1: 用户可提交有效表单并导航到。
/welcome - AC-2: 无效邮箱显示内联。
"Enter a valid email" - AC-3: 密码不匹配显示内联。
"Passwords must match" - AC-4: 请求处理中提交按钮禁用。
- AC-5: 服务器返回409时,表单级别显示。
"This email is already registered" - AC-6: 表单可键盘导航;验证失败时焦点移到第一个错误字段。
- AC-7: 所有输入有程序化标签;错误通过宣布。
aria-live="polite"
关键程度: (注册是关键用户路径;可访问性在许多司法管辖区受监管)。
MEDIUM-HIGH决策门分析
| 决策门 | 决策 | 理由 |
|---|---|---|
| 0 跳过 | 禁用 | 包含行为+可访问性逻辑 |
| 1 单元测试 | 启用 | 验证辅助工具( |
| 2 集成测试 | 禁用(此处) | 组件本身不跨真实边界;网络在fetch层模拟。网络集成由 |
| 3 组件/端到端测试 | 启用(组件) + 启用(注册路径端到端) | 有UI界面、关键程度MEDIUM-HIGH、用户关键路径——测试金字塔顶层 + 跟随用户原则 |
| 4 契约测试 | 禁用 | UI消费API;提供者端契约测试在示例B中 |
| 5 冒烟测试 | 启用 | 网页应用可部署;“注册页面渲染并可提交”的冒烟测试有意义 |
| 6 基于属性的测试 | 禁用 | 表单输入有界;等价类+边界值分析已覆盖 |
test_strategyyaml
test_strategy:
artifact: "src/components/RegistrationForm.tsx"
rationale: "网页应用中使用的React表单组件;注册是业务关键用户路径。"
criticality: "MEDIUM-HIGH"
selected_types:
- rationale: "验证辅助工具(validateEmail、passwordsMatch、parseAge)是纯逻辑;对每个字段使用等价类+边界值分析"
type: "unit"
size: "small"
framework: "vitest"
dependencies: []
gate: "Gate 1"
- rationale: "单个组件内的UI渲染+交互;网络在fetch层模拟 - 测试聚焦于用户可见行为(跟随用户原则)"
type: "component"
size: "small"
framework: "vitest + React Testing Library"
dependencies: ["happy-dom", "msw(mock service worker)用于fetch模拟"]
gate: "Gate 3"
- rationale: "注册是关键用户路径;一个端到端测试覆盖完整正常路径(Testcontainers支撑的真实后端)"
type: "e2e"
size: "large"
framework: "Playwright"
dependencies: ["本地运行的应用服务器", "通过Testcontainers使用Postgres", "通过Testcontainers使用Kafka"]
gate: "Gate 3"
- rationale: "网页应用部署到staging/prod;冒烟测试验证部署环境中/register页面加载且表单可提交"
type: "smoke"
size: "large"
framework: "Playwright(1条关键路径)"
dependencies: ["部署环境URL", "测试账号预填充"]
gate: "Gate 5"
rejected_types:
- reason: "组件不拥有真实边界;网络集成由POST /users(提供者)负责——Gate 2对该工件禁用"
type: "integration"
- reason: "UI消费API;提供者契约测试与提供者(POST /users)一起——Gate 4对消费者禁用"
type: "contract"
- reason: "输入域有界;单元测试的等价类+边界值分析已足够——Gate 6禁用"
type: "property-based"
deliberately_skipped:
- why: "项目浏览器矩阵不支持旧浏览器(IE11)的跨浏览器端到端测试"
what: "IE11 / Edge Legacy的浏览器兼容性端到端测试"
- why: "视觉回归(像素对比)由单独的Storybook chromatic流水线负责"
what: "像素级视觉回归断言"需覆盖的测试用例
markdown
undefinedAC-1: User can submit a valid form and is navigated to /welcome
.
/welcomeAC-1: 用户可提交有效表单并导航到/welcome
。
/welcome- [unit] validateEmail accepts "alice@example.com" [EP: well-formed]
- [unit] parseAge rejects 12 [BVA: B-1 at boundary 13]
- [unit] parseAge accepts 13 [BVA: B at boundary 13]
- [e2e] user fills valid form, submits, and lands on /welcome page
- [smoke] /register page loads and form submits in deployed environment
- [unit] validateEmail接受"alice@example.com" [等价类:格式正确]
- [unit] parseAge拒绝12 [边界值分析:边界13的B-1]
- [unit] parseAge接受13 [边界值分析:边界13的B]
- [e2e] 用户填写有效表单、提交并进入/welcome页面
- [smoke] 部署环境中/register页面加载且表单可提交
AC-2: Invalid email shows inline "Enter a valid email"
.
"Enter a valid email"AC-2: 无效邮箱显示内联"Enter a valid email"
。
"Enter a valid email"- [unit] validateEmail rejects "" [BVA: empty boundary]
- [unit] validateEmail rejects "alice@" [EP: missing domain]
- [component] entering invalid email and blurring shows "Enter a valid email" inline
- [unit] validateEmail拒绝"" [边界值分析:空边界]
- [unit] validateEmail拒绝"alice@" [等价类:缺少域名]
- [component] 输入无效邮箱并失焦时显示"Enter a valid email"内联错误
AC-3: Mismatched passwords show inline "Passwords must match"
.
"Passwords must match"AC-3: 密码不匹配显示内联"Passwords must match"
。
"Passwords must match"- [unit] passwordsMatch returns true when both equal "Abcd1234"
- [unit] passwordsMatch returns false when one is "" [BVA: empty]
- [component] entering mismatched passwords shows "Passwords must match" inline
- [unit] passwordsMatch在两个密码均为"Abcd1234"时返回true
- [unit] passwordsMatch在其中一个为空时返回false [边界值分析:空值]
- [component] 输入不匹配密码时显示"Passwords must match"内联错误
AC-4: Submit is disabled while request is in flight.
AC-4: 请求处理中提交按钮禁用。
- [component] submit is disabled when password and confirmPassword differ
- [component] submit click disables button while request is pending [State Transition: idle -> pending]
- [component] 密码与确认密码不同时提交按钮禁用
- [component] 点击提交后,请求处理中按钮禁用 [状态转换:空闲 -> 处理中]
AC-5: 409 response from server shows "This email is already registered"
at form level.
"This email is already registered"AC-5: 服务器返回409时,表单级别显示"This email is already registered"
。
"This email is already registered"- [component] 409 response shows form-level "This email is already registered"
- [component] 409响应时显示表单级别错误"This email is already registered"
AC-6: Form is keyboard navigable; focus moves to first error on validation failure.
AC-6: 表单可键盘导航;验证失败时焦点移到第一个错误字段。
- [component] validation failure moves focus to first error field [a11y]
- [component] 验证失败时焦点移到第一个错误字段 [可访问性]
AC-7: All inputs have programmatic labels; errors are announced via aria-live="polite"
.
aria-live="polite"AC-7: 所有输入有程序化标签;错误通过aria-live="polite"
宣布。
aria-live="polite"- [component] form renders email, password, confirmPassword, age, submit [happy path render]
- [component] all inputs have programmatic labels and errors live in aria-live="polite" region [a11y]
**Why types were rejected**: This artifact is a UI consumer — its real boundary is the API, which is tested as integration in Example B (provider side). Property-based testing is not justified for bounded UI input handling. Cross-browser legacy and visual-regression are out of scope and explicitly skipped with rationale.
---- [component] 表单渲染邮箱、密码、确认密码、年龄、提交按钮 [正常路径渲染]
- [component] 所有输入有程序化标签,错误位于aria-live="polite"区域 [可访问性]
**未选中测试类型的原因**:该工件是UI消费者——其真实边界是API,API的集成测试在示例B(提供者端)中。有界UI输入处理无需基于属性的测试。跨浏览器旧版本和视觉回归超出范围,已明确跳过并说明理由。
---Skill Self-Check
技能自检
Before declaring a strategy complete, the loading verify:
- All 7 gates evaluated explicitly (ON/OFF + reason).
- order is
selected_types[*].rationale -> type -> size -> framework -> dependencies -> gate - order is
rejected_types[*].reason -> type - order is
deliberately_skipped[*].why -> what - Each AC is referenced by at least one test case.
- BVA cases enumerate ,
B-1,Bfor each numeric boundary.B+1 - Test sizes (small/medium/large) are assigned per Google Test Sizes.
- Test names contain no "and" (per Skip Heuristic).
- At least one Strategic Skip Heuristic was applied or explicitly considered and overridden with rationale.
If any check fails, revise the strategy before delivering.
在宣布策略完成前,需验证:
- 所有7个决策门已明确评估(启用/禁用+理由)。
- 顺序为
selected_types[*]。rationale -> type -> size -> framework -> dependencies -> gate - 顺序为
rejected_types[*]。reason -> type - 顺序为
deliberately_skipped[*]。why -> what - 每个验收标准至少关联一个测试用例。
- 边界值分析用例为每个数值边界枚举、
B-1、B。B+1 - 测试规模(小/中/大)根据Google测试规模分配。
- 测试名称不包含“和”(根据策略性跳过启发规则)。
- 至少应用了一个策略性跳过启发规则,或明确考虑并说明否决理由。
如果任何检查未通过,在交付前修订策略。
Coverage Analysis
覆盖率分析
Mutation testing and other coverage-analysis methods (used after tests are written to assess test-suite quality) are documented in the companion skill.
test-coverage变异测试和其他覆盖率分析方法(测试编写后用于评估测试套件质量)在配套的技能中记录。
test-coverage