design-testing-strategy

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Design 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

如何使用本技能

  1. Read Decision Gates in order (Gate 0 -> Gate 6). Each gate is independent — you may finish with any subset of test types ON.
  2. Apply Strategic Skip Heuristics to remove ON gates that would yield low ROI for this artifact.
  3. For each ON gate, fill the Test Matrix Schema (
    selected_types
    entry) — the field order is load-bearing.
  4. List rejected types in
    rejected_types
    and deliberate skips in
    deliberately_skipped
    .
  5. Produce a Test Cases to Cover markdown bullet list using ISTQB techniques from Case Design Techniques.
  6. Cross-check against the matching Worked Example (A pure function / B HTTP+DB endpoint / C UI component).

  1. 按顺序阅读决策门(从Gate 0到Gate 6)。每个决策门相互独立——最终你可能只启用部分测试类型。
  2. 应用策略性跳过启发规则,移除对当前工件投资回报率低的已启用决策门。
  3. 对每个已启用的决策门,填写测试矩阵模式
    selected_types
    条目)——字段顺序是固定要求。
  4. rejected_types
    中列出未选中的测试类型,在
    deliberately_skipped
    中列出主动跳过的内容。
  5. 使用用例设计方法中的ISTQB技术,生成一份需覆盖的测试用例Markdown无序列表。
  6. 对照匹配的完整示例(纯函数/B HTTP+DB端点/C UI组件)进行交叉检查。

Decision Gates

决策门

Apply gates in numeric order. Each gate produces an independent boolean (
applies: true|false
). Gates do NOT veto each other — a single artifact may have unit + integration + contract + property-based all ON.
#TypeON whenOFF whenSource
0Skip AllCriticality is
NONE
(docs-only, comments, formatting, generated code, config without logic, throwaway prototypes)
Anything with branching, computed output, side effects, or user-visible behaviorPragmatic Programmer — "Test ruthlessly and effectively" implies effective skipping when ROI is zero
1UnitCode contains any logic: branches, loops, conditionals, computation, transformation, parsing, validation, formattingPure declarative wiring (DI registration, route table) with no behaviorTest Pyramid (Vocke) base layer + Beck TDD Red-Green-Refactor unit
2IntegrationBoundary crossing: HTTP call, DB query, external SDK, message queue, filesystem I/O, OR collaboration with >=2 distinct collaborators where unit doubles distort behaviorPure function with no I/O and 0-1 stable collaboratorsTesting Trophy (Dodds) — integration is the highest-ROI layer; Google "Follow the User"
3Component or E2EUI 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 UITest Pyramid top + ISO/IEC/IEEE 29119 risk ranking + Google e2e principles
4ContractPublic API consumed by >=1 distinct clients (mobile + web, multiple internal services, external partners) AND independent deploy cadenceAPI where consumer and provider deploy togetherPact / CDC + Pactflow CDC explainer
5SmokeDeployable surface (web app, API, service) AND a deploy/CI pipeline exists where post-deploy validation is meaningfulLibrary, internal helper, or no deploy pipelineGoogle "What Makes a Good End-to-End Test" — smoke = minimal e2e for deploy gate
6Property-BasedInput 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-HIGHSmall finite input domain, unstable invariants, or LOW criticalityHypothesis / QuickCheck
按数字顺序应用决策门。每个决策门会生成独立的布尔值(
applies: true|false
)。决策门之间不会相互否决——一个工件可以同时启用单元测试+集成测试+契约测试+基于属性的测试。
#类型启用条件禁用条件资料来源
0全部跳过关键程度为
NONE
(仅文档、注释、格式代码、生成代码、无逻辑配置、一次性原型)
任何包含分支、计算输出、副作用或用户可见行为的内容《程序员修炼之道》(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,且客户端与服务端部署节奏独立消费者与提供者同步部署的APIPact/消费者驱动契约(CDC) + Pactflow CDC说明
5冒烟测试可部署的应用(网页应用、API、服务),且存在部署/CI流水线,部署后验证有意义类库、内部辅助工具或无部署流水线Google“优质端到端测试的标准”——冒烟测试是用于部署校验的最小化端到端测试
6基于属性的测试输入域庞大或无界(数值范围、字符串、列表、解析器、序列化器、编码器、数学运算),且不变量稳定(往返一致性、幂等性、单调性、交换性),同时关键程度≥MEDIUM-HIGH输入域小且有限、不变量不稳定或关键程度为LOWHypothesis/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, stop
Criticality Scale (used by Gates 3 and 6):
LevelDefinition
NONE
Docs, formatting, generated code, throwaway code, configs without logic
LOW
Internal dev tooling, admin-only screens, logging formatters
MEDIUM
Standard CRUD, internal APIs with a single team consumer, non-critical UI, helpers and utilities
MEDIUM-HIGH
User-facing UI on critical paths, public APIs with multiple consumers, business workflows
HIGH
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使用):
级别定义
NONE
文档、格式代码、生成代码、一次性代码、无逻辑配置
LOW
内部开发工具、仅管理员可见界面、日志格式化工具
MEDIUM
标准CRUD操作、仅单个团队消费的内部API、非关键UI、辅助工具
MEDIUM-HIGH
用户关键路径上的UI、多消费者的公开API、业务工作流
HIGH
资金流转、认证/授权决策、安全关键验证、数据完整性、受监管领域

