☰
测试用例考古学:用Playwright和Tessy挖透遗留系统
2026/10/8 10:15:59 网站建设 项目流程

接到那个项目的第一天,我从运维手里接过一份被称为“唯一还活着”的文档——一张三年前的Excel测试用例表。点开一看,用例编号到TC-471就断了,后面全是(废弃)标注。代码库是典型的老中医式:写文档的早走了,注释里一半是乱码,一半是前前任追星的碎碎念。在那一刻我突然意识到,测试用例压根不是什么验收依据,而是一根根能往地层深处戳的探针。

这根探针戳下去,戳出来的不只是bug,还有人的选择、项目的旧伤、以及不少装着没看见的历史遗留问题。这就是我想在这篇文章里聊透的东西:怎么用功能测试用例、单元测试用例设计方法、Playwright和Tessy这类工具,把一套没人敢动的老系统挖个底朝天;以及在这个过程中,当探针底下突然露出一个“不能写进缺陷单”的秘密时,一个QA到底该怎么选。这篇文章不打算讲纯理论,我按自己实际操作过的三次“考古”经历来拆,工具、模板、代码、伦理选择题全都有。

1. 为什么测试用例能当“考古探针”

1.1 探针原理:用输入输出反推黑箱结构

考古学里探针是个细长杆子,打进土里不为了挖,而是为了靠手感判断地下有没有空腔、墙体、墓道。测试用例在这套老系统面前干的就是同样的事:系统还在跑,但内部逻辑对你是半透明的,你不需要完全读懂源码,只需要构造一个输入,观察输出,然后跟“预期”比对,就能知道这一层土底下到底是什么结构。

关键是这个“预期”不能来自代码注释,也不能来自过期的需求文档。我用的最简单办法是:先写一个只记录不校验的冒烟脚本,把所有核心入口带真实数据跑一遍,把实际输出打印出来。第一轮你可能完全看不出对错,因为你不知道“正确”长什么样,但你会得到一个行为快照。第二轮开始,你拿这个快照当基线,改一个输入参数、切一个分支条件,再看输出怎么变。变化就是线索,不变也可能是线索——说明这个参数被某个历史分支吃掉了。

这跟黑箱测试原理是一回事,但心态完全不同。普通测试追求“验证需求是否实现”,考古式测试追求“发现系统实际是怎么活的”。需求早就死了,系统还活着,你要探的是活物。从这个角度讲,测试用例的断言,就是你的探针触到目标层时发出的那声响——响得对,你知道地层没塌;响得不对,底下八成埋着东西。

1.2 现代QA工具箱为什么选这三件套

我这次用的工具组合是Playwright、Tessy加Excel用例表,对应功能回归、单元级验证和用例资产管理三个层面。选它们不是因为它们最新,而是因为它们最适合当探针。

工具/方法探测层级核心用途为什么适合考古
Playwright端到端功能层给旧系统做行为基线自动等待稳、支持录屏和trace,证据链完整
Tessy单元/模块层C代码分支覆盖验证适合嵌入式老代码,能精确到语句和分支
Excel用例管理资产层记录用例、预期、证据、风险标志人人能读、可批量筛选、可导出评审

这套组合的核心逻辑是:Playwright负责告诉你“系统对外是什么样”,Tessy负责告诉你“系统里面哪个角落是死的”,Excel负责把这两层发现固定成团队能看懂的记录。以前我也试过用Selenium、Postman这类工具做类似的事,但实测下来有两个问题——一是新工具对老浏览器的兼容性太差,二是生成的报告只适合自己看,不适合拿去做评审证据。Playwright的trace文件可以直接发给开发,人家看一眼时间轴和网络请求就知道问题出在哪,这一点在推诿扯皮的环境里太重要了。

另外提一句,测试用例模板在考古项目里别用公司通用的那种“冒烟测试模板”“验收测试模板”。我后面会专门讲怎么改模板字段,这里先记住一个原则:考古用的用例模板,核心字段不是“预期结果”,而是“实际行为”和“偏差分析”。

2. 考古第一层:功能测试用例的“地表测绘”

2.1 用Playwright给旧系统建立行为基线

