ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

测试技术债务全面治理:从flaky test到数据工厂的实战指南

测试技术债务全面治理:从flaky test到数据工厂的实战指南 前几天接手一个维护了四年的老项目跑一遍回归测试三分之一的用例随机失败。有人说是环境问题有人说是数据污染还有人悄悄说可能是最近改代码搞出来的但没人能定位。最后大家心照不宣先把失败用例重跑一遍过了就算过。这种状态其实就是典型的测试技术债务已经堆到暴雷边缘。测试技术债务这个词听起来不像代码技术债务那么扎眼但破坏力一点不小。代码技术债务拖累的是开发效率测试技术债务拖累的则是整个团队的信任感——当测试结果开始变得不可信测试这件事就慢慢变成了走流程最终防线形同虚设。这篇文章想聊聊我这些年治理测试技术债务的实际经验它到底长什么样、怎么量化、怎么还、以及怎么防止它卷土重来。适合正在被不稳定测试、维护成本暴涨、覆盖率虚高这些事困扰的测试负责人和技术Leader参考。1. 测试技术债务不只是代码烂账先认清它长什么样很多人一提到测试技术债务第一反应是测试代码写得烂。这确实是其中一块但远远不是全部。我见过不少项目测试代码本身写得挺规范但照样被债务拖死。原因在于测试技术债务的藏身之处比大多数人想象的更隐蔽。1.1 测试债务的四个典型层次我把日常工作中遇到的测试债务大致分成四个层次每个层次的症状和影响面都不一样第一层是用例层。这个最直观测试代码耦合严重、断言缺失、用例之间互相依赖、一个工具类改个签名能挂掉几十个用例。典型表现是修改被测代码时明明功能没变测试却大量失败逼着人花半小时改测试代码。第二层是数据层。测试数据散落在各处有人用线上库的脱敏数据有人自己拼SQL往库里插有人在setup里硬编码一个电话号等哪天这个号码被人注册了用例就挂了。更麻烦的是测试数据没有隔离A用例改了某条记录B用例读到的就是脏数据两个模块单独跑都通过一起跑就互相踩。第三层是环境层。CI上用的测试环境跟开发环境混在一起谁部署个新版本都会影响别人测试数据库没做备份和恢复机制跑挂了只能靠手动重建还有各种第三方依赖测试环境连不上外网就整个链路瘫痪。环境不稳定导致的结果是很多团队用重跑来掩盖问题越掩盖越严重。第四层是流程层。这个最容易忽略没有明确的测试环境申请标准、没有测试数据管理规范、没有变更测试评估要求新需求来了直接写用例没人管用例质量也没人统计测试稳定性和执行成本。流程层的债务不是某一段代码的问题而是整套测试机制在腐烂。1.2 测试技术债务和功能技术债务的本质差异功能技术债务的还债动作通常是重构某一段代码风险可预估、收益可感知。但测试技术债务有个让人头疼的特点它还债的收益是间接的、滞后的。你花三天把一套混乱的测试数据改成工厂模式代码看起来没什么变化覆盖率数字也没提升只有等到某次紧急发布前别人因为环境问题抓耳挠腮时才能体会到自己省下的时间。另一个差异是功能技术债务通常由开发团队主导而测试技术债务常常处在开发觉得是测试的事、测试觉得是开发的事的真空地带。我遇到过一个典型的扯皮场景测试环境不稳定前端说后端改接口没通知后端说测试环境本身就有问题最后没人真正去修环境大家只是学会了在汇报时把风险提一句。1.3 一份高负债项目的症状清单如果你不确定自己的项目是否已经被测试技术债务缠上可以对照下面这份清单自检回归测试执行时间越来越长但没人敢删用例因为不清楚每条用例到底在验证什么。同一功能有多个层级的用例重叠覆盖UI层用例跑接口断言接口层用例又去连数据库边界完全模糊。测试用例随机失败而且失败原因五花八门超时、端口占用、数据冲突、断言顺序错乱。新人在第一个月几乎都在学怎么让测试跑过而不是怎么设计测试用例。测试环境需要手艺人维护只有一两个人知道怎么修环境他休假就抓瞎。覆盖率报告显示60%以上但产品上线后核心流程照样出严重缺陷——说明覆盖率被大量低价值用例注水。中了三条以上你的测试环节大概率已经在负债运转而且利息是按天计算的。2. 一把手盘点如何把隐性债务变成可量化的数字治理债务的第一步不是急着写代码修用例而是先搞清楚债在哪儿、欠了多少。很多团队失败在一步大家凭感觉说测试很乱但乱在哪、乱到什么程度谁也说不清。没有量化后面所有优先级讨论都会变成口水仗。2.1 从现有数据里找线索不用额外开发盘点测试技术债务不需要从零开始搭一套复杂平台大部分人已有的CI、覆盖率、缺陷管理系统里就有大量线索关键看你会不会捞。先从CI执行记录下手。把近三个月所有的测试执行结果导出来统计几个数字总执行次数、失败次数、失败后重跑次数、同一用例失败次数。这里有个小窍门找你技术平台的日志或者Jenkins的插件数据按用例维度统计失败频率很快就能揪出一批惯犯用例——它们可能只占用例总数的5%但消耗了超过40%的重跑成本。再看覆盖率报告的细节。不要只看总覆盖率要按模块拆开对比代码行覆盖率、分支覆盖率和变更覆盖率。我见过一个项目总行覆盖率68%但最近一次发布涉及的代码变更覆盖率只有11%等于新代码几乎裸奔。这说明用例增长的速度远远赶不上代码演进的速度覆盖债务在持续累积。然后是缺陷数据。把过去两个迭代线上和测试环境发现的缺陷拉出来按缺陷被发现时测试在哪里、为什么没拦住做根因归类。有时候会发现某类缺陷之所以漏出去是因为对应的测试用例压根没写或者写了但断言太弱——这种漏网之鱼是测试债务最直接的经济损失证据。2.2 给测试债务分类打分影响面、发生频率和修复成本数据捞完需要给每项债务排个轻重缓急。我用一个非常简单的评分模型影响面 × 发生概率 × 修复成本。三个维度都按1到5打分乘积越高说明这项债务越需要优先处理。举个例子。某个公共测试工具类被全项目80%的用例调用但内部维护混乱每次改它都引发连锁失败——影响面5分发生概率5分修复成本3分毕竟只有一个类要重构总分75属于最高优先级。反观某个边缘模块的测试数据硬编码只有7条用例受影响每次跑都挂但手动改一下数据就能好——影响面2分发生概率4分修复成本2分总分16就可以排到后面慢慢处理。打分过程强烈建议拉上开发和测试一起做不要测试团队自己闭门打分。原因很简单测试认为的高影响在开发看来可能无所谓反之亦然。大家一起打才能达成共识——共识是为后续还债争取资源的基础。2.3 建立一张测试债务台账量化完就要记账。我习惯用一张简单的表格维护测试债务台账不用什么高大上工具Confluence或者Excel都行。字段包括债务描述、所属模块、发现时间、影响面得分、概率得分、成本得分、总评分、状态待处理/处理中/已还款、负责人、计划处理日期。这张表的价值不在于形式而在于让债务可见。团队每周开站会时扫一眼新增债务随手加进去还掉的标成绿色慢慢地大家心里就有数了。最忌讳的情况是债务存在几个人脑子的我知道哪里有问题里人一走债就彻底成了无主之地。3. 还债不等于重写按ROI排序的治理策略盘点完债务最容易犯的错误就是头脑一热把测试推倒重来。我见过好几个团队雄心勃勃地决定用Pytest重写所有接口测试全面切换到新框架结果搞了三个月旧用例还没删完新框架水土不服测试团队陷入更深的泥潭。推倒重来不是不可以但它应该是最后选项而不是默认选项。3.1 为什么推倒重来往往是最大的坑测试代码和业务代码有个很大区别业务代码重写原有功能是明确的照着写就行但一套老测试代码很多时候已经成了需求历史档案——别人为什么写了这条用例当时为了防什么bug断言为什么是大于等于而不是等于这些问题往往连写的人都忘了。贸然重写很可能把那些藏着历史经验的用例顺手丢掉等上线后踩了雷才想起来。而且重写周期太长期间团队无法正常回归业务方不会给你这个窗口期。所以我的原则是能小修绝不大改能用脚本自动化迁移的绝不手工重写。真正的治理应该是渐进式的——每天还一点保持系统随时可用。3.2 优先级排序先止血、再补漏、最后加固根据我的经验治理节奏可以分成三步每一步都有一个核心目标。第一步是止血目标是让测试结果重新可信。先把那些惯犯用例处理掉要么修好要么删除要么标记为known issue不再参与阻塞。同时把测试环境的稳定性问题解决掉不管是换机器还是加容器化总之要让用例跑失败的原因回归到代码本身而不是环境抽风。这一步通常能在一个迭代内见效团队对测试的信任感会快速回升。第二步是补漏目标是让覆盖率真正反映风险。结合第一步清理出来的低价值用例把节省出来的执行时间用在补核心链路的关键场景上。比如支付类的项目支付成功、失败、超时、重复通知这些场景必须每个都有断言完整的用例。这个阶段可以配合变异测试或者接口覆盖对比找出那些代码覆盖高但场景覆盖少的盲区。第三步是加固目标是让测试代码本身变得好维护。包括重构公共工具类、把硬编码测试数据切换到数据工厂、消灭用例之间的依赖、引入分层设计。这一步的产出是长期的短期看不到明显收益但会显著降低后续新增测试的边际成本。3.3 从flaky test治理到测试数据工厂两条最值得先行啃的硬骨头在所有测试债务里我建议优先啃两块硬骨头flaky test不稳定用例和测试数据管理。原因很简单这两块直接决定了测试执行的可信度和效率。flaky test治理有个很典型的方法论先隔离再分类最后根除。把随机失败的用例单独拉到一个标签组里先用重跑策略保证主流水线不崩然后逐个分析失败日志。常见的原因无外乎等待时间不够把sleep改成轮询、共享状态冲突用例并行导致、外部依赖问题mock没有彻底。每个flaky case都要写分析结论要么修好放回主套件要么删掉——绝对不能无限期留在known failure里自动重跑通过。测试数据管理这块投入产出比也很高。我的做法是搭建一个轻量测试数据工厂用函数生成各种状态的业务数据而不是靠SQL直插或手工准备。比如创建一条已支付、待发货的订单封装成一行代码用例里直接调用数据独立、生命周期可控。刚开始搭这个工厂会花一些时间但搭完后新增用例的成本会大幅下降数据相关的不稳定问题也会同步消失。4. 实战案例一个支付项目测试债务治理的完整过程理论讲了一堆不如看一个真实案例。这里分享一个我前几年参与的支付类项目测试债务治理过程会比较详细地拆解从盘点到还款的完整链路。4.1 项目背景和债务症状这个项目是个中台支付服务大概有两个核心模块交易流水模块和清结算模块。团队规模不大开发加测试一共十几个人项目已经运行了两年多。测试债务的症状非常明显回归测试总用例约1200条执行时间从最初的四十分钟膨胀到了三个半小时并行跑的时候稳定通过的用例大概只有六成每次发版本测试组需要提前一周开始清理数据、预留环境、逐条确认用例而且经常出现在测试环境跑得好好的一上生产就出问题的口碑翻车现场。4.2 治理前的问题清单和量化我用前面说的办法先做了一轮盘点得到的数据列在这里债务类型具体表现影响面1-5发生频率1-5修复成本1-5总分环境债测试数据库与开发库共用数据被随意修改554100用例债核心交易链路用例依赖测试数据库特定订单ID54360工具债公共请求类封装混乱有4套不同风格历史代码44580数据债用例内直插SQL无数据清理机制45480流程债没有用例评审环节无效用例持续进入套件33327总评分排下来最扎眼的是环境债和数据债。这两个直接导致大量随机失败如果不先解决后面干任何事都会被绊住。4.3 治理动作和节奏按迭代推进我们没有停掉日常业务来做大扫除而是规定每个迭代拨出20%的容量专门用于债务治理并且把治理动作拆成小块保证每个迭代都有产出。第一个迭代的治理目标是环境。我们把测试库从开发库中物理拆离做了一套定期恢复到基线数据的机制每晚凌晨自动跑备份恢复任务把所有测试数据重置为干净状态。同时把测试环境的部署流程改成按版本号拉镜像避免手工人肉deploy导致环境半更新的情况。第二个迭代我们集中处理flaky test。把所有随机失败的用例挑出来大概有80多条逐个分析失败日志。最终归类发现超过一半的问题来自共享数据库订单状态被并发用例修改而这个现象得以暴露也正是因为第一个迭代把环境问题消除了让真正的用例互踩问题浮出了水面。我们给这些用例统一加了独立的测试数据组并移到一套串行执行的任务队列里并行度降了一些但稳定性直线上升。第三个迭代开始做数据工厂。为交易、清结算各建了一个测试数据生成模块封装了创建订单完成支付触发清分生成对账单这些高频操作。旧用例里的SQL直插逐步替换成工厂调用每替换完一批就验证一次这组用例的独立运行性和执行时间。这个迭代结束时回归执行时间从三个半小时降到了一个小时以内。后面两个迭代做的事情更偏加固清理无效用例、合并工具类、给核心用例补断言。到了第五个迭代结束时1200条用例精简到1100条但稳定通过率达到了98%执行时间稳定在四十五分钟。4.4 治理结果和关键指标变化治理前后对比最直观的是三个数字回归稳定通过率从60%提升到98%。回归执行时间从3.5小时降低到45分钟。线上漏测缺陷数连续两个季度下降超过40%。但比数字更重要的是团队状态的变化测试人员不再花时间去排查到底是谁改了环境里那批脏数据开始有余力去设计更有价值的异常场景测试开发也不再因为随机失败而咒骂测试环境CI的信任度恢复了。项目后续再接手新需求时新增用例的边际成本明显下降——这其实就是把债还掉之后利息停止滚动带来的长期红利。5. 防止债务复发把债挡在门口的制度设计还债只是一时的真正难的是让债务别再回来。很多团队治理完一轮觉得自己大功告成结果三个月后测试又乱回去。原因很简单没有建立防止债务新增的闸门。治理和防御必须同步建设否则就是一边打扫地板一边滴水。5.1 让测试代码评审成为硬门槛测试代码进main分支前必须走评审。这一点很多团队只是口头说说。评审时重点看什么我总结了几条用例是否独立有没有依赖其他用例的执行顺序测试数据怎么来的硬编码还是工厂生成销毁机制是什么断言是否有效有没有出现只验证接口返回200不验证关键字段的通洞断言有没有绕过依赖是不是全都mock掉了把集成问题掩盖了测试代码评审不一定要开发Teammate来做测试团队的同伴互评就够。关键是把它变成流程的一部分而不是可有可无的环节。如果觉得评审拖速度可以只针对关键模块的用例做强制评审其余做轻量抽查。5.2 把覆盖率与稳定性指标钉在CI里指标不融入CI等于没设。我们的做法是在CI流水线里加了几条硬性校验新代码变更覆盖率低于某个阈值我们用的是80%构建直接红掉同一用例在最近50次执行中失败超过5次自动报警拉入flaky观察组测试套件整体执行时间超时失败防止又有人无限增加用例导致回归时间失控。这套机制一开始会让团队不适应因为经常有人提交时撞到覆盖率红线。但坚持一个迭代后大家就习惯了写完代码先补测试再提MR。这才是从源头阻止新债生成。5.3 定期开一次技术债评审会议我推荐每个迭代结束后抽出半小时开一个技术债评审会。会议内容很简单看台账新增了哪些债务哪些正在还哪些债务评分涨了。不需要大报告只需要每个人都对当前债务水位有数。这半小时不会浪费它最大的作用是让债务保持可见——而可见本身就是一种压力会让团队在下个迭代下意识地控制自己别再加新债。5.4 把债务意识种进团队文化最后一条偏软性但很重要新人培训时就要讲清楚测试代码是资产不是随便跑跑就行的附属品。让新人了解哪些是公共测试工具、哪些是很容易踩的坑、修改公共测试模块需要通知谁。我见过很多债务积累都是因为中间换了一两轮人新来的不知道老规矩凭感觉写测试结果两三个月就搞出一堆重复且脆弱的用例。建立一个小团队维护的测试开发规范文档并随着实践持续更新是成本最低的防线。6. 治理路上最容易翻车的几个坑和我的工具清单最后集中写几个治理过程中容易踩的坑以及我目前用下来顺手的工具组合。这些更多来自个人经验不一定适合所有项目但思路应该可以复用。6.1 常见的失败模式提前避开第一个坑是想一口吃成胖子。一次迭代想把所有债务清零结果什么都做了一点点什么都没解决。治理动作必须小步快跑每次只锁一两个目标完毕再扩张。第二个坑是只治理不验证。改了几条用例、换了一套数据工厂效果如何没有数据佐证全凭感觉。每次治理动作后必须跑一次对比至少记录执行时间、失败率这些基础指标用数字说话。第三个坑是忽视人的瓶颈。测试技术债务治理通常集中在少数几个核心人员手里其他人只想围观。一定要拆解任务到每个人头上——比如张三负责订单模块的用例清理李四负责数据工厂接口补充哪怕每人每天只投入两小时也比一个高手熬夜单打独斗更可持续。第四个坑是误把覆盖率高当账还清了。覆盖率只是其中一个视角真正要关注的是能否防止重要缺陷漏出。有些团队为了让覆盖率达标写出一堆笑话式断言这类债务比没有覆盖更糟糕因为它在制造虚假安全感。6.2 工具链参考我这边打算推荐几类常用工具这些都是这些年实际用下来比较稳的不涉及广告纯粹是个人习惯测试执行与CI流水线Jenkins或GitLab CI都行关键是做好插件与报告的集成让不稳定用例自动归档。覆盖率采集JaCoCoJava系、pytest-covPython系记得要按模块和变更分别统计。接口测试框架Rest Assured或者Pytest Requests看组里技术栈原则是统一且不过度抽象。flaky test管理可以在CI侧写一个简单的脚本从单测报告里捞失败列表自动触发重跑并分析稳定性趋势不必买商业工具。测试数据管理用工厂模式自研配合数据库备份恢复任务这套最笨但往往比气模框架更容易落地。6.3 最后说说我的个人体会测试技术债务治理这件事真正考验的不是技术而是耐心和共识。技术方案其实都很成熟难的是让团队相信投入时间还债是值得的尤其当业务方催得紧、上线日期压得近的时候。我自己最大的感触是还债的最佳窗口不是等有空了而是现在——因为债务会滚利息晚一天还后面支付的成本就高一分。如果你正被不稳定测试折腾得焦头烂额建议先从台账开始拿起Excel列几行把自己项目里最扎心的三条债务写下来。不用想太多先让债见到光治理就已经开始了一半。
返回列表