Test Type Reference

测试类型参考

TypeUse whenDo NOT use whenFrameworksTypical dependenciesGoogle Size
unitPure logic, single function/method/class, deterministic inputsCode is just I/O orchestration with no logicvitest, jest, pytest, go test, JUnit, xUnit, RSpecNone (or in-memory fakes)Small
integrationBoundary crossing (DB, HTTP, queue, FS); multiple collaborators where mocking distorts behaviorPure function with no boundaryvitest, jest, pytest, go test, JUnit + Testcontainers, supertest, TestRestTemplateReal Postgres/Redis/Kafka via Testcontainers, in-process HTTP server, real FS in tmpdirMedium (single machine, localhost OK)
componentUI rendering + interaction within a single component, no full app contextBackend-only logic; multi-page user flowReact Testing Library, Vue Test Utils, Angular TestBed, Storybook interaction testsjsdom or happy-dom, mocked network at fetch/axios levelSmall to Medium
e2eFull user path through running app: real browser, real backend, real DBInternal helper, single component, non-critical UIPlaywright, Cypress, SeleniumReal running app + Testcontainers-backed DB or seeded stagingLarge (multi-process, possibly multi-machine)
smokePost-deploy go/no-go: hit / health, key endpoints respond, login worksDetailed correctness; smoke is shallow by designPlaywright (1-3 critical paths), HTTP probe scripts, k6 minimal scenariosReal deployed environmentLarge
contractPublic API consumed by 2+ distinct clients with independent deploy cadenceSingle-consumer internal API; provider and consumer deploy togetherPact, Spring Cloud Contract, OpenAPI schema validatorsPact broker or contract files in repoMedium
property-basedLarge/unbounded input domain with stable invariants (parser, serializer, encoder, math)Small finite input space; unstable invariantsHypothesis (Python), fast-check (TS), QuickCheck (Haskell), jqwik (Java), proptest (Rust)Same as unitSmall
类型使用场景禁用场景框架典型依赖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运行中应用的完整用户路径:真实浏览器、真实后端、真实数据库内部辅助工具、单个组件、非关键UIPlaywright, Cypress, Selenium运行中的真实应用 + Testcontainers支撑的数据库或预填充的 staging环境大型(多进程,可能多机器)
smoke部署后校验:访问/health、关键端点响应、登录功能正常详细正确性校验;冒烟测试设计为浅层次Playwright(1-3条关键路径)、HTTP探测脚本、k6最小场景真实部署环境大型
contract被2+个独立部署节奏的客户端消费的公开API仅单个消费者的内部API;提供者与消费者同步部署Pact, Spring Cloud Contract, OpenAPI schema validatorsPact 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):
SizeProcess modelNetworkFilesystemTime budgetNotes
small
Single process, single threadNoneNone (in-memory only)< 100msFast, hermetic, parallelizable
medium
Single machine, multiple processes allowedlocalhost onlytmpdir allowed< 1sTestcontainers fits here
large
Multi-machineExternal network allowedPersistent FS allowed< 15minFull e2e
enormous
DistributedWide networkAnywherelongerCluster / 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章根据资源消耗(规模)对测试进行分类,与覆盖范围(路径覆盖)无关:
规模进程模型网络文件系统时间预算说明
small
单进程、单线程无(仅内存)< 100ms快速、封闭、可并行
medium
单机器,允许多进程仅本地网络允许临时目录< 1sTestcontainers属于此类
large
多机器允许外部网络允许持久化文件系统< 15min完整端到端测试
enormous
分布式广域网络任意位置更长集群/混沌测试
测试的类型(单元/集成/端到端)和规模(小/中/大)是正交的:小型集成测试(通过JDBC在同一进程中使用Testcontainers Postgres)是合理的。

