让 Agent 测试先行
系列入口:如何使用 Agent。相关:用 Agent 调试、用 Agent 重构。
一、为什么测试和 Agent 是天然搭档
Agent 很强,但它有个特点:你给的目标越具体、越可判定,它收敛得越准。 「写一个函数处理登录」这种模糊目标下,它只能猜你想要什么边界;而一组测试恰恰把「什么叫做对了」变成了客观、可执行的判据。测试先行等于在写实现之前,先给 Agent 立好验收边界。
这带来两个好处:一是 Agent 不用猜,实现到所有测试转绿即可;二是你不必靠主观感觉判断「写得行不行」,跑一遍测试,红绿分明。测试既是给 Agent 的目标,也是你验收的工具。

二、第一步:先把行为和边界讲清楚
写测试前,先和 Agent(或你自己)厘清这个功能的行为契约:正常输入给什么、异常输入怎么处理、边界条件在哪。
很多人直接让 Agent 「写实现 + 补测试」,结果测试只是照着它自己的实现倒推出来的——实现错了,测试也跟着错,形成自我印证,根本挡不住问题。测试先行的关键是测试来源于需求和行为约定,而非来源于将要写的代码。你可以先让 Agent 帮你列出应覆盖的场景清单(正常、异常、边界、空值、极端值、并发或顺序相关),由你确认这些确实符合需求,再据此写测试。
三、第二步:先写会失败的测试
针对确定的行为写测试,并在没有实现(或实现不完整)时运行,亲眼看到它失败。这一步常被忽略,却极其重要:一个从没失败过的测试,可能根本没在检验任何东西(比如断言写错、永远通过),你却误以为有保护。
看到它因「功能尚未实现」而红,等于确认了这条测试确实有效、能抓住缺失行为。这也给随后的实现设定了清晰靶子。
四、第三步:让 Agent 实现到转绿
此时再让 Agent 编写实现,目标明确:让全部测试通过。它每改一版就跑测试,红→绿的反馈客观迅速。这种协作把 Agent 的发挥限制在「满足既定行为」内,而不是自由发散、顺手加一堆你没要求的东西。
要提醒的是约束它只为让当前测试通过而实现,不要过度设计;额外需求应先变成新的测试。这样实现与需求严格对应,也避免无谓复杂度。
五、防止 Agent「为过测试而投机」
测试先行有个必须警惕的失败模式:Agent(或人)可能让实现去迎合测试的字面写法,而非满足测试背后的真实意图。
典型表现包括:在实现里硬编码测试用例的特定输入返回值,而非实现通用逻辑;针对狭窄的测试数据做特判;或利用测试本身的漏洞让它形式上通过。这样测试绿了,换个真实输入就露馅。防范方法:
- 测试要覆盖多组不同数据和边界,让硬编码无法蒙混,而不是只测一个例子。
- 包含随机或额外的隐藏校验(评审时临时加用例),检验实现是否真通用。
- 审查实现是否有对测试数据的特判,任何
if input == 测试值都值得警惕。 - 测试断言真实意图,而不是断言容易通过的表面现象。
核心认识:测试是意图的表达,不是可被绕过的关卡。 保留一组不对外公开、用于验收的用例,能有效防止面向公开测试「应试」。
六、绿之后:在测试护航下重构
测试全绿后,如果你想改善实现结构,可以放心重构——因为有这组测试兜底,任何行为偏差立刻暴露。这就自然接到了测试护航下的重构:先让行为被测试锁定,再优化结构,安全且从容。
六点五、最大的陷阱:测试照着实现抄
测试先行一旦变形,最常见也最致命的就是「让 Agent 先写实现、再照着实现补测试」。这样产出的测试有极强的迷惑性——它们通常会全绿、看起来很完整,但实际上什么都保证不了。
问题在于这些测试是在描述代码碰巧做了什么,而不是需求要求什么。如果实现里有个逻辑错误,照它写出来的测试会把这个错误当成预期固化,形成自我印证:代码错、测试也错、但两者一致,于是 CI 全绿、错误顺利流入生产。这类「实现驱动的测试」比没有测试更危险,因为它制造了被保护的假象。
守住这条线的关键是顺序与来源:测试必须来自你对行为的约定,而且要在(或独立于)实现之前确定。 实操中可以先让 Agent 列出它理解的行为清单和边界用例,由你对照需求确认或修正——这一步把人的意图注入进去;再据此写测试,然后才允许写实现。即使无法严格先写,也要保证写测试时以需求文档为准、回头检查它是否在为实现背书。
一个简单的检验办法:问一句「如果实现换一种同样满足需求的写法,这个测试还成立吗?」如果测试绑死了某个内部实现细节(而不是外部行为),它就过于脆弱、也更像在抄实现。好测试锁定的是行为契约,而非具体代码。
六点六、怎么设计一组「够用」的测试用例
很多人卡在「不知道该测哪些情况」。这里给一个可以直接套用的用例框架。
对一段逻辑,至少从四个角度想:正常路径(典型合法输入、预期输出)、边界值(空、零、最大值、恰好临界点、首尾元素)、异常与非法输入(缺失、格式错、越权、不允许的取值,期望被拒绝或给出错误)、以及组合与顺序(多步操作、状态累积、重复执行)。让 Agent 先按这个框架穷举候选,再由你挑选真正与业务风险相关的,避免它堆砌一堆形式相似、价值不高的用例。
设计时关注的是「做错代价高」的分支,而不是追求行数或覆盖率数字好看。一个精准命中关键边界的用例,远胜十个重复验证同一happy path的用例。把测试预算花在最可能藏 bug、且 bug 后果最重的地方,是务实而高效的测试设计。
六点七、测试质量本身也要检验
「有测试」不等于「测试有用」,一组形同虚设的测试给的是虚假安全感。怎么确认测试真的有效?
最直接的手段是前面强调的看着它先失败:在实现前或临时破坏实现时运行,确认测试会变红。一个无论代码对错都恒绿的测试(断言为空、被注释、断言写反)等于不存在。除此之外,可以做变异式抽查——故意把实现里某处改错(返回值、不等号、边界),看测试是否能抓住;抓不住,说明这里有覆盖盲区或断言太弱。
这种「检验测试」的动作尤其在让 Agent 批量生成测试后必要,因为它可能产出大量看起来正规、实则断言空洞的用例。把测试也当作需要被验证的产物,随机抽查几条是否真的对错误敏感,你才能确信这道验收门禁名副其实,而不是绿灯下的自我安慰。
六点八、验收测试与单元测试:该让 Agent 先写哪类
测试先行里有个选择:先写的是细粒度单元测试,还是端到端的验收测试?理解两者作用,能让你给 Agent 的边界更有效。
验收/集成测试从外部行为出发,直接对应「这个功能整体要达成什么」,数量少但能抓住需求是否满足,适合作为最先确立的总目标——它告诉 Agent「终点长什么样」。单元测试粒度细、跑得快、定位准,适合在实现过程中为各个部件补边界条件。一个务实的组合是:先用一两层验收测试锁定整体意图,再让 Agent 在实现具体逻辑时用单元测试铺满边界,既有全局靶心、又有局部反馈。
不要陷入「只写大量单元测试却没有一个验证整体行为」的状态,那样各部件看似都对、拼起来可能错位;也不必为每个琐碎内部细节都堆端到端测试,代价高、跑得慢。按层级搭配,让测试既对准需求、又能快速定位。
六点九、当 Agent 说「测试都过了」,你该追问什么
这是验收时最该警惕的一句话。全绿只在测试本身有效时才有意义,几个追问能快速戳破虚假安全感。
先问:这些测试在实现前失败过吗? 没失败过的测试可能根本无效。再问:覆盖了哪些边界和异常,有没有多组不同数据? 只验证一个 happy path 的测试挡不住真实输入的问题。接着问:断言检查的是真实结果吗,还是只断言了不报错? 空泛断言下什么都能绿。
你还可以像前面说的临时改坏一处实现,看测试会不会红——这是最直接的有效性抽查。核心态度是:不把 Agent 的「全绿」当结论,而把它当作需要核实的报告。 当这些追问都有满意答案,这组测试才真正立住了验收边界;否则绿灯只是颜色,不是保证。
六点十、测试先行如何改变你和 Agent 的对话
换个角度看,测试先行不仅是技术动作,更是一种沟通方式,它让你和 Agent 的协作质量明显不同。
没有测试时,对话常常是模糊的指令加主观的验收:「写好点」「看起来可以」。引入测试后,对话变成围绕具体例子的讨论:「输入空列表应该返回什么?金额为负要拒绝吗?」这些问题逼着需求里的含糊处提前暴露,很多歧义和遗漏在写实现前就被澄清,而不是做到一半才发现理解不一致。
这也让 Agent 的角色更像一个按约定执行的协作者:你提供或确认行为样例,它实现到样例通过,争议时回到测试这个客观第三方,而不是各执一词。把对话从形容词变成具体输入输出,是测试先行最被低估的价值。 它让需求更清晰、验收更客观,也让你对 Agent 的引导有了稳固的支点。
七、常见问题
Q:一定要严格 TDD 吗,写实现后补测试行不行? 不是教条。关键是让验收标准在(或尽早于)实现前明确;先写测试是把标准前置的最强形式。若事后补,也要从需求出发、别照着实现抄。
Q:Agent 写的测试我能直接信吗? 不能照单全收。检查用例是否真正覆盖需求和边界、断言是否有效;尤其警惕它用测试为自己的实现背书。
Q:写测试会不会更慢、不划算? 前期看似多花时间,但它减少了返工、让验收自动化,并为后续重构护航;对非琐碎、会长期维护的逻辑,通常总体更快、更稳。
Q:UI 或难自动化的部分怎么测试先行? 尽量把可判定的业务逻辑抽离出来用单元/集成测试锁定;难以自动验证的表现层再辅以人工检查。
Q:测试数量多少才算够? 没有绝对数字,关键是关键路径和高风险边界都被覆盖、且测试确实对错误敏感。宁可少而准,也不要堆砌大量重复、断言空洞的用例。
Q:需求本身还在变,适合测试先行吗? 更应先锁定当前已明确的行为;变化部分等需求清晰再补测试,避免为不确定的需求过早固化大量用例。
八、检查清单
- 已先明确行为契约与正常/异常/边界用例,并确认符合需求。
- 先写测试并亲眼看到它失败,确认测试有效。
- 让 Agent 只为通过当前测试实现,未过度设计。
- 用多组数据与隐藏用例防止硬编码和「应试」。
- 已审查实现中没有针对测试数据的特判。
- 转绿后如需重构,在测试护航下进行。
总结
让 Agent 测试先行,就是先把「做对」的标准客观化:先从需求而非实现出发,明确行为与边界、写出会失败的测试,再让 Agent 实现到转绿,并用多组数据和隐藏用例防止它投机硬编码;绿之后还能在测试护航下安全重构。测试既是给 Agent 的清晰靶子,也是你不依赖主观感觉的验收门禁。