☰
软件测试实战:从用例设计到自动化与缺陷管理的完整指南
2026/10/11 7:48:47 网站建设 项目流程

1. 测试不是写用例,是在跟"理所当然"作对

我做了这么多年测试,最深的感受是:一份测试用例值不值钱,不看数量,看它有没有击穿那些"理所当然"的假设。

举个最简单的例子。一个登录功能,需求文档写着"用户输入正确账号密码后进入首页"。看起来没毛病吧?但细想一层:什么算"正确账号"?带空格算吗?大小写混着算吗?密码框里粘进去一段带换行符的字符串算吗?验证码区分不区分大小写?连续输错五次后账号锁不锁?锁了之后是提示"账号锁定"还是提示"密码错误"?——就这么一个平平无奇的登录框,往下拆能拆出三四十条用例,而且每条都是真实会出事的场景。

很多刚入行的测试同学喜欢拿着需求文档一条一条对着写用例,写完还觉得自己覆盖率挺高。但实际上,需求文档描述的是"设计意图",而线上跑的代码是"对设计意图的一种具体解释"。这两者之间永远存在缝隙,测试要做的就是去凿开这些缝隙,看看里面藏了什么。需求说"支持上传图片",那你得问:支持什么格式?WebP支持吗?超过2MB是压缩还是拒绝?上传中断了怎么处理?传错格式报错提示文案是谁定义的?这些需求文档里基本都不会写,但它就是上线的第一道坎。

所以我一直坚持一个原则:写用例之前,先把需求里每个动词都圈出来,然后挨个问"如果这个动作的输入不合法怎么办""如果中间状态断掉了怎么办""如果用户连续重复这个动作怎么办"。这套追问做完,用例的大致框架就出来了,根本不需要硬凑。

另外一个容易被忽略的事:用例设计不是一个人的事。开发写完接口,跟测试对一下边界定义;产品把交互稿给出来,测试先拿交互稿走一遍用户路径。这些动作看着不起眼,但它能省掉后面大量因为理解偏差导致的返工。你一个人闷头憋出来的用例,和生产、开发、产品一起过一遍之后完全不是同一个东西。

1.1 需求文档里的需求,其实都是"理想状态"

需求文档默认了一个前提:用户会按照文档里描述的顺序操作。但实际上,真实用户的操作路径是杂乱的。他会先点这里再退回去,会在一半的时候刷新页面,会开两个浏览器窗口同时操作同一个账号,会在弱网环境下疯狂点击提交按钮。

这些"异常但真实"的路径,往往才是上线后客诉的大头。

我做过的项目里,有个很典型的翻车案例:一个电商下单页,开发做了提交订单的防重复设计——点击按钮后置灰并显示"处理中"。听起来没问题。但用户在下单页停留了很久,页面token过期了,他点提交,按钮置灰,请求发出去返回登录失效,前端没做跳转处理,按钮保持置灰不动了。用户以为订单提交了,其实什么都没发生,他等了两分钟没反应,又刷新,发现购物车还在,于是他再提交一次,结果这次成功了——但他前面那次以为成功却没成功的体验,已经把信任感消耗完了。

这个case里,产品文档写的流程是"点击提交→生成订单→跳转收银台";真实世界里多了一个"token过期"的分支。哪个环节负责处理?没人规定。但测试如果能把这种场景写进用例里,上线前就能拦住,而不是让第一个用户来当这个测试员。

所以我现在带团队,第一课永远不是讲工具怎么用,而是讲"如何把需求当谎言来审"。不是抬杠,而是用测试的视角,把每一个理所当然的流程拆出岔路。这个思路捋清楚了,用例设计就不愁没内容,也不愁没价值。

1.2 从功能清单到场景地图:用例结构的一次升级

传统的用例设计是"功能点×规则组合",一个模块一个模块地列。这在系统简单的时候够用,但系统一旦复杂起来,比如涉及多角色、多状态流转、外部依赖一堆,这种做法就会出现一个致命问题:用例之间是孤立的,没法覆盖跨模块的场景。

我后来换了一种方式,叫"场景地图"。

