5.3 测试代码也有规范


5.3 测试代码也有规范

本节摘要:测试代码是写给未来改代码的人的保险单,它自己也需要规范:命名让人不看实现就知道测什么,结构遵守准备执行断言三段式,断言精确到行为而非实现细节,测试彼此独立可乱序执行,测试与生产代码同等纳入评审。本节同时讨论覆盖率目标的合理设定与测试金字塔的层次分工。

本节导航

阅读完本节,你应当能够:

  1. 用"被测对象加场景加预期"三段式为测试命名
  2. 按准备执行断言结构组织测试体并说明理由
  3. 区分行为断言与实现断言并说明为什么只测行为
  4. 设计相互独立、可乱序执行的测试
  5. 合理设定覆盖率目标并理解其局限

一、问题与直觉:一份没人敢改的测试

一个团队的测试套件出了名地"脆":任何重构——哪怕只是提取一个函数——都让二十个测试变红;有的测试连数据库,跑一次八分钟;测试之间还有隐藏的顺序依赖,全量跑能过,单跑某个就挂(它依赖前一个测试留下的数据)。结果:测试失败不再意味着缺陷,而意味着"又要修测试了"。开发者看到红色第一反应是跳过而不是排查——当测试不可信,等于没有测试,甚至比没有更糟,因为它还在消耗维护工时。

这份脆性的根源是测试代码没按规范写:断言写到了实现细节上(改个内部结构就红)、测试之间共享状态(牵一发动全身)、命名含混(test1test47,红了一个不知道坏了什么)。测试代码的第一性要明确:它不是一次性验证脚本,是要被维护多年的代码——生产代码每次改动它都要跟着跑,生产重构时它要能不改而全绿(这是重构安全的定义)。所以测试需要与生产代码同级的规范,外加几条测试特有的纪律。

二、核心原理:五条测试规范

2.1 命名:测试的目录

测试名是失败时的第一行诊断信息。规范格式是"被测对象加场景加预期"三段式:

# 差 失败了不知道坏了什么 test_1() test_order_ok() # 好 失败信息自成一句诊断 test_calculate_total_applies_member_discount() test_withdraw_raises_when_balance_insufficient() test_fetch_user_returns_none_when_id_not_found()

好的测试名读起来是一句业务断言:"计算总价时应应用会员折扣"。测试列表因此成为模块行为的目录——新人想知道某模块支持哪些行为,读测试名列表比读代码快得多。

2.2 结构:三段式

每个测试体遵守准备、执行、断言三段:准备测试数据与环境,执行被测行为,断言结果。三段之间空行分隔,每段尽量短。三段式的价值在失败排查:断言失败时,读者扫一眼准备段就知道前提,扫一眼执行段就知道操作,定位是几何级的快。一个测试只测一个行为——多行为混测的测试失败时,你要先弄清是哪个行为坏了。

2.3 断言:测行为不测实现

这是最重要也最常违反的纪律。行为是外部可观察的契约(输入什么、输出什么、抛什么);实现是内部怎么做的。断言应只针对行为:

# 测实现 脆弱:把排序算法换成另一种就红 但行为没变 assert result.was_sorted_by_quick_sort() # 测行为 稳定:只关心结果有序 assert result == [1, 2, 3, 5]

测实现的测试会把每一次合理重构都误报为破坏,逼开发者"改实现必改测试",测试从保险单退化成枷锁。检验句式:不改变外部行为的前提下重写实现,测试应该全绿

2.4 独立:测试之间零依赖

每个测试自备数据、自建环境、自己清理,不依赖其他测试留下的状态,不依赖执行顺序。共享状态的测试是定时炸弹:单独跑挂、批量跑过(或反过来),排查顺序依赖是测试世界里最浪费时间的活动之一。需要昂贵公共资源的场景(数据库),用每个测试独立事务或测试夹具按测试粒度提供,而不是全局共享一份可变数据。

2.5 同等对待:测试也走评审

测试代码与生产代码同库同评审。评审测试时问三句:失败时名字能说明坏了什么吗;断言的是行为还是实现;这个测试依赖别人吗。草率的测试比没有测试更危险——它制造"已验证"的错觉。

三、工程实践要点

3.1 覆盖率:目标与局限

设定覆盖率目标(如核心业务逻辑百分之八十)有动员价值,但要清楚它的局限:覆盖率只说明"代码被执行过",不说明"行为被验证过"——没有断言的测试也贡献覆盖率。把覆盖率当体检指标(长期趋势)而不是考核指标(冲刺数字),否则会收获一批没断言的空跑测试。真正要盯的是核心路径覆盖变更覆盖(新代码必须带测试)。

3.2 测试金字塔与层次分工

测试分三层,数量呈金字塔:单元测试(量大、快、测单个函数与类,毫秒级,是日常开发的秒级反馈)打底;集成测试(中量,测模块间协作与真实依赖交互)居中;端到端测试(少量,从外部视角走完整业务流,慢而脆,只覆盖最关键链路)封顶。常见失误是倒金字塔——大量端到端测试撑起"安全感",每次跑半小时、随机挂两个,团队逐渐习惯性忽略失败。分层原则:能在下层验证的不拖到上层

层次 数量 速度 验证对象 失败定位
单元测试 毫秒级 单个函数或类 秒级定位
集成测试 秒级 模块协作 分钟级
端到端 分钟级 关键业务链路 小时级且脆

⚠️ 常见坑:为了凑覆盖率给 getter、setter、简单赋值函数写测试。这类测试贡献覆盖率数字却不拦缺陷,还稀释测试套件的信噪比。测试要瞄准有分支、有边界、有业务语义的逻辑——条件边界(空列表、零、负数、恰好越界)才是缺陷藏身处。

💡 关键直觉:写每个测试前默念它的完整生命周期——它将在未来几年里被执行上万次、在生产代码每次改动时被跑一遍、在某个深夜失败给你看。为这个生命周期写代码,命名、独立性、行为断言的规范就不再是束缚而是仁慈。

重点提炼

  • 测试是被维护多年的代码:不可信的测试比没有更糟,规范的目标是让红必是缺陷
  • 命名三段式:被测对象加场景加预期,失败信息自成诊断
  • 准备执行断言三段:段间空行,一测一行为
  • 只测行为:实现重写而行为不变时测试应全绿,这是重构安全的定义
  • 测试独立:自备数据自清理,零顺序依赖
  • 覆盖率是体检不是考核:盯核心路径与变更覆盖,警惕无断言的空跑
  • 金字塔分层:能在下层验证的不拖到上层,警惕倒金字塔

下一节是团队协作的最后一环:版本控制——把每次改动写成未来的排查档案。


作者与出处
原作者: 灏天文库
来源:灏天文库
整理: 灏天文库整理
由灏天文库平台收录,内容或由平台用户上传,仅供学习交流
发布者: 作者: 灏天文库 转发
评论区 (0)
U