测试基建前置:把规约变成可执行约束

测试是 spec 的可执行形式。验收标准写的是自然语言,测试把它翻译成机器能运行的断言。这个翻译必须在第一行业务代码之前完成,这样 Agent 从执行的第一步开始就有反馈信号:这一步是否忠实于 spec。

但一个 feature 不是一个整体。它涉及前端页面、后端接口、数据库操作,跨多个模块。如果不定义模块之间的边界,测试只能走端到端的全链路,从前端操作一直到数据库写入。全链路测试有价值,但粒度太粗:一个测试失败了,不知道是前端渲染的问题、后端逻辑的问题还是数据库查询的问题。定位成本高,修复周期长。要在执行过程中给 Agent 精确的反馈,测试必须能在模块级别运行。这就需要先定义可测试的模块边界。

API Contract:可测试的边界

API contract 定义了每个模块的输入和输出。后端接口接收什么参数,返回什么结构,什么情况下报错,报什么错。有了这个定义,三件事变得可行。

第一,模块可以独立测试。后端还没写的时候,前端可以基于 contract 搭建 mock server,验证自己的调用逻辑是否正确。前端还没写的时候,后端可以用 contract 定义的输入参数直接测接口。每个模块在自己的边界内验证"我是否实现了 contract 规定的行为",不需要等其他模块完成。

第二,测试断言有了明确的参照物。Contract 说这个接口返回一个包含 status 和 data 字段的 JSON,测试就断言返回值包含这两个字段。参照物是 contract 文档,不是 Agent 碰巧写出来的实现。

第三,问题定位变得精确。某个模块的测试失败了,问题就在这个模块的边界内。不需要在全链路里逐层排查。

这也是多 Agent 并行开发的前提(第五章展开):有 contract 才能让多个 Agent 独立开发不同模块而不互相冲突。

边界定义好了,下一步是在这些边界上搭建测试。

编码前生成测试

测试的输入有两个来源:spec 中的验收标准,和架构文档中的 API contract。测试编写的时候,业务代码还不存在。这个时序保证了测试独立于实现,是对 spec 意图的直接翻译,不会被实现细节污染。

从验收标准生成的测试覆盖用户可感知的行为。Spec 里写"支付成功后在账单页出现一条记录",测试就是:调用支付接口,传入有效参数,验证账单表新增了一条记录,验证接口返回了成功状态码。Spec 里写"每个目标最多 5 个 KR",测试就是:创建一个目标,添加 5 个 KR,尝试添加第 6 个,验证接口返回错误。每条验收标准对应一个或多个测试用例,Given/When/Then 结构自然映射为测试的 setup/action/assertion。

从 API contract 生成的测试覆盖接口级行为。Contract 定义了每个接口的输入参数、返回结构和错误码,测试逐一验证这些约定是否被实现。这类测试的粒度比验收标准测试更细,覆盖的是模块边界上的契约遵从度。

暂时不存在的依赖用 mock 替代。前端测试用 mock server 模拟后端接口,后端测试用 mock 数据库或 mock 外部服务。Mock 的行为基于 API contract 定义,不是凭空编造。这让测试框架在编码开始前就能搭建完成并运行(全红,因为实现还不存在)。

为什么集成测试是验收的主力,而不是单元测试?集成测试验证的是用户故事能不能走通,直接对应 spec 中的意图。单元测试验证的是函数返回值对不对,这是实现细节,和 spec 意图的距离太远。更关键的是,Agent 可以通过调整实现的内部结构来让单元测试通过,但要伪造一个完整的用户旅程(从 API 调用到数据库写入到返回值验证)要困难得多。单元测试不是没用,Agent 可以自由编写单元测试来辅助自己的开发过程。但作为验收依据,集成测试是更可靠的信号。

过程中的持续验证

测试框架就位之后,验证就成为执行过程的一部分,不是事后的检查步骤。

Agent 的工作被拆成小块:实现一个接口,完成一个用户故事,处理一个边界条件。每完成一块,立即运行相关的测试。测试通过,进入下一块。测试失败,当场修复,不带着偏差继续推进。

这个节奏的价值在于把偏差的存活时间压缩到最短。引言中分析过,Agent 在长链执行中会漂移,第十步的代码可能已经偏离了第一步参照的 spec。如果一个偏差在第五步产生,到第五十步才被发现,中间四十五步的产出可能都建立在有偏差的基础上。如果偏差在第五步就被测试捕获并修正,影响范围被限制在这一步内。

这个闭环结构和规约章的迭代环是同一个模式在不同阶段的投影。规约阶段的循环是:人写意图 → Agent 展开 → 交叉验证暴露问题 → 修正 → 再验证。执行阶段的循环是:spec 定义行为 → 测试编码行为 → Agent 实现 → 测试反馈 → 修正 → 下一步。两个循环的共同特征是:每一步都有独立于 Agent 产出的反馈信号。规约阶段的反馈信号是交叉验证,执行阶段的反馈信号是测试。

通过测试检验规约

测试基建前置是对 spec 质量最早的检验。

如果一条验收标准无法被转化为测试,问题不在测试,在 spec。"系统应该有良好的用户体验"写不出测试,因为它没有定义可观测的行为。"用户提交表单后 2 秒内看到确认页面"可以写测试,因为它定义了具体的输入(提交表单)、输出(确认页面)和约束(2 秒内)。

这个检验发生在编码之前。如果 spec 有模糊之处,测试编写阶段就会暴露,你可以回到规约阶段修正,成本很低。如果等到编码完成后才发现 spec 模糊,修复的成本包括已经写好的代码和测试,远高于在 spec 阶段修正。

测试基建解决了行为正确性的验证:spec 说的功能,代码做到了吗。但引言中已经指出,测试覆盖不了所有的意图漂移。硬编码、超出范围的改动、架构偏离,这些不是行为层面的问题,测试不会捕获它们。下一节讲怎么用 code review 补位这些测试的盲区。


Harness Engineering Playbook · AgentsZone Community

results matching ""

    No results matching ""