具体做法是:先不急着写用例,而是把核心用户路径画出来。比如一个订单从创建到完成,中间经历哪些状态(待支付、已支付、处理中、已发货、已完成、已取消),哪些角色会参与(用户、运营、仓库、财务),哪些触发器会导致状态变化(用户付款、运营手动取消、仓库发货、系统超时自动确认收货)。把这些关系理清楚,场景地图就出来了,然后每一个状态流转、每一个角色的操作边界、每一个触发器的异常分支,就是一个个用例组。

这种做法的好处是:你测的不是"某个页面正不正常",而是"整条链路通不通"。因为你手里拿的是一张地图,走哪条路会经过哪些关卡,哪条路被堵死了影响的是哪些场景——一眼就能看出来。

而且这种场景地图对回归测试很有用。版本迭代时,开发说"我只改了一个字段",你可以拿地图去比划,这个字段挂在哪个状态上,影响哪条路径,回归范围一下子就锁定住了。省下来的时间非常可观。

2. 探索性测试:把手动测试从"体力活"变回"脑力活"

有自动化测试之后,不少人觉得手工测试要失业了。这个说法,我只能说一半对。大量的重复性回归确实会被自动化顶掉,但有一种测试是自动化替代不了的,那就是探索性测试。

探索性测试是什么?简单说,就是不预先写死用例,而是带着一个目标和一份好奇心,边测边学、边学边测。你打开一个页面,看到某个按钮,心里冒出"如果我现在双击会怎样""如果我清掉cookie再点会怎样""如果我从别的入口跳过来直接点这个按钮会怎样"——这些问题不是用例文档里写的,而是你在实际操作过程中自然产生的疑问。然后你顺着疑问去操作,发现一个bug,这就是探索性测试的价值。

自动化没法做这件事,因为代码不会"好奇"。自动化脚本只会严格地按你写的步骤走,它的每一步都是确定的,它永远不会去想"如果我换个顺序呢"。而这恰恰是探索性测试的核心能力:在不确定中寻找系统行为的破绽。

我自己每次拿到一个迭代版本,都会先留出一天时间专门做探索性测试,不看用例,不带脚本,就在真实环境里像用户一样用它。这个过程里,我经常能找出一些自动化脚本和常规用例完全覆盖不到的bug。

当然,探索性测试最被人诟病的一点是"不可复现""不可衡量"。为了解决这个问题,我给团队定了一个简单的规则:探索性测试期间,一旦发现可疑行为,立刻记录操作路径(用screen recording或者每一步截图),并标注当时的测试数据和系统状态。这样即使你当时没空完整提单,后面也能根据记录把bug复现出来。这套规则不做,探索出来的bug会白白丢掉,非常可惜。

2.1 探索性测试的几招实用打法

探索性测试听起来玄,但它是有方法论的。我这里说几个我自己用得最多、也最见效的打法:

  • 打断流程:正常流程做到一半,强制刷新页面、杀掉进程、断网重连、退出重进。重点看系统是能恢复原状态,还是直接进入不可预料的脏状态。E.g. 一个表单填到一半,刷新之后草稿还在不在?这个在很多产品里都有问题。

  • 逆向操作:先做"不该做"的操作。比如直接改URL上的参数、在搜索框里输入超长字符串、在接口层传入被篡改的数据。这其实是在替黑客和捣蛋用户提前找系统弱点。

  • 多路径汇合:从前一个页面用不同的方式进入同一个目标页。比如从列表点进去、从搜索跳进去、从消息通知点进去——看起来目标页一样,但可能因为带了不同的参数,渲染出来是bug和bug之间的区别。

  • 状态叠加:某个操作正常,某个操作也正常,但两个状态叠加在一起就容易出问题。比如"登录状态下修改头像"没问题,"登录状态下连续修改三次头像"也没问题,"登录状态下修改头像后立刻刷新个人信息页"可能就有问题。状态叠加是探索性测试最容易抓bug的点,也是最接近真实用户行为的点。

这些打法不需要复杂的工具,一个浏览器、一个抓包工具、一个录音软件就够了,但效果非常好。建议每个测试同学在面对大版本的迭代时,都专门安排一个下午做这种探索性测试。注意,是专门安排,不是做完回归以后顺手看看——因为你一旦有"顺手看看"的心态,就只会走正常流程,不会去制造异常了。