Playwright vs Cypress (UI e2e)

Playwright vs Cypress(UI端到端测试)

DimensionPlaywrightCypress
BrowsersChromium, Firefox, WebKitChromium, Firefox, WebKit (limited)
Multi-tab / multi-originYesLimited
ParallelismBuilt-in shardsPaid dashboard or external
Network interceptionRobust route-levelcy.intercept
DefaultChoose Playwright for new projects unless team already standardized on CypressChoose Cypress when team has heavy investment

维度PlaywrightCypress
浏览器支持Chromium、Firefox、WebKitChromium、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
:
PartitionRangeRepresentative test inputExpected
Below threshold
0 <= total < 100
50
0% discount
Mid tier
100 <= total < 500
250
5% discount
Top tier
total >= 500
1000
10% discount
Invalid (negative)
total < 0
-1
throw / error
Four tests cover all partitions. EP alone misses boundaries — combine with BVA.
将输入域划分为系统预期行为一致的分区;每个分区只需一个测试用例。
示例
discount(orderTotal: number) -> number
:
分区范围代表性测试输入预期结果
低于阈值
0 <= total < 100
50
0% 折扣
中间层级
100 <= total < 500
250
5% 折扣
最高层级
total >= 500
1000
10% 折扣
无效输入(负数)
total < 0
-1
抛出异常/错误
四个测试用例覆盖所有分区。仅使用等价类划分会遗漏边界——需结合边界值分析。

2. Boundary Value Analysis (BVA)

2. 边界值分析(BVA)

Bugs cluster at boundaries. For every boundary value
B
, test
B-1
,
B
,
B+1
(or for floats, the smallest representable step).
Worked example — same
discount
function, boundary at
100
:
Test inputWhyExpected
99
(= B-1)
Last value of "below threshold" partition
0% discount
100
(= B)
First value of "mid tier" partition
5% discount
101
(= B+1)
Confirms not off-by-two
5% discount
Repeat for boundary at
500
: test
499
,
500
,
501
. Total: 6 boundary tests + 4 EP tests = 10 cases.
The
B-1 / B / B+1
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
,测试**
B-1
B
B+1
**(浮点数则测试最小可表示步长)。
示例 — 同一
discount
函数,边界为
100
:
测试输入原因预期结果
99
(= B-1)
“低于阈值”分区的最后一个值
0% 折扣
100
(= B)
“中间层级”分区的第一个值
5% 折扣
101
(= B+1)
确认没有差一错误
5% 折扣
对边界
500
重复此操作:测试
499
500
501
。总计:6个边界测试 + 4个等价类测试 = 10个用例。
B-1 / B / B+1
三元组在所有边界处的模式一致(仅输入和预期输出变化,断言相同);这非常适合表格驱动测试(见下文第5小节)。

3. Decision Tables

3. 决策表

When output depends on combinations of conditions. Each column is a rule.
Worked example
canCheckout(cartHasItems, paymentValid, addressOnFile)
:
Condition / RuleR1R2R3R4
cartHasItemsTTTF
paymentValidTTF*
addressOnFileTF**
Resultallowblock:addressblock:paymentblock:cart
Four tests, one per rule (
*
= don't care, dropped via merging).
当输出取决于多个条件的组合时使用。每一列是一条规则。
示例
canCheckout(cartHasItems, paymentValid, addressOnFile)
:
条件/规则R1R2R3R4
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}
:
FromEventToTest
draftsubmitsubmittedhappy path
submittedpaypaidhappy path
paidshipshippedhappy path
draftcancelcancelledearly cancel
paidcancelrejectforbidden — refund flow required, NOT direct cancel
shippedsubmitrejectforbidden
Cover one test per legal transition + one per forbidden transition (negative path).
当行为取决于历史状态时使用。识别状态、事件和禁止的转换。
示例 — 订单状态机,状态包括
{draft, submitted, paid, shipped, cancelled}
:
起始状态事件目标状态测试说明
draftsubmitsubmitted正常路径
submittedpaypaid正常路径
paidshipshipped正常路径
draftcancelcancelled提前取消
paidcancel拒绝禁止操作——需走退款流程,不可直接取消
shippedsubmit拒绝禁止操作
每个合法转换对应一个测试用例,每个禁止转换对应一个测试用例(负向路径)。

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
discount(orderTotal)
(boundary at
100
) collapsed into one table-driven unit test (TS / vitest syntax; the same pattern applies to Go
t.Run
, JUnit
@ParameterizedTest
, pytest
parametrize
):
ts
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
name
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.

