AI 参与说明(Agent:Codex):本文由 Codex 基于文中所列研究论文、测试工具官方文档与工程实践资料协助整理。概念分类和落地顺序是工程建议,不是新的测试标准。资料整理于 2026-09-07。
资料与修订记录
本次撰写与校验运行记录:模型
gpt-6-astra;reasoning effort:medium;执行入口:Codex Desktop;提供方:openai;CLI 版本:0.153.4(不代表桌面 App 版本)。参数来自本次任务运行记录。
英文表述修订(2026-09-07,Agent:Codex):核对原始论文与 Stryker 文档,调整测试方法定义、区分 Mutant 状态并统一 Agent 术语。此次运行记录:模型
gpt-6-astra,reasoning effortultra,执行入口 Codex Desktop,提供方openai,CLI 版本0.153.4(不代表桌面 App 版本);上方medium记录仍归属于原有撰写与校验。
术语统一(2026-09-08,Agent:Codex):为 Specification、Test Oracle、Agent Evaluation 等概念固定英文名称,区分 Property、Mutant 与对应的测试方法;保留中文关系解释与既有参与记录。运行记录:模型
gpt-6-astra,reasoning effortultra,执行入口 Codex Desktop,提供方openai,CLI 版本0.153.4(不代表桌面 App 版本)。
导读补充(2026-09-08,Agent:Codex):在正文前增加关键术语的中文名称与简要解释对照表,正文继续使用统一的英文名称。运行记录:模型
gpt-6-astra,reasoning effortultra,执行入口 Codex Desktop,提供方openai,CLI 版本0.153.4(不代表桌面 App 版本)。
阅读前先看这几个词
先对照中文名称,再用简要解释理解开篇的关键术语;正文统一使用英文名称,具体边界和例子在后续小节展开。
| 英文术语 | 中文名称 | 简要解释 |
|---|---|---|
| Specification | 规格说明 | 对预期行为、规则及适用范围的明确描述。 |
| Test Oracle | 测试预言机 | 判断观察结果是否符合预期的依据或机制。 |
| Test Double | 测试替身 | 测试中用来替代真实依赖的对象。 |
| Mock | 模拟对象 | 用于检查依赖是否按预期被调用的测试对象。 |
| Unit Testing | 单元测试 | 围绕一个较小代码单元检查行为,例如一个函数或类。 |
| Integration Testing | 集成测试 | 检查多个模块或服务连接后能否正确协作。 |
| End-to-end Testing | 端到端测试 | 沿完整业务流程检查系统各部分协作后的结果。 |
| LLM-as-a-judge | 大语言模型作为裁判 | 让大语言模型依据明确标准,对结果或执行过程给出评价。 |
| Side Effect | 副作用 | 除了返回结果,还改变外部可观察状态的行为,例如写入记录或发送消息。 |
| Test Case | 测试用例 | 为验证某个行为而安排的输入、执行条件与预期结果。 |
| Assertion | 断言 | 检查某个条件是否成立,并在不成立时报告失败的语句。 |
| Deterministic Assertion | 确定性断言 | 按固定规则直接检查结果,相同的检查输入与规则产生相同判断。 |
核心观点:从“多写测试”转向“获得什么证据”
Agent 可以同时生成实现、测试和 Test Double(用于替代真实依赖,例如 Mock)。如果它们共享同一个错误假设,测试可能全部通过,却不能满足业务要求。因而需要分别检查三个问题:预期结果依据什么制定,测试有没有机会发现实现错误,以及失败能否稳定复现。
这些需求大多对应已有的测试思想。Agent 开发改变的是使用它们的成本与必要性,并没有让 Unit Testing、Integration Testing 或 End-to-end Testing 失去价值。也不必把每个功能都交给 LLM-as-a-judge:明确的数值、状态、权限与 Side Effect,通常可以用 Deterministic Assertion 检查。
概念地图:这些名称不在同一个维度
| 维度 | 代表概念 | 要回答的问题 |
|---|---|---|
| 验证对象与范围 | Unit Testing、Component Testing、Integration Testing、Contract Testing、End-to-end Testing | 测的是哪一段真实系统,哪些依赖被替换? |
| Specification 与工作方式 | Acceptance Criteria、Executable Specification、Test-driven Development、Behavior-driven Development | 预期行为从哪里来,怎样约束实现? |
| 输入与反例生成 | Example-based Testing、Property-based Testing、Metamorphic Testing、Model-based Testing、Fuzz Testing | 除了想到的几个例子,还能怎样寻找错误? |
| 对照与判定依据 | Test Oracle、Reference Model、Differential Testing、Characterization Testing、Snapshot Testing、Approval Testing | 根据什么说结果对、错或发生了变化? |
| 测试质量与运行环境 | Mutation Testing、Code Coverage、Hermetic Testing、Deterministic Replay | 测试是否敏感、隔离、可复现? |
| 非功能属性与约束 | Performance Testing、Security Testing、Accessibility Testing、Architecture Testing | 功能之外的成本、权限、体验和结构是否符合要求? |
| Agent 自身 | Agent Evaluation | Agent 是否完成任务、遵守约束,以及结果是否可靠? |
例如,同一个 Test Case 可以在 Unit Testing 中采用 Property-based Testing 生成输入,用 Reference Model 判定结果,再用 Mutation Testing 检查 Assertion 是否足够敏感。这些术语可以组合,不是一条只能选一个位置的测试层级。
Test Oracle:谁定义“正确”
Test Oracle 是判断观察结果是否符合预期的依据或机制。它可以是明确的期望值、必须始终满足的业务规则、独立的 Reference Implementation、已审阅的 Baseline,也可以来自人工判断或 LLM-as-a-judge。
假设需求是“未登录时禁止提交”,Agent 却理解成“离线时一律入队”。若测试也从后者推导期望值,实现和测试就会一起出错。独立性首先来自 Specification(对预期行为与约束的明确描述)、证据和变更控制,而不是简单地换另一个 Agent 写测试。
实践中应保留可追踪的需求 ID,把关键期望值连接到业务规则。Coding Agent 可以提出 Specification 变更,但不能通过删除 Assertion、修改 Baseline 或跳过失败场景来自动宣告修复。采用 LLM-as-a-judge 时,要记录 Rubric、模型配置、实际输入证据,并用人工标注的正反例校准。
用业务要求约束开发过程
Acceptance Testing 从可观察的业务结果验收功能,例如“同一个草稿重复提交,不产生两条业务记录”。Executable Specification 将 Specification 与可以运行的检查连接起来;Behavior-driven Development 强调围绕行为与例子建立共同理解,不能仅等同于一种 Given / When / Then 文件格式。
Test-driven Development 是开发工作方式:先写一个体现目标行为的失败测试,再实现让它通过,最后重构并保留测试。它不规定必须使用哪种测试范围。让 Agent 执行这套循环时,要先确认测试因为预期行为尚未实现而失败,而不是因为导入错误或环境损坏而失败。Test-driven Development
它们适合减少需求歧义,但不能保证 Specification 本身正确。对于小范围、低风险修改,不必机械地增加测试;应优先覆盖会造成真实损失的错误模式。
Property-based Testing:验证规则,而不只枚举例子
Example-based Testing 指定几个输入与结果;Property-based Testing 定义输入空间和应成立的 Property(测试期望成立的关系或约束),由工具生成多组输入,寻找反例。部分工具还能通过 Shrinking 缩小失败输入,便于诊断。Hypothesis 官方介绍
对跨端业务逻辑,可以检查:
- 在约定的非负金额域中,最终价格不应为负。
- 对同一个业务操作 ID 的重复请求,不应重复产生业务记录。
- 排序后的元素多重集合与原输入相同,且顺序符合比较规则。
Property 必须说明适用域。例如,“增加商品不会降低总价”在有满减优惠时未必成立;“编码再解码得到原值”需要排除不支持的数据类型,并定义相等语义。错误的 Property 会把正确实现判为失败。
对 Agent 的任务可以从“多补几个测试”改成“列出关键 Property、适用范围和反例生成策略”。测试通过仍只是已执行输入的证据,不是对所有输入的形式化证明。
Metamorphic Testing:没有现成答案,也可以检查结果之间的关系
Metamorphic Testing checks specified relationships between the outputs of related test inputs. These relationships help construct follow-up tests when individual expected outputs are difficult to determine. Metamorphic Testing 原始技术报告
例如,在明确不考虑顺序相关优惠、舍入和溢出的整数加总模型中,调换项目顺序不应改变总和。对于只做确定性排序的函数,重新排列输入后,排序结果应一致。
这类关系也可以由 Property-based Testing 生成输入来检查,两者并不互斥。关系仍需经过审阅:搜索排序可能受个性化影响,图像变换可能改变识别目标,不能直接套用“输入变化、结果不变”。
Mutation Testing:故意改坏实现,看测试会不会失败
Mutation Testing introduces small changes into a program and reruns its tests. A Mutant is killed when a test fails because of the change. Stryker:What is mutation testing?
例如,把 > 改成 >=、删除分支或替换返回值,再检查现有测试能否识别变化。Code Coverage 只说明哪些代码被执行过;这些定向改动则帮助检查 Assertion 是否足够敏感。
对 Agent 生成的权限、边界比较和错误处理测试,这是一种有价值的质量检查。它直接追问:如果实现犯一个常见错误,测试能否阻止它?
Mutant 指施加小改动后的程序版本。Survived 表示测试仍通过,但不一定代表漏测:该 Mutant 也可能与原程序在适用域内等价。CompileError 和 Timeout 是另外的状态,不能归入 Survived;在 Stryker 的统计中,Timeout 计入已检测的 Mutant,CompileError 不计入有效的 Mutant。应区分状态并报告选择范围,不能把一个高 Mutation Score 当成系统正确率。优先检查关键模块或当前变更,控制计算成本。Mutant states and metrics
Model-based Testing 与 Stateful Testing:验证操作顺序
许多 Bug 不在单个函数调用中,而在“先做什么、再做什么”。Model-based Testing 用简化的 Reference Model 描述允许的状态与动作;Stateful Testing 保留动作之间的状态联系。工具可以生成动作序列,并把真实实现与 Reference Model 比较。Hypothesis:Stateful Testing 文档
例如,离线草稿可能经历:编辑、提交、超时、重试、退出账号、重新启动。每一步都应满足账号隔离、状态合法性与记录唯一性等要求。只验证“点击一次提交成功”,无法覆盖这些组合。
这里的 Reference Model 描述业务状态,不是 LLM。它应比实现简单,避免复制实现的同一错误。只按顺序执行操作的 Model-based Testing 也不自动证明并发正确;并发操作还需要调度控制、历史记录以及明确的一致性判据。
用现有系统作为对照
这几种方法适合 Agent 重构、迁移或跨端重写,但其证据含义不同:
| 方法 | 对照方式 | 关键限制 |
|---|---|---|
| Differential Testing | 同一输入交给 iOS / Android、新旧实现或多个独立实现 | 一致可能是一起错;不同也可能是允许的平台差异 |
| Characterization Testing | 先记录既有实现的实际行为,作为重构保护 | 记录的是“现在怎样”,不能自动升级为“业务应当怎样” |
| Snapshot Testing 与 Approval Testing | 输出与已审阅的 Snapshot 或 Baseline 比较 | Baseline 质量决定意义;自动接受所有变化会削弱保护 |
应明确哪些历史行为必须保持,哪些是准备修复的缺陷,并把后者改成符合 Specification 的期望。对输出做归一化时,只排除真正无关的随机 ID 或格式差异,不能顺手忽略业务时间、权限字段或错误码。
既有系统难以直接测试时,可以先寻找可替换依赖、可观察结果的边界,再建立保护性测试。Legacy Seam
Fuzz Testing 与 Fault Injection:分别扰动输入和环境
Fuzz Testing 大量生成输入或修改已有输入,寻找崩溃、挂起、Assertion 失败或其他异常。它适合解析器、文件导入、协议和不可信输入边界;不是只能做纯随机字符串测试,也不能只靠“没有崩溃”判断业务正确。OSS-Fuzz
Fault Injection 则改变依赖或执行环境:超时、断连、失败响应、重复消息,或在明确时点中断进程。对同步、上传和重试功能,可以在测试环境注入“服务端已经成功,客户端丢失响应”,再验证恢复与幂等行为。
Chaos Engineering 通常进一步关注系统在受控扰动下的韧性假设、稳态指标和影响范围;并非每个超时 Mock 都需要称为 Chaos Engineering。日常 Agent 开发优先在隔离环境做可复现的 Fault Injection,按风险决定是否需要更大范围实验。Principles of Chaos Engineering
Hermetic Testing 与 Deterministic Replay:给 Agent 稳定的反馈
Hermetic Testing 强调隔离或控制外部依赖;Deterministic Replay 强调记录并重放影响执行的输入、事件和调度条件。两者都服务于稳定诊断,但并非同义词。
应显式控制时间、随机数、数据初始状态、外部响应和资源清理。遇到不稳定测试,先定位原因;让 Agent 不断重跑直到一次成功,只会隐藏问题。重试可以帮助识别波动,但不能代替修复。Eradicating Non-Determinism in Tests
固定 Random Seed 也不保证一切可复现:网络、线程调度、文件系统状态和外部服务变化可能仍然影响结果。对生成式测试,应保存失败输入或事件序列,以及相关环境版本。
Architecture Testing 与非功能测试
Architecture Testing 把允许的模块依赖和结构规则变成可检查的约束,例如“业务层不依赖 UI 框架”“客户端模块不导入服务端实现”。它适合限制 Agent 在快速补功能时引入的依赖混乱,但不能判断架构本身是否适合业务。ArchUnit User Guide
功能通过也不代表性能、权限、可访问性或资源成本合格。按实际风险增加明确检查:越权访问是否被真实服务端拒绝,大文件处理是否超过资源预算,关键任务能否用辅助技术完成。阈值需要说明环境、样本和测量方式,不能用一次耗时结果宣称性能稳定。
Agent Evaluation:测试的是 Coding Agent 本身
当目标是比较模型、Prompt、工具配置或 Harness 时,被测对象是 Agent 的工作过程与交付结果。一个任务的最终测试通过,不等于 Agent 在一批任务上可靠。
可同时观察任务完成率、代码或环境的实际最终状态、工具轨迹中的约束违规、成本和耗时。不要要求所有成功运行必须走完全相同的工具序列;只约束有业务意义的顺序或禁止行为。评估需要多次 Trial 与清楚的统计口径,不能把“多试几次至少成功一次”当作每次运行的成功率。Demystifying evals for AI agents
开发反馈使用的 Test Case 与独立评估任务应适当分离,避免反复针对同一套题调优后高估泛化能力。评估标准和隐藏 Test Case 的修改权限也应独立管理。Deterministic Assertion、LLM-as-a-judge 与人工检查可以组合,但每种证据的范围应分别报告。
最小可运行例子:用 Property 识别 Mutant
下面用 Python 3.9+ 标准库演示一个排序 Specification:输出既要有序,也要保留输入元素的出现次数。将代码保存为 test_sort_properties.py,运行 python3 test_sort_properties.py。输入是整数,示例不涉及自定义比较器、浮点 NaN 或对象稳定排序。
这不是完整的 Property-based Testing 工具或自动 Mutation Testing 框架:它显式枚举有限输入,并手动注入一个错误,用来说明两种思路的关系。
from collections import Counter
from itertools import product
def ordered(values):
return all(a <= b for a, b in zip(values, values[1:]))
def verify(sort_fn):
count = 0
for size in range(5):
for values in product((-1, 0, 1), repeat=size):
result = sort_fn(values)
if not ordered(result):
raise AssertionError(f"Not ordered: {values} -> {result}")
if Counter(result) != Counter(values):
raise AssertionError(f"Elements changed: {values} -> {result}")
count += 1
return count
def wrong_sort(values):
return sorted(set(values)) # Deliberate mutation: loses duplicates.
if __name__ == "__main__":
print(f"Correct implementation: {verify(sorted)} cases passed")
try:
verify(wrong_sort)
except AssertionError as error:
print(f"Mutation detected: {error}")
else:
raise AssertionError("Mutation survived")
预期输出:
Correct implementation: 121 cases passed
Mutation detected: Elements changed: (-1, -1) -> [-1]
只检查“输出有序”,错误实现也会通过;补充“元素及次数保持不变”,才会识别这个 Mutant。这个例子说明 Property 需要足够完整,而不是证明排序函数对所有输入都正确。
如何用于日常 Agent 任务
不必每次启用所有方法。先根据失败风险选择证据,再决定工具和成本:
| 改动 | 优先方法 | 希望获得的证据 |
|---|---|---|
| 修复明确 Bug | Regression Testing,必要时按 Test-driven Development 推进 | 未修复版本因目标缺陷失败,修复后通过 |
| iOS / Android 重写同一业务功能 | 共享 Specification、Property-based Testing、Differential Testing | 各端分别符合 Specification;允许差异有明确说明 |
| 离线同步与重试 | Stateful Testing 与 Model-based Testing、Fault Injection | 错误顺序与恢复场景下状态和Side Effect仍正确 |
| 大规模重构 | Characterization Testing、Architecture Testing、关键 End-to-end Testing | 约定保留的行为不变,依赖边界与关键路径成立 |
| 解析或导入不可信文件 | Fuzz Testing、资源限制与明确的错误 Assertion | 异常输入被处理,失败可重现 |
| 怀疑 Agent 测试过弱 | 定向 Mutation Testing | 常见错误能被现有测试识别 |
| 更换编码模型或工具链 | 独立 Agent Evaluation、多次运行 | 按统一任务和口径比较结果、约束与成本 |
对所有场景,共同要求是:说明替换了哪些依赖,保留实际执行证据,检查测试是否真的被发现和运行,并报告尚未覆盖的风险。自动生成的测试数量与 Code Coverage 可以辅助观察,不能代替这些判断。
关联阅读
- Contract Testing:接口兼容、多端功能一致性与 AI Agent 编码:接口与多端业务 Specification 的具体落地方案。
- 前端 UI 测试全景:浏览器行为、视觉和可访问性验证。
- 技术文档术语与 Mermaid 改写规范:本篇采用的命名规则与分批验收方法。
- Agent 工程实践导航。