2.2 探索性测试的记录与交接

探索性测试有个很现实的问题:你测出来的bug是记在脑子里的,不写下来,下次就被忘了。而且如果你请假或者离职,你脑子的这些信息就彻底没了。

我的做法是:探索性测试期间,每发现一个可疑行为,就当场截屏并加一行文字描述,然后丢到一个公共的"探索性测试发现"文档里。每天结束前花十五分钟整理一下这个文档,把可疑行为分成三类:可立即重现的(直接提单)、偶发的(标注触发条件和时间)、未复现的(记录操作路径,标注待跟踪)。这样一来,探索性测试的产出就变得可追踪了,不像以前一样测完就散。

另外还有个小技巧:探索性测试的环境尽可能用生产环境的副本数据,而不是一套干干净净的测试数据。因为真实数据的复杂度远超制造的数据,各种边界值、脏数据混在一起,更容易触发问题。我们经常说"测试环境都好的,一上生产就出问题",很大程度上就是因为测试环境的数据太"干净",跟真实世界脱节。用一套复制出来的生产数据跑探索性测试,很多问题提前就暴露了。

3. 自动化测试的投入产出边界:什么值得测,什么不值得

聊自动化测试,我见过两种极端:一种是完全不自动化,所有回归都靠手工点,发一次版全家跟着熬夜;另一种是看到什么都想自动化,连文案改了都要写个断言,结果用例维护成本比收益还高。

这两种情况我都经历过,最后摸索出来的经验是:自动化测试不是越多越好,而是越"稳"越好。

任何一条自动化用例,都有三笔成本要算:脚本开发成本、脚本执行成本、脚本维护成本。很多团队只算了第一笔,觉得写个脚本花不了多少时间,然后等第二笔、第三笔账单来了才傻眼。

维护成本是最大的一头。UI自动化尤其明显,前端一个class名改了,一个DOM结构调整了,你的定位器就废了。如果产品还在快速迭代期,UI天天在变,你花大力气写的UI自动化简直是在给前端打工——今天修好明天挂。所以我在快速迭代的项目里,不太建议一上来就铺大面积的UI自动化,而是先把手动回归的流程捋清楚,把稳定不变的、高频重复的、对业务安全影响大的部分先自动化起来,其他部分先忍一忍。

3.1 什么样的测试最适合自动化

按照我的经验,自动化测试的优先级是这样排的,从高到低:

  • 核心业务链路:登录、下单、支付、主要状态流转。这些链路一断就是大事故,必须保证每轮版本都能快速回归。
  • 高重复度的数据校验:比如一个接口的字段校验规则,前端的、后端的加在一起可能有上百条,每轮都手动点一遍,谁都会疯。这种适合用接口自动化跑。
  • 配置类/逻辑类校验:优惠券计算、价格计算、分润计算这些规则类功能,逻辑一旦确定就不太会变,非常适合自动化,而且要会做参数化,把各种边界值都丢进去。
  • 跨系统的接口契约:你系统和依赖方之间改字段名、加参数、改类型,这些靠手动测很难覆盖全(因为很多是边界场景),用接口自动化做契约测试性价比极高。

反过来,什么样的情况不建议自动化:UI文案的拼写检查(人工一眼就能看完,写了一堆脚本反而每次改文案都要改脚本)、上线的临时验收(这种场景跑一把主流程就完了,没必要做成长期用例)、以及环境极不稳定的项目——环境不稳定时自动化脚本跑出来的失败有一半是环境问题,你会花大量时间在甄别失败原因是真bug还是测试环境抽风,非常消磨信心。

3.2 自动化用例稳定的核心:不是怎么定位,而是怎么设计

很多人自动化用例不稳定,第一反应是"换个更强的定位方式",什么xpath用得更精细、加一堆expected conditions。这些是表面功夫,真正能解决稳定性问题的,是用例本身的设计。