当等价类划分、边界值分析或决策表分析产生3+个模式相同的用例(相同的前置条件、相同的断言,仅输入和预期输出不同——例如解析有效/无效日期格式、计算不同区间的税费、路由规则)时,将它们合并为一个表格驱动测试。用例成为数据表中的行;测试主体遍历行并为每行执行一次断言。参考资料:Dave Cheney,优先使用表格驱动测试Go wiki: TableDrivenTests
如果用例的前置条件、框架调用或断言模式差异较大,不要强行合并为表格。强行统一会掩盖真实差异,导致模糊的失败信息——应将这些用例保留为单独的、命名明确的测试。
示例 — 将
discount(orderTotal)
的6个等价类+边界值用例(边界为100)合并为一个表格驱动单元测试(TS/vitest语法;同样的模式适用于Go
t.Run
、JUnit
@ParameterizedTest
、pytest
parametrize
):
ts
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);
    });
  }
});
name
列是必填项:每行必须生成一个可单独定位的测试,以便失败时指向具体用例,而非“第3行/共6行”。需要不同断言的行(例如负向输入抛出异常)应保留为表格外的单独测试。

Dependency 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 styleUse whenAvoid whenNotes
Real infra via TestcontainersDB/Redis/Kafka/Browser, dev needs real driver behavior, hermetic CI requiredCold-start budget < 1s, no Docker availableDefault for integration tests on Postgres / Redis / Kafka / Localstack
In-memory fakeOwned interface, semantics are simple (key-value, list), test speed criticalFake diverges from real — silent bugs at integration boundaryAcceptable 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 HTTPCalling external SaaS where Testcontainers / Localstack option doesn't existWhen Pact / CDC is needed (use contract tests instead)nock (Node), responses (Python), WireMock (JVM)
Real external serviceSmoke test in staging onlyUnit / integration / CI — always non-deterministicReserve 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.
SkipRule
No e2e for internal helpersIf artifact has no UI surface and no user-facing path, skip e2e. Unit + integration is sufficient.
No contract test for bound by deploy consumer APIIf only one client consumes the API and they deploy together, contract testing adds maintenance with no decoupling benefit.
No property-based on small finite domainsIf input space is
enum {A, B, C}
, EP + BVA already covers it; property-based adds infra without finding more bugs.
No integration test for pure functionsAdding a Postgres container to test a
formatCurrency
helper is waste. Unit only.
No component test for static markupIf the component has no state, no events, no conditional rendering, a snapshot is enough — or skip entirely.
No unit test for declarative wiringDI bindings, route registration, schema declarations: assert at integration level (does the route serve the right handler) instead.
No e2e for things integration covers reliablyPer 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 codePer 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" testsIf a test name contains "and", split it into separate tests (one assertion per behavior).

明确的“无需费心”规则。跳过这些测试不是懒惰——而是基于ISO/IEC/IEEE 29119风险测试和基于风险的测试进行的风险调整投资回报率决策。
跳过规则说明
内部辅助工具无需端到端测试如果工件无UI界面且无用户可见路径,跳过端到端测试。单元+集成测试已足够。
与消费者同步部署的API无需契约测试如果仅一个客户端消费API且双方同步部署,契约测试只会增加维护成本,无解耦收益。
小而有限的输入域无需基于属性的测试如果输入域是
enum {A, B, C}
,等价类+边界值分析已覆盖;基于属性的测试只会增加基础设施成本,不会发现更多缺陷。
纯函数无需集成测试为测试
formatCurrency
辅助工具添加Postgres容器是浪费。仅需单元测试。
静态标记无需组件测试如果组件无状态、无事件、无条件渲染,快照测试已足够——或完全跳过。
声明式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_strategy
    :
    artifact
    BEFORE
    rationale
    BEFORE
    criticality
    .
  • selected_types[*]
    :
    rationale
    BEFORE
    type
    BEFORE
    size
    BEFORE
    framework
    BEFORE
    dependencies
    BEFORE
    gate
    .
  • rejected_types[*]
    :
    reason
    BEFORE
    type
    .
  • deliberately_skipped[*]
    :
    why
    BEFORE
    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