地表测绘这步,说白了就是先别急着找茬,先把整片区域走一遍,记录成图。我第一次接触那套老系统时,连登录后到底跳转到哪个页面都拿不准,文档说跳工作台,浏览器实测跳到了一个叫“中转页”的东西。这不是bug,只是没人更新文档,但对于后来的所有用例,这个中转页就是真实的地表特征,不是错误。

我写了下面这组Playwright用例,刻意把所有断言都写得“宽”——先只断言URL模式,其他的全部输出日志:

import { test, expect } from '@playwright/test'; test.describe('遗留系统地表测绘', () => { test('登录后实际跳转路径记录', async ({ page }) => { // 用环境变量注入账号,避免凭据进代码库 const username = process.env.PROBE_USER ?? 'qa_probe'; const password = process.env.PROBE_PASS ?? ''; await page.goto('https://legacy.example.com/login', { waitUntil: 'domcontentloaded' }); await page.fill('#username', username); await page.fill('#password', password); await page.click('button[type="submit"]'); // 只等待跳转,不预设一定是 /dashboard await page.waitForURL('**/*', { timeout: 10000 }); console.log('实际跳转URL:', page.url()); console.log('页面标题:', await page.title()); // 记录所有可见按钮文本,用来反推功能入口 const buttons = await page.locator('button, .btn, [role="button"]').allTextContents(); for (const b of buttons.slice(0, 20)) { console.log('可见操作:', b.trim()); } }); });

这段代码看着简单,但它是整个考古项目的第一个工具。跑完之后,我拿到了一份“系统实际入口清单”,里面有三个按钮的名字跟需求文档完全对不上。比如有个按钮叫“重新同步”,文档里写的是“修复数据”。我当时就意识到,文档描述的可能是三年前的逻辑,而按钮文案暗示这套系统经历过一次不彻底的重构。

实际操作中有几个坑要提醒。第一,不要在旧系统上一上来就用Page Object Model那一套,太重的封装会在你还没摸清页面结构时拖慢你。第二,Playwright的自动等待默认很激进,老系统接口常常要十秒才响应,建议把timeout调大,并且在关键操作前用waitFor而不是expect。第三,所有探针脚本务必能从命令行传参,因为旧系统的环境往往不止一套,你总得在测试环境、预发环境各跑一遍。

2.2 功能测试用例模板的“探方设计”

考古不是乱挖,要有方格网。Excel里的测试用例模板就是我的方格网。公司里常见的那种模板字段是:用例编号、模块、前置条件、测试步骤、输入数据、预期结果、实际结果、执行人。这套字段对付“验证有没有实现”够用,但对付“探明系统长什么样”太单薄。

我给老系统项目设计的模板长这样:

字段名填写说明考古用途
用例编号TC-AR-001探方编号
探针目标本次想摸清的行为/接口明确探测意图
输入扰动相对基线的改动(参数/顺序/状态)记录变量
实际行为系统真实反馈地层记录
预期依据文档/代码/历史工单判断偏差来源
偏差分析行为与依据的差异确认疑似文物
风险标记高/中/低决定是否下钻
证据链接截图/trace/日志路径后续评审材料

这个模板里最反直觉的是“预期依据”。普通用例模板里的预期写的是“正确结果”,但考古场景下你根本没有标准答案。所以我把“预期”改成了“依据”——你预期它应该怎样,是基于文档,还是基于代码注释,还是基于“我猜的”。如果三条依据互相打架,这个用例本身就是一个重大发现,哪怕系统行为看起来再怎么正常。

给老系统写快照用例,我总结出三条经验。一是每个用例只做一个扰动,别摸着摸着把三个变量一起改了,到时候出了偏差根本没法归因。二是先写“地层基线”用例,再写“扰动用例”,基线用例锁死当前行为,扰动用例负责探索差异。三是表格里必须留一列叫“重复执行稳定性”,因为老系统经常有偶发问题,一个用例跑三次结果不一致,这本身就是一条需要上报的高价值发现。

3. 考古第二层:单元级测试用例设计方法与Tessy实践

3.1 单元测试用例设计方法:从业务场景钻到代码结构

功能用例探到表层后,我已经锁定了好几个“可疑地层”。这时候要继续往下钻,就得进单元级。单元测试用例设计方法的核心是等价类、边界值、判定表、路径覆盖这四板斧,但在考古模式下,每一板斧问的问题不太一样。

等价类划分我用来回答“哪个输入类别的行为跟文档描述不符”。边界值用来找“刚好卡在阈值上的历史特判”——很多老系统都留着这种特判:某个参数等于0时走A逻辑,等于1时走B逻辑,中间一概当异常。碰到这种代码,你几乎可以断定当年有人做了个紧急修复,然后没补注释也没补测试,直接把系统留给后来人考古。

我印象最深的一次,是用路径覆盖挖出了一个“永远不可达”的分支。那个模块是个C语言写的鉴权函数,长约两百行,Tessy跑完之后报告显示有一行代码的覆盖率始终是0。我一开始以为是桩函数配得不对,查了半天才发现,那行代码所在的分支前面有一个逻辑嵌套,父条件是if (flag == 2),但flag在进入这个包时已经被强制置为1了。换句话说,这段代码从三年前一次“临时改动”开始就再也没被执行过,但它的存在一直在虚耗后来的维护者。

3.2 Tessy测试用例与Excel表的管理实践

Tessy是嵌入式领域常见的单元测试工具,尤其在汽车电子和通信设备这类对代码覆盖率有硬性要求的行业里会碰到。它用起来有个特点:测试用例可以手工在工具里录,也可以从Excel导入导出。很多QA一听到“代码覆盖率”就以为这是开发的事,其实Tessy用得好不好,关键就在于用例设计那一环。

我用Excel做Tessy用例的标准表头如下:

Excel列对应Tessy概念填写要点
TestCase_ID用例唯一ID推荐M_[函数名]_[序号]
Function被测函数必须带模块前缀
输入参数实参/桩值结构体参数要逐字段列清楚
Stub返回值桩函数行为区分“返回默认值”和“返回指定值”
预期输出期望返回值/被修改参数写清精度和范围
覆盖目标语句/分支/MC/DC对应Tessy里的CoverageGoal
依赖条件全局变量/静态变量初始化这是最容易被忽略的

Excel和Tessy的往返同步是个实操痛点。我的做法是先导出一份干净的模板,在Excel里批量填好用例,然后导入Tessy跑覆盖,跑完后把覆盖率结果再导回Excel的一列“实际覆盖率”。这里有个细节:结构体输入参数在Tessy里展开成很多行,直接在Excel里手写很累,我一般用公式生成struct.field的完整路径,再配合批量填充,效率能提不少。

这次考古我根据函数行为把鉴权模块的用例设计成了几张判定表:

  • 输入用户类型(内部/外部/第三方)与资源等级(机密/普通/公开)的九种组合
  • 时间戳参数取今天、去年、1970年、2038年四个边界值
  • 每个分支至少一条“真”路径和一条“假”路径,用于路径覆盖

判定表设计完再映射成Excel用例,Tessy就能一次跑完。有一个用例输出了一个让我起疑的结果:传入一个早已废弃的“资源类型=9”时,函数居然返回了“机密级别”。文档里根本查不到这个枚举值,顺着代码一翻,才发现结构体里有一个没人引用的联合体字段,里面埋着旧版的加密接口。这件事后来被证明是一个数据兼容性隐患,但它不是功能用例能发现的——功能层根本不会走到这个废弃联合体,只有单元级的探针才能触到。

4. 考古第三层:探针刺破地层,碰到“伦理困局”

4.1 发现了前辈们知道但闭口不谈的隐患

当你挖到这一步,真正的困局才开始。我在那套老系统里挖到了一段状态机代码,功能用例显示它的某个状态分支在线上永远跳不进去。测了三天,我确认这不是环境问题,于是翻历史工单,翻到三年前的一条记录,里面有句话写得很隐晦:“因业务原因,该分支暂不启用,已通过配置规避”。

我当时心里咯噔一下。这不是一个普通的历史遗留问题,这是有人“知道问题存在但选择了绕过”。我拿着这个分支的复现路径去找负责的老开发,对方一句话就让我愣住了:“这个分支别测了,测出来大家都不好看。”我后来才明白,那个分支如果被触发,会导致一个极端情况下数据错乱,但触发概率低、业务影响面小,三年前的团队决定不修,只是用配置把它绕开了。测试用例一旦把这条路径打通,等于把当年那个“心照不宣”的决定翻到桌面上。

这里我做了个选择,我把三种方案摆在脑子里比了一遍:

方案做法风险我的判断
沉默不报把用例删除,当没看见隐患埋着,未来出事责任在测试不可取
私聊相关人员口头告知,不落文档问题不被重视,也不会被记录治标不治本
按缺陷流程上报写缺陷单,附证据链,会议评审可能得罪人,但事实清楚我选择了这个

我当时的做法是:先把复现路径写成一份最小化用例,附上三年前的工单截图、当前的覆盖率报告、以及线上配置对比,然后在缺陷评审会上说了一段话:“这个分支现在不是bug,因为配置绕开了它;但如果配置被改,或者数据量达到某个阈值,它会成为线上故障。修复成本是X人日,不修复的风险是Y,请业务和开发一起拍板,我按结论走。”

这是关键一步:QA的伦理困局往往在于“测试发现了东西但没人授权你处理它”。我的原则是,我不替任何人做业务决策,但我绝不让决策者在不了解事实的情况下做决策。把证据链交出去,把选择权交出去,剩下的责任归属就清楚了。

4.2 “覆盖率必须百分百”的形式主义陷阱

考古行动进行到一半,领导发了一个指令:单元测试分支覆盖率必须达到100%。这个指标看起来很漂亮,但做过老系统的人都知道,硬指标催生出来的往往是数字游戏。

我见过一种特别坑的做法,就是给那些“永远不可达”的分支强行加一个空断言,或者用一个假的输入直接跳过判断。覆盖率报表上从95%涨到100%,但真实的质量一根毛都没提升。更麻烦的是,这种“为了指标凑覆盖”的用例会污染整个测试资产,等你真正想用这些用例做回归时,会发现它们全是占位符。

我的处理方法是做两套统计。一套叫“声明覆盖”,就是报表上给领导看的覆盖率,我如实统计Tessy跑出的数据。另一套叫“验证覆盖”,把那些真正有断言、有输入扰动、失败了能定位问题的用例单独汇总。声明覆盖是100%,验证覆盖可能只有83%。我会把两套数字都放在周报里,用一行字说明差异来源。这不是阴阳怪气,而是让数据自己说话:差的那些分支,是因为“绕开配置”或“废弃代码”没法被测。

这里有个很实用的技巧:给每一条“纯占位”用例在Excel里加一列“是否有效验证”,只有标注为“是”的用例才进入缺陷关联和回归分析。这样领导看到的是完整的数字,但团队内部有另一套准确的地图。别小看这一列,后来好几次风险评审,我都是靠这列数据挡住了“盲目冲指标”的决策。

4.3 QA的边界:不当道德警察,也不做装睡的人

考古挖到深处,很多新人会陷入两个极端。一个极端是把自己当成道德警察,觉得发现了历史问题就必须“昭告天下”,跟团队闹得很僵;另一个极端是选择装睡,用例发现了问题也不报,因为“别人都不管,我干嘛管”。

这两个极端我都见过,也都踩过。我的体会是,QA的职责边界很清晰,但执行起来需要技巧。第一,你不是产品经理,所以不用决定要不要修、什么时候修,这个问题请丢回给业务和开发。第二,你不是背锅侠,你的职责是“证明事实存在”,至于“为什么存在”那是开发要解释的。第三,你也不是透明人,你不能在已经掌握证据的情况下,把风险吞进肚子里。

我后来总结出一套“翻译话术”,专门用来把技术问题变成业务能听懂的语言。比如对付那个状态机分支,我不会说“路径覆盖率和判定表覆盖率不一致”,我会说:“线上如果出现某种配置组合,这里的数据可能错乱,最坏情况影响大约涉及X个用户,修复预估需要2天。”开发反驳的时候,我手里有Tessy报告、Playwright录屏、Excel历史工单对照表,每一样都是带时间戳的证据。

我记得那次评审会结束后,开发组的组长私下来找我,说了一句我一直记到现在的话:“要是早有人像你这么测,这个坑三年前就该填了。”那句话不是说我的技术多厉害,而是说:很多坑不是不能填,是没人把它挖出来放到阳光下。QA的价值从来不是会写几个漂亮用例,而是敢把真相摆到桌面上,并且把话说得让人无法忽视。

5. 避坑清单与复盘:让考古成果变成团队资产

5.1 遗留系统自动化的五个典型坑

考古探针要有效,探针本身得稳定。我在老系统上跑Playwright和Tessy时,踩过高频的五个坑,值得单独列一下:

  • 过度依赖自动等待:老系统的接口经常三五秒没响应,Playwright的自动等待一旦超时就重试,结果把偶发慢接口当成了失败。解决方式是给核心接口单独设置test.setTimeout(15000),并区分“超时失败”和“断言失败”。
  • iframe和动态元素:老系统特别喜欢用iframe包旧功能页面,Playwright默认只能访问主frame,必须手动page.frameLocator()切换。还有一类元素是点击后才动态生成的,要先触发事件再定位。
  • 环境依赖太强:老系统的测试环境连数据库、缓存、文件服务全靠一套共享环境,自动化跑着跑着就被别人的数据搞挂了。后来我给所有探针用例都加上“数据指纹”前缀,比如用户名后面拼时间戳。
  • Trace文件爆炸:Playwright的trace录多了以后,磁盘占用会非常大。考古阶段我建议只对可疑用例开启trace,别对全套用例开。
  • 用例太脆:一个用例里写了十几个断言,最后根本无法定位失败原因。考古用例坚持“一扰动一断言”,断言失败后先看日志而不是先改用例。

5.2 用例评审:让开发承认这是bug的三板斧

考古发现的价值,最终要靠缺陷评审会来确认。怎么让开发在评审会上认下这个“历史坑”,我有一套三板斧流程:

  1. 先给复现录屏:不要一上来就丢代码片段或Excel表格,先放一段Playwright的trace回放或录屏,让全场看到“这个分支真的能走到”。录屏十五秒的效果,胜过口头辩论半小时。
  2. 再给最小用例:把问题简化成最快的复现路径,最好是一段能从浏览器控制台或Tessy单独跑的用例。开发不需要理解你的整个测试框架,他只需要在自己的环境里跑一遍就能看到现象。
  3. 最后给影响分析:说清楚“现在不修,什么情况下会爆,爆了影响多大”。这里就用上Excel用例表里的“风险标记”列,把高、中、低风险各列成一个清单,让业务方在知情的前提下表态。

缺陷标题也有讲究。我见过写成“某某页面报错”的缺陷单,开发看半天不知道要干什么。合格写法最好是一个公式:[模块] + [条件] + [实际表现] + [期望表现]。比如:“[状态机] 当资源类型为9时,鉴权返回机密等级,预期应拒绝访问”。这种标题信息量足够,开发一看就知道问题的性质。

5.3 从用例到知识库:考古结束时留些什么

考古项目收尾时,除了缺陷单和测试报告,我会额外做一件长期有用的事:把整个探测过程整理成一份“系统地层图”文档,沉淀到团队知识库。这份文档不是测试报告,里面写的不是“本次发现了几个bug”,而是“这个系统有哪些地方是活的、有哪些是历史遗留的、哪些是刻意绕开的”。

具体内容包括三层:第一层是功能入口实测清单,标注哪些入口是文档没写的;第二层是模块级的地示意图,列出每个C函数里“永远不可达”的分支和废弃结构体;第三层是“已确认但暂缓处理”的历史隐患清单,每条都要有决策时间、决策人、风险描述。做个十几分钟就能把这份文档做完,但它对后来接手的人价值极大。很多新人在老系统面前手足无措,不是因为技术不够,而是因为没有这份“地层图”,每踩一种土都得从头猜。

后来我养成了一个习惯:每次写完一个测试用例,都会问自己一个问题——“如果这个用例失败了,它能告诉我到底是哪个环节出了问题吗?”能回答的,才配叫测试用例;回答不了的,它只是一个占位符。考古探针的本质,不在于你戳了多少个点,而在于每个点戳下去之后,能不能精准定位到那层真正有问题的地方。QA这个岗位不太会出“孤胆英雄”,但我们手里有探针,有证据,有把问题摊开来说的底气。这套东西用熟了,哪怕眼前的代码再烂、文档再稀烂,你也总能找到第一个可以撬动的地方。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询