我给你举个例子。你写一个"登录后首页展示用户名"的UI用例,如果直接把断言写成"页面上出现'张三'"——那么只要产品把用户名挪个位置,改个样式,你的用例就挂了。但如果你把断言写成"页面右上角出现包含用户名的文本",并且用相对稳定的容器去定位,这个用例在产品改版后就还能活着。稳定性的本质,是让你的用例对"不重要的变化"不敏感,对"重要的变化"敏感。

具体来说,我觉得有这几条设计原则:

  • 面向用户行为写用例,而不是面向页面结构写用例。页面结构天天变,用户行为变化很慢。你写的是"用户能成功登录",不是"用户在input#username里面输入文本"。
  • 用数据隔离环境。用例跑之前先造好一份稳定可用的测试数据,不要依赖执行顺序——A用例跑完了,B用例还能不能跑,不取决于A。如果两个用例共享同一份数据,互相影响,那你迟早会被偶发失败逼疯。
  • 失败时要能快速定性。脚本失败,要么是产品出bug了,要么是测试数据变了,要么是脚本本身的问题。好用例应该做到:任何一次失败,日志里都能直接看到是哪一类,以及相关的截图和上下文。我见过太多团队,脚本红了之后要人工去翻半天日志才能搞明白为什么红,这个成本比脚本本身高多了。

3.3 断言的艺术:测什么,不测什么

断言是自动化用例里争议最多的地方。断言多了,稍微有点非核心的变化就误报;断言少了,真出问题又发现不了。我的经验是:断言业务结果,不断言实现细节。

什么叫业务结果?用户下单成功后,订单列表里多了一条状态为"待支付"的订单,这就是业务结果。什么叫实现细节?订单列表里的某个元素用了某个特定的css class,这就是实现细节。断言业务结果,用例在金丝雀变化中依然稳;断言实现细节,人家前端稍微换个类名,你的用例就红了,但用户其实毫无感知。

另外,断言也不是越多越好。每个用例,我的习惯是抓"最能说明问题的那一两个结果"来断言。登录场景,断言跳转后的页面URL和页面上出现用户名就够了,不必再断言十来个页面上次要元素的状态。逻辑简单一点,维护成本就低一点,稳定性也好很多。

4. 缺陷管理里最常见的几个误会

我在评审缺陷单的时候,经常看到两类极端情况:一种是一句话带过,"页面报错"——然后没了,开发拿到手想复现都不知道从哪下手;另一种是洋洋洒洒两三千字,从背景写到排查过程写到猜测的代码位置,但关键信息(版本号、操作步骤、环境、数据)却给了个大概。

缺陷单的目的,是让一个完全不了解你当时操作的开发,能按照你记录的信息,快速、稳定地复现问题。所有不服务于这个目的的信息,都是噪音;所有服务于这个目的的信息,少一条都不行。

4.1 一条好缺陷单的标准模板

我自己用下来比较顺手的模板是这样的:

  • 标题:一句话说清楚"在什么环境下、做什么操作、出现什么现象"。比如"iOS 17.4环境下,订单详情页点击'申请退款'后页面白屏"。这个标题写好了,开发看列表就能判断优先级,不用一条条点进去。 注意不要写成"退款功能挂了"这种模糊描述,也不要写"【BUG】订单详情页申请退款白屏"——【BUG】这种前缀没有信息量。

  • 环境信息:操作系统、浏览器/客户端版本、后端分支/版本号、测试数据ID。这些信息不是每一条都跟问题相关,但是一旦要排查,没有就是无头公案。

  • 操作步骤:每一步都要具体到"点击哪个按钮""输入什么内容""在哪个页面停留多久"。如果有前置条件(比如必须先登录、必须先有某张订单),一定要在步骤里写清楚。

  • 实际结果 vs 预期结果:这两栏分开写,而且都要写。很多人只写"实际结果报错",预想结果不写。预期结果的作用,是让开发和产品快速对齐"这个行为到底算不算bug",如果预期结果写得含糊,光这个判断就能来回拉扯好几轮。

  • 附加材料:截图、录屏、抓包日志、console报错。这些不是可选项,是必须项。至少要有截图。截图能说明的是"表象",日志能说明的是"原因",两个都有,开发排查效率能提高一大截。

