Community编程与开发github.com

ShortArrow/test-design

根据规范而非实现推导测试用例,覆盖等价类、边界值、决策表和状态表,避免常见陷阱。

test-design 是什么?

该技能旨在帮助开发者从规范(而非实现)出发系统化地设计测试用例,以提高测试套件的有效性和覆盖率。它识别并替代了测试设计中的常见不良实践,例如仅测试快乐路径、从代码反推测试、遗漏边界值或未命名的边界、忽略错误路径、以及使用模糊的预期结果(如“或”)。技能覆盖了等价类划分、边界值分析(包括2值和3值BVA)、决策表测试、状态转换测试等核心黑盒测试技术,强调每个测试类只需一个测试(避免冗余),并确保每个类(尤其是无效类、禁止转换和未实现路径)都有对应的测试用例。它特别指出,测试应基于规范(如规格说明、线格式、合约、用例)推导,而非实现,以避免捕获当前行为中的bug。技能还提供了评估测试套件充分性的方法:根据命名准则(如分区覆盖率、MC/DC等)进行计数,而非主观判断。该技能适用于Claude Code、Cursor等AI编码代理,在编写测试计划、添加测试到现有代码、审查测试套件缺口,或当测试全部通过但证明力不足时使用。

兼容平台~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/ShortArrow/skills/tree/HEAD/skills/test-design

Installed? Explore more 编程与开发 skills: steipete/bluebubbles, steipete/eightctl, steipete/blucli · View all 6 →

在你喜欢的 AI 中提问

打开一个已预加载此 Agent Skill 的新对话。

文档

测试设计

测试用例决定了测试套件能捕获什么。 之后的一切——测试框架、测试运行器、 测试计数——都只是执行那个决定。

本技能替代的时刻

正要…改为…
编写快乐路径并就此停止遍历输入空间:对每个参数,找出其等价类和边界
从实现推导测试用例从规范推导,并让实现给你惊喜
使用舒适值进行测试测试每个边界内部的最后一个值和外部的第一个值
测试规范命名的边界每个范围有两个端点;句子遗漏的那个仍然是边界
枚举规范列出的角色添加调用者遗漏的角色:无角色、未认证、未分配
从快乐路径变化一个条件构建决策表并覆盖两个条件同时失败的规则
绘制状态表然后继续在每一行放入测试ID;最需要的是写着“不允许”的那行
编写“拒绝,或接受前提…”作为预期结果每个用例一个结果;无法决定的规范是一个开放问题,不是测试
因不太可能而跳过错误路径每个错误模式都是一个等价类:无效输入、缺失资源、拒绝、超时、半完成
留下未实现的路径,没有测试、跳过标记或无人断言的桩代码编写测试断言该路径今天以未实现的方式被拒绝;它现在通过,并在有人构建它的那一天变红(见下文)
通过主流程测试一个功能从用例中读取替代流程和异常流程;每个都是一个等价类
通过阅读来评估测试套件对照规范计数:等价类、两个边界、规则、转换
向已覆盖的等价类添加第五个测试将其用于未覆盖的等价类——第五个测试消耗运行时间且不增加覆盖率

攻击规范,而非实现

通过阅读代码编写的测试断言代码的行为。 它到达时即通过,它将当前bug编码为期望值, 并且变更后的失败意味着“代码改变了”, 这已是已知信息。 这是可测量的,而非怀疑:在24个Java仓库上, LLM生成的预言倾向于捕获程序的实际行为而非其预期行为(Konstantinou, Degiovanni, Papadakis, arXiv:2410.21136)。

从事物应该做什么推导——规范、 线格式、合约——然后才运行它。 唯一的合法例外是有意固定当前行为的特征测试, 并且必须标记清楚,以免后来者将固定的bug误认为需求。

一个有用的味道:如果每个测试在首次运行时都是绿色的, 那么测试套件是从实现推导的,无论意图如何。

等价类:计数类,而非测试

代码无法区分的输入形成一个类, 每个类一个测试就足够了——第二个到第五个消耗运行时间且不证明任何新东西。 这种纪律双向适用:没有类未覆盖, 没有类被意外覆盖两次。

一个包含十七个测试的套件看似全面。 但它是否全面取决于这十七个测试落入多少个类中, 而这个数字不出现在任何运行器的输出中。 必须手工对照输入空间计数——这正是测试计划的作用。

规范列出的类是容易的一半。 一个三行的角色表有第四个类, 即不匹配任何行的请求, 而这是从表构建的套件从未包含的。 对于每个参数,在编写任何用例之前,先编写无效类与有效类并列: 错误类型、缺失、超出每个列出的值、 完全没有权限的调用者。

教学大纲将此作为规则而非习惯陈述: 在等价划分中,覆盖项是分区, 100%意味着每个已识别的分区都得到练习,“包括无效分区”, 并且分区“不得重叠且必须是非空集”(ISTQB CTFL v4.0.1 §4.2.1)。 对于多个参数,它命名了最弱的标准 Each Choice——每个参数的每个分区一次, 忽略组合——这是大多数套件达到的级别而不知其名, 也是下面的决策表开始的地方。