undefined
markdown
undefined

Test 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
undefined
markdown
undefined

Test 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.
  1. Test Pyramid — Mike Cohn (2009, Succeeding with Agile) + Ham Vocke, The Practical Test Pyramid, martinfowler.com.
  2. Testing Trophy — Kent C. Dodds (2018), The Testing Trophy and Testing Classifications and Write Tests.
  3. Google Test Sizes — Mike Bland (2011), Small / Medium / Large; Software Engineering at Google Ch.11; Test Sizes (Google Testing Blog).
  4. Google Testing on the ToiletWhat Makes a Good End-to-End Test, Testing UI Logic - Follow the User, Origins (Mike Bland).
  5. ISTQB Foundation Level — Black-box techniques: Boundary Value Analysis white paper; ASTQB Black-Box Techniques.
  6. ISO/IEC/IEEE 29119 — Risk-based test process standard. Wikipedia overview.
  7. Kent Beck — Test Driven Development: By Example (Addison-Wesley, 2002). Publisher page. ISBN 978-0321146533.
  8. The Pragmatic Programmer (20th Anniversary Edition) — Hunt & Thomas (2019). pragprog.com.
  9. AAA pattern — Bill Wake (2001), 3A — Arrange, Act, Assert. Given-When-Then — Dan North, Introducing BDD.
  10. Property-based testingHypothesis: What is property-based testing?; QuickCheck (Haskell), fast-check (TS).
  11. Contract testing / Consumer-Driven ContractsPact docs; Pactflow CDC explainer.
  12. Testcontainerstestcontainers.com.
  13. Table-driven tests — Dave Cheney, Prefer table-driven tests; Go wiki: TableDrivenTests.
  14. Risk-based testingRisk Management During Test Planning (softwaretestinghelp.com).

上述每个决策门和规则都基于以下14个资料。有疑问时,参考对应决策门链接的资料。
  1. 测试金字塔——Mike Cohn(2009,《敏捷成功之道》)+ Ham Vocke,《实用测试金字塔》,martinfowler.com。
  2. 测试奖杯——Kent C. Dodds(2018),《测试奖杯与测试分类》和《编写测试》。
  3. Google测试规模——Mike Bland(2011),《小型/中型/大型》;《Google软件工程》第11章;《测试规模》(Google测试博客)。
  4. Google测试厕所读物——《优质端到端测试的标准》、《测试UI逻辑——跟随用户》、《起源(Mike Bland)》。
  5. ISTQB基础级——黑盒技术:《边界值分析白皮书》;《ASTQB黑盒技术》。
  6. ISO/IEC/IEEE 29119——基于风险的测试流程标准。维基百科概述
  7. Kent Beck——《测试驱动开发:实例解析》(Addison-Wesley,2002)。出版社页面。ISBN 978-0321146533。
  8. 《程序员修炼之道》(20周年版)——Hunt & Thomas(2019)。pragprog.com
  9. AAA模式——Bill Wake(2001),《3A——Arrange, Act, Assert》。Given-When-Then——Dan North,《介绍BDD》。
  10. 基于属性的测试——《Hypothesis:什么是基于属性的测试?》;QuickCheck(Haskell)、fast-check(TS)。
  11. 契约测试/消费者驱动契约——《Pact文档》;《Pactflow CDC说明》。
  12. Testcontainers——testcontainers.com
  13. 表格驱动测试——Dave Cheney,《优先使用表格驱动测试》;Go wiki: TableDrivenTests
  14. 基于风险的测试——《测试规划期间的风险管理》(softwaretestinghelp.com)。

Worked Examples

完整示例

Each example shows:
  • a. the artifact and acceptance criteria
  • b. gate-by-gate walkthrough
  • c.
    test_strategy
    YAML following the schema
  • d.
    Test Cases to Cover
    list
  • e. commentary on rejected types

