一条“测试通过”的记录,如果只写了结果,过几天可能就很难重现。代码更新了,依赖重新安装了,运行环境也换过了。即使测试命令没有变,实际参与这次测试的东西也可能已经不同。
要让测试结果便于检查和比较,需要把它对应的代码、依赖、环境和输入一起记录下来。软件配置管理中的版本标识与配置控制,就能帮助解决这类问题。
1. 固定具体提交,不只记分支名称
假设测试记录写着“在main分支上通过”。当天这句话可能足够,等分支又增加几个提交,main指向的就已经是另一份代码。
Git的分支名会随着开发推进而移动。具体的提交标识则能指出历史中的某一个版本。因此,保存测试记录时,应当记录对应仓库和完整提交标识,分支名称可以作为补充,但不宜单独充当版本依据。Git对分支与提交的定义 ↗
不过,记下提交标识还不能直接证明当前目录就是那个版本。
工作目录里可能存在未提交的修改,也可能多了一个临时测试文件。如果这些内容参与了测试,只记录原来的提交,别人检出同一版本后仍然可能得不到相同结果。
可以先检查工作目录状态,再把额外修改保存为明确的补丁,参与运行的新增文件也要一并保留。普通的git status会列出已跟踪文件的变化,以及未被忽略的未跟踪文件;被忽略的本地配置、缓存或生成文件,还需要根据项目情况另行检查。git status文档 ↗
这样,测试对象才能描述清楚:它以哪个提交为基础,又额外包含了哪些改动。
2. 代码固定以后,依赖也要有确定的来源
程序通常不会只运行仓库里自己编写的代码。测试框架、工具库以及这些库继续依赖的其他包,也会参与执行。
以npm项目为例,package.json里的依赖声明可以使用版本范围。如果重新安装时选中了范围内的新版本,就可能出现业务代码没改、实际依赖却变了的情况。
package-lock.json记录了生成的具体依赖树,包括间接依赖,帮助后续安装重建相同的依赖选择。因此,需要把依赖清单和对应的锁文件一起纳入版本管理,而不是只保留package.json。package-lock.json文档 ↗
对于已有锁文件的npm项目,npm ci适合按照锁文件执行干净安装。如果锁文件与package.json不匹配,它会报错,而不是自动修改锁文件。它也会先移除现有的node_modules,因此应在专用测试目录中,按项目要求使用。
安装工具版本和影响依赖树的选项也要保持一致。比如生成锁文件时使用过影响依赖解析的参数,之后安装时不能随意换一套参数。npm ci文档 ↗
锁文件解决的是依赖选择问题。运行时版本、系统差异,以及安装过程中执行的脚本,仍然可能影响最终得到的环境。
3. 同一套依赖,还需要相同的运行条件
记录环境时,最先想到的通常是操作系统和运行时版本。这些确实重要,但测试还可能受到配置和初始数据的影响。
例如,一个日期处理测试使用了本机默认时区,在两台时区不同的机器上就可能得到不同结果。另一个测试如果读取上次运行留下的缓存,第一次和第二次运行的起点也不相同。
一份实用的运行记录,可以包括:
- 操作系统、处理器架构,以及实际使用的运行时和包管理器版本。
- 完整测试命令、工作目录、必要参数,以及会影响行为的非敏感配置。
- 输入数据的版本,以及测试开始前的数据库、文件或缓存状态。
- 外部服务是实时访问,还是由固定的测试数据替代。
这些信息不是越多越好,而是要能解释影响结果的条件。构建环境的记录也遵循类似思路,需要保留实际参与过程的依赖版本、配置参数和环境变量。构建环境的相关属性 ↗
每轮测试还应尽量恢复相同的初始状态。比如测试需要写入临时文件,就在独立目录中准备输入,结束后清理本轮产物,避免下一轮接着使用残留数据。
如果测试仍然时而通过、时而失败,应继续检查共享状态、执行顺序或并发影响。单纯多跑几次,直到碰巧通过,不能说明这些原因已经消失。pytest关于不稳定测试的说明 ↗
4. 比较修复效果时,让两次运行尽量只差修复本身
假设要验证一个补丁是否修复了问题,可以先在固定的原始版本上运行相关用例,保存失败输出;再在相同基础上应用补丁,重新运行同一组用例。
两次运行应尽量使用相同的依赖、命令、输入和初始状态。如果测试用例是为这次问题新增加的,也应让它同时作用于原版和修复版。这样才能观察同一个检查条件在改动前后的表现。
如果一边应用补丁,一边升级运行时、更新依赖,再看到测试通过,就很难判断结果究竟来自哪项变化。如果修复本身必须调整依赖,则应把这项调整明确算进修复范围,而不是当成无关的环境准备。
保存结果时,也不要只留一行“全部通过”。最好同时保留执行命令、退出状态、实际执行的用例及关键输出,并说明有哪些步骤没有运行。例如只验证了一个函数,就如实记录函数层面的结果;页面交互、网络请求或完整构建仍需要各自的检查。
固定这些条件,是为了让一次测试可以被检查和重跑,并不保证程序从此不会出现偶发问题。如果同样条件下仍然产生不同结果,那些差异本身就是继续调查的线索。
之后上游更新了,也可以保留原来的记录,另选新的提交重新做一次原版与修复版对照。这样能够看清:旧问题是否仍在,新版本是否改变了触发条件,以及同一个补丁现在是否还适用。