边界,因为错误就在那里

差一错误是最常见的算术错误, 并且在除边界外的任何地方都不可见。 在每个边界处最重要的对是边界值及其跨过线的最近邻居, 即2值BVA(CTFL v4.0.1 §4.2.2); 3值BVA增加了内部邻居, 教学大纲展示了这带来了什么: 一个本应为x ≤ 10但写为x = 10的检查,10和11都通过, 只有9能捕获它。 反复出现的边界:空、正好一个、多个;零和负数; 声明的最大值及其后一个; 恰好等于缓冲区、限制、超时的值。

来自本机器的一个具体对: 在8,190字符处正常的PATH检查必须在8,192字符处失败, 必须在这两个值处测试。 在4,000和12,000处通过几乎不能说明两者之间的线。

当边界是声明的前置条件时, 越过边界值是调用者的bug而非有预期结果的用例; design-by-contract决定给定边界属于哪种情况, 测试随后断言适用的一种。

规范未命名的边

“最多10 MB”命名了一个范围的一端。 另一端是否存在取决于句子是否提及它, 而从文本推导的用例在文本停止处停止: 一个在−1、0和+1字节处测试了上界且没有低于1,024的套件, 只对半个范围进行了边界分析。 定义没有偏向性: “一个分区的最小值和最大值是其边界值”(CTFL v4.0.1 §4.2.2)。 首先为每个分区写下两个边界, 然后每个边界与相邻分区中的最近邻居一起处理。 未命名的边通常是零、空或一, 并且规范通常对此保持沉默,因为无人决定。

同时移动两个条件

通过从快乐路径改变一个条件构建的套件,覆盖了每个条件的每个值,但几乎不覆盖任何组合: 格式错误、尺寸过大、数量过多,每个单独出现, 而没有两个同时失败的用例。 当两者都适用时哪个错误获胜,也很少在规范中, 因此缺失的用例隐藏了一个缺失的决策。

将条件布局为决策表并计算规则, 然后覆盖两个条件同时失败的规则,以及角色限制和格式限制同时适用的规则。 在决策表测试中,覆盖项是包含可行组合的列, 100%意味着每个这样的列都得到练习(CTFL v4.0.1 §4.2.3); 教学大纲补充说表“有助于发现需求中的任何缺口或矛盾”, 这正是上面缺失的决策自行浮现的地方。 同一份文档在 Each Choice 下可能显示为完全覆盖,而在列下仅覆盖四分之一, 只有第二个数字说明了一些关于组合的情况。 当规则数是标志集的乘积时, 更便宜的修复在上游: state-first将标志折叠为可能发生的状态, 表也随之缩小到这些状态。

表是对测试套件的声明

状态表、决策表、用例的流程列表: 每一行都声明存在一个针对它的测试。 将测试ID放入行中。 没有ID的行是未覆盖的等价类, 而写着“不允许”的行是最可能为空的行, 因为编写禁止感觉就像已经处理了它。 禁止的转换——在飞行中发送第二个——就是测试, 而应该阻止它的守卫就是被测试的东西。

状态表使这一行显式化: 无效的转换是一个空单元格, 而教学大纲定义的三个标准中最强的, 所有转换覆盖率, 要求尝试每一个转换——每个测试用例一个, 以便一个缺陷不能掩盖另一个(CTFL v4.0.1 §4.2.4)。 有效转换覆盖率,即大多数套件停下的0-switch, 永远不会触及这些单元格, 这就是未测试的“不允许”行如何保持绿色。

然后对照阅读两份文档。 一个测试的路径与表不一致(表说 失败→发送中,测试走 失败→已选择→发送中)意味着其中之一是错误的, 除了比较之外没有其他方法能找出哪个。 自转换和离开失败状态的转换也算作行。

每个用例一个结果

“拒绝,或者如果接受,发送后不留下损坏的数据”无法通过或失败。 它诚实地出现,当规范从未说明零字节文件会发生什么,且作者拒绝发明答案时, 由于用例明显存在,它通过了审查。 析取隐藏在预期列中。 正确形式是向规范所有者提出的问题, 如此列出,并且在得到回答之前不测试该类。

错误路径也是等价类

不快乐的路径是未测试代码积累的地方, 因为在正常使用中没有东西练习它们——锁持有者中途死亡、 不存在的映射文件夹、 缺失预期字段的输入。 每个都是一个等价类,与任何输入范围一样有权获得测试。 一个仅证明工具在一切配合时工作正常的套件,测试的是演示, 而不是工具。

用例在代码存在前枚举等价类

用例是已经切割成等价类的规范:一个主流程、 每个替代流程、每个异常流程、 以及每个可进入的前置条件。 编写它的人已经完成了本技能要求的枚举——替代和异常流程就是错误路径——所以一个练习了主流程的套件只覆盖了文档列出的N个类中的一个。