每个示例包含:
  • a. 工件和验收标准
  • b. 逐个决策门的分析
  • c. 遵循模式的
    test_strategy
    YAML
  • d.
    需覆盖的测试用例
    列表
  • e. 未选中测试类型的说明

Example A — Pure Helper Function:
formatCurrency(amount: number, code: string): string

示例A——纯辅助函数:
formatCurrency(amount: number, code: string): string

Artifact
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
    Error("Unknown currency code")
    for unsupported codes.
  • AC-4:
    amount = 0
    formats as
    "$0.00"
    /
    "€0,00"
    .
Criticality:
LOW
(helper used in display only, no money movement here).
Gate Walkthrough
GateDecisionReason
0 SkipOFFHas logic
1 UnitONPure logic with branches per currency code — Test Pyramid base
2 IntegrationOFFNo I/O, no boundary — Skip Heuristic: no integration for pure functions
3 Component/E2EOFFNo UI surface
4 ContractOFFNot a public API
5 SmokeOFFNot deployable
6 Property-BasedON (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_strategy
YAML
yaml
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_strategy
YAML
yaml
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
undefined

AC-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.

AC-3: 对不支持的代码抛出
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"
.

AC-4:
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

示例B——带数据库和多消费者的HTTP POST端点:
POST /users

Artifact
A user-registration endpoint that:
  1. Validates request body (email format, password complexity, age >= 13).
  2. Checks email uniqueness against Postgres.
  3. Inserts user record (transactional).
  4. Emits
    user.created
    event to Kafka.
  5. Returns
    201
    with
    {id, email, createdAt}
    .
  6. Returns
    400
    for invalid input,
    409
    for duplicate email.
Consumed by: mobile app (iOS/Android) and web app on independent deploy cadences.
Acceptance criteria:
  • AC-1: Valid request returns
    201
    and persists user.
  • AC-2: Invalid email format returns
    400
    with field-level error.
  • AC-3: Password not meeting policy returns
    400
    .
  • AC-4: Duplicate email returns
    409
    .
  • AC-5: Successful registration emits exactly one
    user.created
    event.
  • AC-6: Response schema is stable for mobile + web consumers.
Criticality:
MEDIUM-HIGH
(auth surface, identity domain, multi-consumer public API).
Gate Walkthrough
GateDecisionReason
0 SkipOFFHas substantial logic
1 UnitONValidators (email, password, age) are pure logic — Test Pyramid base
2 IntegrationONBoundary crossing: HTTP, Postgres, Kafka — Testing Trophy ROI sweet spot
3 Component/E2EOFF (here)No UI in this artifact; UI lives in mobile + web repos and tests itself
4 ContractONTwo distinct consumers (mobile + web) on independent deploy cadences — Pact CDC
5 SmokeONDeployable HTTP service; post-deploy probe of
/users
registration is meaningful — Google e2e
6 Property-BasedOFFInput 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_strategy
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: "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
工件
用户注册端点,功能包括:
  1. 验证请求体(邮箱格式、密码复杂度、年龄≥13)。
  2. 检查邮箱在Postgres中的唯一性。
  3. 插入用户记录(事务性)。
  4. 向Kafka发送
    user.created
    事件。
  5. 返回
    201
    {id, email, createdAt}
  6. 无效输入返回
    400
    ,重复邮箱返回
    409
消费者: 移动应用(iOS/Android)和网页应用,部署节奏独立。
验收标准:
  • AC-1: 有效请求返回
    201
    并持久化用户。
  • AC-2: 无效邮箱格式返回
    400
    和字段级错误。
  • AC-3: 密码不符合规则返回
    400
  • AC-4: 重复邮箱返回
    409
  • AC-5: 注册成功时恰好发送一个
    user.created
    事件。
  • AC-6: 响应模式对移动+网页消费者稳定。
关键程度:
MEDIUM-HIGH
(认证界面、身份领域、多消费者公开API)。
决策门分析
决策门决策理由
0 跳过禁用包含大量逻辑
1 单元测试启用验证器(邮箱、密码、年龄)是纯逻辑——测试金字塔基础层
2 集成测试启用跨边界操作:HTTP、Postgres、Kafka——测试奖杯投资回报率最佳点
3 组件/端到端测试禁用(此处)该工件无UI;UI位于移动+网页仓库中并自行测试
4 契约测试启用两个独立消费者(移动+网页)部署节奏独立——Pact CDC
5 冒烟测试启用可部署的HTTP服务;部署后探测/users注册有意义——Google端到端测试原则
6 基于属性的测试禁用输入域(邮箱、密码、年龄)受限,单元测试的等价类+边界值分析已覆盖;关键程度为MEDIUM-HIGH但Gate 6对有界输入禁用——跳过启发规则
test_strategy
YAML
yaml
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
undefined

AC-1: Valid request returns
201
and persists user.

AC-1: 有效请求返回
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.

AC-2: 无效邮箱格式返回
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
.

AC-3: 密码不符合规则返回
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
.

AC-4: 重复邮箱返回
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.

AC-5: 注册成功时恰好发送一个
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)