我见过很多团队,缺陷单模板定了不少字段,但大家实际填的时候就随便填,因为字段太多反而没人愿意写。所以我建议反着来:模板不要搞一堆字段,就保留上面这几行核心,其余的按需补充。字段越少,大家填写的意愿越高,信息的质量反而更好。

4.2 优先级之争:本质是风险偏好,不是技术问题

工作中最容易吵起来的不是bug本身,而是bug的优先级。测试觉得"这个bug很严重,必须修复后才能上线",开发觉得"影响面很小,可以先上后补"。这种争论的本质,其实是双方对风险的容忍度不一样,而不是谁对谁错。

我的处理方式是这样的:判定优先级之前,先回答三个问题——影响范围多大(影响一个用户还是一类用户)、触发条件多苛刻(需要特定数据、特定路径还是随便操作就触发)、有没有绕过方案(用户侧可以手动绕开还是直接卡死)。这三个问题答完,优先级基本就达成了共识,不需要拍桌子。

另外一个容易被忽略的点:有时候一个bug本身不严重,但它掩盖了更深层次的问题。比如某个字段偶尔显示错乱,看起来是个展示问题,但顺着查下去可能是数据在写入时就错了,源头在某个上游服务,影响的场景远比现在看到的要多。所以遇到"偶发""特定条件下"这类描述,别急着降优先级,多问一句"为什么偶尔才出现",很可能问出一个隐藏的地雷。

4.3 缺陷生命周期管理:别让bug烂在"待处理"里

很多缺陷单死在"待处理"状态里,一放就是几个迭代。不是说开发故意拖着,而是bug实在太多,没人做筛选和分流,最后有专人去翻,才发现里面既有真bug也有需求变更也有环境问题,乱七八糟的。

我的做法是每周固定花一个小时做一次缺陷库的"整理"。怎么整理?按状态分三堆:

  • 确认是bug、影响明确、在本次迭代范围内的:立刻跟进,标记优先级,推动解决;
  • 确认是bug、但优先级不高,短期不修的:跟产品对一下,明确"已知问题"放回 backlog,并备注不修的原因;
  • 确认不是bug或者是环境问题/操作误报的:直接关闭,备注原因,不要留在列表里浪费大家注意力。

不要小看这个动作。缺陷库一旦积累了太多"僵尸单",团队成员会形成一种潜意识——"反正单子也不看,写详细点也没用",然后缺陷质量继续下滑,恶性循环。定期整理,是维持缺陷管理信号价值的关键,它告诉所有人:每一个bug都会被认真对待。

5. 一个可落地的小型测试流程:从需求评审到上线验收

光说理论,不够。我拿一个典型的"小型敏捷团队"(比如十个人以内,开发三四个,产品一个,测试一个)来搭一套实际可执行的测试流程。这个流程不用花里胡哨的工具体系,就靠一套简单的规范,足够保证发版质量。

5.1 需求阶段的测试介入

很多团队里,测试是"等开发提测了才开始干活"的。这个模式在我看来非常低效——因为你前面损失的两周时间,后面全靠加班补。

正确的姿势是需求评审的时候,测试就要在场。但注意,测试在会上不是去听热闹的,你的任务是带上三个问题:

  • 这个需求的用户场景是什么?大致的用户路径有几条?
  • 哪些是主路径、哪些是异常路径?
  • 数据从哪来、存到哪去、接口是谁提供?

需求评审会结束之后,测试要做的事是:写一份"测试要点清单"。注意,不是完整用例,是清单。把核心场景、边界情况、待确认的问题列出来。这份清单会在测试阶段直接转化成用例,也会反过来提醒产品和开发——你们有些设计还没想清楚,趁开发阶段前赶紧定。

这一步做好了,后面所有阶段都顺。因为没有谁愿意在开发做到一半的时候,你突然问"如果用户是港澳台手机号怎么办",然后发现产品压根没考虑过这个分支。

5.2 开发阶段的灰盒准备

开发还在写代码的时候,测试也不是闲着。我最推荐做的是"接口契约梳理"——把本次迭代涉及的所有接口列出来,搞清楚每个接口的入参、出参、异常码、权限控制。这个信息非常重要,因为你后续写接口自动化用例、写异常分支用例,全靠它。