从用例中读取流程并为每个分配测试, 然后检查前置条件, 因为一个流程只有在其前置条件可持有时才可达。 翻译页面的奇偶检查有一个主流程(两种语言编辑并提交)、 一个替代流程(声明为例外的一侧编辑)、 以及两个只有在前置条件写下后才出现的异常流程: 树是脏的,所以无法判断陈旧性; 历史被重写,所以声明的提交不再可解析。 两者在一个站点上一周内被命中。 仅快乐路径不会显示任何一个。

异常流程是否真的会发生,不能由其感觉多不可能来决定。 操作范围说明了什么超出范围,危险分析说明了什么仍然存在; 两者都存在于assurance-case中, 本技能采用它们产生的列表,而非凭直觉修剪。

覆盖率是命名标准下的数字

同一个测试文档有三种不同度量:100%的条件覆盖率、90.9%的MC/DC、 25%的决策规则覆盖率。 所有三个对同一文件都为真, 所以没有标准的覆盖率数字毫无意义, 而在计数后选择标准的数字意义更小。 在计数前命名标准, 使用教学大纲自己的术语,以便数字可验证: 分区覆盖率和 Each Choice(§4.2.1), 2值或3值BVA(§4.2.2),决策表列(§4.2.3), 所有状态、有效转换或所有转换(§4.2.4)。 MC/DC不在基础教学大纲中; 它来自安全标准(DO-178C, ISO 26262), 将其与其他并列而不说明,是一种混合引用,看起来像严谨。 数字欠读者的其余内容在measured-claims中。

检查测试套件:计数,不要判断

询问测试套件是否充分,会返回产生它的盲点。 同一模型上的第二次会话共享这些盲点, 同一个人第二天也会分享。 教学大纲称此为“一定程度的独立性”, 四个级别中的第二个,并说明了价值来自何处: “作者和测试者之间认知偏差的差异”(CTFL v4.0.1 §1.5.3)。 同一模型上的第二次会话共享偏差并只保留标签。 不共享偏差的步骤是计数: 列出规范中的等价类并勾选有测试的, 列出每个范围的两个边界,列出决策表的规则, 列出每个状态表的每一行, 并引用每个缺口的文件和行号。 预期值是否正确仍然是一个判断,并且留给未编写它的读者; 等价类是否有测试是算术。 如果计数是抽样而非穷举, 报告说明是抽样。 判决的默认反驳形式在adversarial-verify中; 本技能提供要计数的内容。

当请求移除一个等价类时

“双重发送不可能发生,因为按钮已禁用,删除那行”基于一个层中的一个守卫的强度,从套件中移除一个等价类。 按要求做,并用一句话说明哪个等价类现在未覆盖以及什么能抵达它: 状态机仍然接受来自任何不是按钮的调用者的第二次发送。 带那句话删除的行是决策; 不带那句话删除的行是永远无法再次找到的缺口。

未实现的路径有测试来表示

故意留在此次变更之外的路径、 此版本不支持的角色、尚未解析的格式、 抛出NotImplementedException或调用todo!()的分支, 仍然是输入的一个等价类, 测试套件记录今天在那里发生什么。 编写测试断言该路径以未实现的方式被拒绝: 它引发的异常或错误值、 缺失的菜单项、回答“不支持”的请求。

那个测试现在通过,并完成两项工作。 它将缺口放在测试套件的列表中, 阅读等价类的读者可以找到它, 而不是在注释或工单中。 并且它在有人实现该路径的那天变红, 那是必须编写其真正用例的时刻; 失败的测试就是提醒,它被替换而非删除。

跳过标记或待定测试不是

…(已截断以进行翻译)

Individual skills in this repo

This repo contains 2 individual skills — each has its own dedicated page.

ShortArrow/interface-design

Deciding how a screen looks and behaves, triggered by the moments that hand the decision to the user unworked: about to ask which layout, which colour, which placement, how to draw a diagram, how wide a pane, how to split a view; about to call a visual choice a matter of taste; about to treat "it looks fine" as having checked it; about to ship a control sized by eye; about to read an automated accessibility pass, or a Lighthouse score of 100, as a clean bill of health. Most of these have an answer in a published standard, a measurable threshold, or one screenshot — WCAG 2.2 fixes contrast, target size, reflow and focus; a design system fixes spacing and components; axe-core finds about 57% of WCAG issues, and Lighthouse's accessibility score is that same engine weighted, so both leave the rest to a person. What survives is a genuine question, asked with the evidence attached. Use before asking any question about appearance, layout or interaction, and before calling

ShortArrow/library-design

Designing a library — a published package, or an in-app module from the moment it has a second consumer. The implementation stays editable forever, while the public API, the dependency set, the performance characteristics and every observable behaviour freeze at first external use (Hyrum's law). Five duties to the caller: take no control, add no hidden cost (zero-cost abstraction as the bar), hold no hidden state, hide no failure, block no escape. Use when designing or reviewing a public API, when extracting shared code into a module, when asked to "make this reusable", when a signature-compatible change alters an observable behaviour, and when an application is being shaped around a framework's types and lifecycle.

相关技能