示例C——UI表单组件:
<RegistrationForm />
(网页)

Artifact
A React form component:
  1. Fields: email, password, confirmPassword, age.
  2. Client-side validation: email format, password >= 8 chars with mixed case + digit, passwords match, age >= 13.
  3. Submits to
    POST /users
    .
  4. Shows inline field errors and submit-level errors (network, 409 duplicate).
  5. Disables submit button while pending; re-enables on response.
  6. WCAG 2.1 AA: labels bound to inputs, errors announced via
    aria-live
    , focus moves to first error on validation failure.
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
    "This email is already registered"
    at form level.
  • 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:
MEDIUM-HIGH
(registration is a critical user-facing path; accessibility is regulated in many jurisdictions).
Gate Walkthrough
GateDecisionReason
0 SkipOFFBehavior + accessibility logic
1 UnitONValidation helpers (
validateEmail
,
passwordsMatch
,
parseAge
) are pure logic
2 IntegrationOFF (here)The component itself does not cross a real boundary; network is mocked at fetch level. Network integration is owned by
POST /users
(Example B)
3 Component/E2EON (component) + ON (e2e for the registration path)UI surface, criticality MEDIUM-HIGH, user-facing critical path — Test Pyramid top + Follow the User
4 ContractOFFUI consumes API; provider-side contract tests live in Example B
5 SmokeONWeb app is deployed; smoke for "registration page renders and submits" is meaningful
6 Property-BasedOFFBounded form inputs; EP+BVA covers them
test_strategy
YAML
yaml
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表单组件,功能包括:
  1. 字段:邮箱、密码、确认密码、年龄。
  2. 客户端验证:邮箱格式、密码≥8位且包含大小写+数字、密码匹配、年龄≥13。
  3. 提交到
    POST /users
  4. 显示内联字段错误和提交级错误(网络、409重复)。
  5. 请求处理中禁用提交按钮;响应后重新启用。
  6. 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 单元测试启用验证辅助工具(
validateEmail
passwordsMatch
parseAge
)是纯逻辑
2 集成测试禁用(此处)组件本身不跨真实边界;网络在fetch层模拟。网络集成由
POST /users
(示例B)负责
3 组件/端到端测试启用(组件) + 启用(注册路径端到端)有UI界面、关键程度MEDIUM-HIGH、用户关键路径——测试金字塔顶层 + 跟随用户原则
4 契约测试禁用UI消费API;提供者端契约测试在示例B中
5 冒烟测试启用网页应用可部署;“注册页面渲染并可提交”的冒烟测试有意义
6 基于属性的测试禁用表单输入有界;等价类+边界值分析已覆盖
test_strategy
YAML
yaml
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
undefined

AC-1: User can submit a valid form and is navigated to
/welcome
.

AC-1: 用户可提交有效表单并导航到
/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"
.

AC-2: 无效邮箱显示内联
"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"
.

AC-3: 密码不匹配显示内联
"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.

AC-5: 服务器返回409时,表单级别显示
"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"
.

AC-7: 所有输入有程序化标签;错误通过
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).
  • selected_types[*]
    order is
    rationale -> type -> size -> framework -> dependencies -> gate
    .
  • rejected_types[*]
    order is
    reason -> type
    .
  • deliberately_skipped[*]
    order is
    why -> what
    .
  • Each AC is referenced by at least one test case.
  • BVA cases enumerate
    B-1
    ,
    B
    ,
    B+1
    for each numeric boundary.
  • 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
test-coverage
skill.
变异测试和其他覆盖率分析方法(测试编写后用于评估测试套件质量)在配套的
test-coverage
技能中记录。