另一个值得做的是"测试数据准备"。提前把各种边界值、特殊字符、超长文本、空值、脏数据准备出来。等到测试阶段再临时造数据,不但浪费时间,还会让你因为"数据造不出来"而跳过某些边界场景的测试——这个坑我踩过太多次了。

5.3 提测阶段的准入条件

没有准入门槛的提测,是测试团队的灾难。开发说"好了,测吧",结果你上去一点,首屏都打不开,或者主流程全断,你是在帮开发做冒烟测试,效率为零。

所以我强烈建议设一个"冒烟测试门槛":开发提测之前,必须先自己跑一遍主流程,确认没有明显阻塞性问题,再正式提交给测试。具体的准入门槛不用太复杂:主流程能走通、没有Crash级别的问题、关键接口不报5xx。满足这几条就可以进入正式测试,不满足的直接打回去。

这个门槛不是说测试在甩锅,而是把"检查自己代码能不能跑通"这件最基本的事还给开发。双向确认,大家都轻松。

5.4 测试执行阶段的节奏感

测试执行阶段,最容易出的问题是"时间全花在细枝末节上"。我给自己定了一条规矩:先测主路径,再测异常路径,最后才是体验类问题。

主路径的优先级最高,因为它一挂,整个迭代的交付就是0。主路径测完了,这轮版本最核心的部分已经保住了,即使后面时间不够,也不会出现"主要功能不能用"的情况。异常路径其次,因为很多安全问题、数据不一致问题都在这里面。体验类问题(按钮对齐、文案措辞、加载动效)放最后,不是说它不重要,而是它不应该占用主路径和异常路径的时间。很多测试同学容易被视觉上的小瑕疵带偏,花一个下午扣一个tab切换的细节,结果回过头来主流程的用例还没跑完——这个习惯一定要改。

5.5 上线之后的验收与复盘

上线不等于结束。上线后的第一个小时,测试要做的是"线上冒烟"——在生产环境把核心用户路径快速走一遍,尤其是本次迭代涉及到的部分。为什么?因为测试环境再像生产,也总会有配置、数据、权限的差异,有些问题只会在生产环境出现。

然后就是复盘。每次迭代结束,我会拉着开发、产品一起做一个小复盘,就看三件事:

  • 漏测的bug里,最典型的那一两类是什么原因漏的?(是场景没覆盖到,还是数据没匹配,还是需求理解偏差?)
  • 整个流程里最耽误时间的是哪一环?(是等待提测太久,还是环境不稳定,还是缺陷来回踢皮球?)
  • 下一迭代能不能把一个具体问题改掉?(不要列十条改进,列一条,真的改掉)

复盘最忌讳的是变成批斗会。我的经验是明确"只谈流程、不谈人",bug漏了,先看流程哪里漏了,而不是"谁当时没测到位"。氛围对了,改进才能落地。

5.6 质量度量:看数据,但别迷信数据

最后聊一下质量度量。很多团队喜欢立一堆指标:用例执行率、缺陷率、缺陷解决时长、用例自动化率……这些指标有用,但一定要知道它们各自的局限。

用例执行率100%不代表质量好,如果你的用例本身就是按照需求文档写的,没覆盖到任何真实场景,执行完了也说明不了什么。缺陷率低也不代表质量好,有可能是你的测试压根没测出东西来。自动化率同理,100%的自动化覆盖率如果维护得稀烂,跑出来的结果也没人信,那这个指标就是纯数字。

我自己比较看重的指标就两个:一个是线上故障率(直接反映质量结果),一个是漏测率(测试执行后,线上还出现的bug数/测试阶段发现的总bug数)。这两者能相对真实地反映测试对质量的贡献。其余指标,比如"用例执行率",可以作为过程参考,但别拿来考核人——真的,一考核人,就会有人刷指标。

数据是用来看趋势的,不是用来证明"我很努力"的。连续几个迭代观察同一个指标的变化,比单看某一次迭代的数据有意义得多。

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

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

立即咨询