
1. 先把功能测试的边界划清楚功能测试这四个字听起来像是测试里最没门槛的一块——点一点、填一填、看看结果对不对谁不会但我带过的几批新人里真正能把功能测试做出深度的往往不到三成。大部分人卡在同一个地方把功能测试理解成照着需求文档走一遍流程走完就打勾收工结果上线之后用户反馈的问题十有八九还是出在他们走流程时根本没注意到的角落里。先说清楚它到底在干什么。功能测试的核心目标只有一个验证系统在给定的输入和前置条件下是否产生了符合预期的输出和状态变化。这句话里藏了三个容易被忽略的关键词——前置条件、状态变化、符合预期。新手通常只盯着输出也就是页面上弹出来的提示或者列表里多出来的那行数据而忽略了数据库里的字段、缓存里的键值、消息队列里的消息、关联表的记录这些状态变化也懒得去认真构造前置条件直接拿现成的测试账号一顿乱点。这就是差距的起点。功能测试和单元测试、集成测试、性能测试、兼容性测试之间的边界很多人也是模糊的。单元测试盯的是一个函数或者一个类的逻辑正确性集成测试盯的是模块之间接口的连通性性能测试盯的是响应时间和吞吐量兼容性测试盯的是不同环境下的表现差异。而功能测试盯的是业务视角下的行为正确性——用户点了这个按钮从业务规则上讲系统应该发生什么实际发生了什么两者是否一致。它跟实现方式无关跟技术栈无关甚至跟界面也无关。一个纯后端的接口只提供 HTTP 调用同样可以做完整的功能测试这一点在做微服务项目的时候体现得特别明显。我见过一个典型场景某电商系统的优惠券模块测试人员把领取优惠券的功能测得很细领到了、重复领取被拦截、库存为 0 时提示、过期券不能领全都过了。上线当天就炸了——因为没人验证领取一张满减券之后这张券在订单结算页的可用券列表里是否正确出现并且使用后金额是否按规则扣减。领券功能本身没错错在跨模块的状态流转没有被覆盖。这就是功能测试里最容易漏的一类问题单个功能点都对组合起来不对。所以真正做功能测试的人脑子里装的不是一个个孤立的用例而是一张业务流程的网。每动一个功能点都要想清楚它上游依赖什么、下游影响什么、并发情况下会怎样、失败回滚时会怎样。这个思维方式比会多少工具都重要。1.1 功能测试的价值到底体现在哪有人会问现在自动化这么发达功能测试是不是快被淘汰了我的判断正好相反。自动化擅长的是已经想清楚的、稳定的、重复执行的检查它不擅长判断这个结果从业务上讲到底对不对。比如一个订单状态从待支付变成了已完成自动化脚本只能断言状态字段的值但它没法告诉你这个流转本身是否违规——有没有可能跳过了已发货这个必经状态有没有可能在未支付的情况下直接完成了这类业务合理性的判断必须由人来定义定义完之后才轮到自动化去执行。功能测试的另一层价值是探索性发现。你在手动操作的过程中会注意到一些奇怪的现象比如某个按钮点击后页面闪了一下、某个列表加载比别人慢半拍、某个字段在边界值下显示成了乱码。这些现象在脚本里是没有断言的但往往就是线上事故的前兆。我带过一个实习生他在测一个导入功能时随手试了一下 Excel 里带换行符的单元格结果发现导入后数据全部错位——这个问题后来被开发确认是老代码里解析逻辑的缺陷影响了几万条历史数据的导入。他说他就是点了点觉得不对劲这种直觉恰恰是功能测试最不可替代的部分。还有一点功能测试是需求质量的最后一道过滤器。很多需求文档写得含糊比如用户可以管理自己的资料什么叫管理是只能改昵称头像还是能改手机号邮箱改了手机号要不要重新验证改了之后已登录的其他设备要不要强制退出这些问题在需求评审时如果没人追问开发就会按自己的理解实现测试也只能按自己的理解验证最后线上就会出现我以为是这样的你以为是那样的。功能测试人员在写用例的过程中实际上是在替整个团队把需求的模糊地带逐条钉死。1.2 一个完整的测试对象拆解思路拿到一个功能模块我习惯先画三张清单不是画图就是列出来写在文档里。第一张是输入清单这个功能接收哪些输入表单字段、URL 参数、请求头、上传的文件、用户的操作顺序、外部接口返回的数据全都算。每个输入要标注类型、长度限制、是否必填、取值范围、来源是否可信。这一步做完边界值用例基本就自动浮出来了。第二张是状态清单这个功能执行前后哪些数据会发生变化主表、关联表、日志表、缓存、消息、第三方同步的数据都要列出来。很多人的测试之所以浅就是因为只看主表不看关联表。我曾经查过一个财务对账差异的问题最后发现是订单主表金额改了但明细表的汇总金额没跟着改测试的时候只看了列表页显示的总金额读的是主表没去看明细页的合计读的是明细表差异就这么漏过去了。第三张是角色与权限清单同一个功能不同身份的人看到的、能做的有什么差异超管、普通管理员、普通用户、未登录用户、被拉黑的用户每种身份都要走一遍。权限问题是最容易出大事故的因为它不影响正常用户的体验只影响那一小撮不该看到却看到了的场景而这类问题一旦被发现性质往往比功能 bug 严重得多。这三张清单看似繁琐但真的做完一次你会发现后面写用例的速度快了一倍因为你不再是凭记忆和直觉在猜而是有清单兜底了。1.3 别把功能测试做成点点点我特别反感一个说法叫点点点工程师。这个说法的流行其实反映了行业里对功能测试的轻视也反映了一部分测试人员自己确实做得太浅。同样是点一个按钮差别可以很大。浅的做法是点下去看有没有弹提示弹出来了就过。深一点的做法是点下去之前先记录当前数据状态点下去之后对比数据变化是否符合预期同时去后端看日志有没有报错去数据库看事务有没有正常提交去缓存看有没有被正确清除再去检查一下这个操作有没有触发应该触发的异步任务。再深一点你还会去关注这次操作的耗时、有没有重复提交的风险、网络中断时会不会产生脏数据、并发情况下库存会不会超卖、接口有没有做幂等。这三层做下来同样是点一下工作量差了好几倍但发现问题的能力也差了好几倍。我一直觉得功能测试功夫的高低不在于你用了多高级的工具而在于你愿意在设计一个用例时花多少心思去想还有什么可能出错。2. 用例设计功能测试真正的技术活写用例这件事我在团队里推过一个观点用例不是写给人看的文档是写给自己未来执行时用的备忘录。很多人写用例只是为了应付评审标题写得高大上步骤却含糊其辞过两周自己都看不懂。这种用例做出来就是废纸。真正有价值的用例应该满足几个条件不看需求文档也能照着执行、执行完能明确判断通过还是失败、能看出这个用例在验证什么业务规则。如果用例里出现检查数据是否正确这种话基本可以判定这条用例是失败的因为正确是什么没写清楚。2.1 最常用的几种用例设计方法及取舍等价类划分和边界值分析是最基础的两把刀几乎所有的输入类用例都靠它们。等价类是把输入空间切成若干组逻辑上等价的集合每一组里取一个代表来验证即可。比如一个年龄输入框合法区间是 18 到 60那有效等价类就是 18 到 60 中的任意值无效等价类就是小于 18 和大于 60。但等价类只取一个值是不够的必须配合边界值17、18、59、60、61这几个点才是真正容易出问题的地方。我见过太多人只测 30 岁这种安全的中间值上线之后 60 岁的人注册失败。原因很常见代码里写的是if age 60而不是或者前端限制写的 59后端写的 61两边不一致。判定表适合处理多个条件组合决定不同结果的场景。比如一个转账功能要看是否实名是否超过单笔限额是否超过当日累计限额是否在允许的时间段内四个条件组合起来有十几种情况每个情况对应的拦截提示和处理逻辑都不一样。这种时候凭脑子想一定会漏老老实实做一张判定表把所有组合列出来再划掉不可能出现的组合剩下的就是必须覆盖的用例。这张表的价值在于它把组合爆炸变成了可枚举而且评审的时候开发一眼就能看出你覆盖到没覆盖到。正交实验法在参数多、组合多、时间紧的情况下特别有用。比如一个商品搜索页面有分类、价格区间、发货地、排序方式、是否包邮五个筛选维度每个维度有若干取值全组合是几百种根本测不完。用正交表挑出一小部分代表性组合能覆盖到绝大部分两两组合投入产出比很高。但我要提醒一句正交法只保证组合覆盖不保证具体业务规则覆盖如果某个筛选维度的边界逻辑本身很复杂还是得单独补用例。场景法是我个人最推荐的。它不关注单个输入而是关注一条完整的用户路径。举个例子用户下单这条路径包含了浏览商品、加入购物车、选择地址、使用优惠、选择支付方式、提交订单、支付、查看订单状态等一连串动作。场景法要求你把这条路径的基本流一切顺利和备选流中途出现各种异常都写出来。基本流通常只有一两条备选流能写十几条库存不足、优惠券失效、地址被删、支付超时、支付失败、重复提交、网络中断。真正的高价值 bug八成藏在这些备选流里。错误推测法听起来最不科学但实际工作中用得非常频繁。它依赖的是经验——你知道这种代码通常会在哪里翻车。比如看到导入 Excel就想到空行、合并单元格、超长文本、特殊字符、日期格式不一致看到金额计算就想到浮点精度、四舍五入规则、分转元、负数、零值看到分页就想到最后一页、页码超出、删除后页码回退。把这些常见坑整理成一份自己的清单每次设计用例时过一遍效率极高。状态迁移法适合有明确状态机的功能比如订单、工单、审批流。要点是画出状态图然后针对每个状态检查三类迁移合法的迁移能不能正常走通、非法的迁移能不能被正确拦截、边界状态下能不能正确触发超时或自动流转。订单从待付款能不能直接跳到已退款已完成的订单能不能再次申请取消这些越权流转的问题是状态机类功能最常见的漏洞。2.2 用例的颗粒度怎么把握这是新人最常问的问题一条用例到底该写多细我的经验是按风险等级来定颗粒度。核心链路、涉及钱和权限的功能写到最细每一步的操作、每一个字段的取值都写清楚包括去数据库查哪张表哪个字段。非核心的展示类功能可以粗一些合并成一条用例走完。另一个判断标准是执行者是谁。如果这些用例要给不熟悉业务的人执行就必须写细如果是自己执行可以只写关键节点和验证点其他靠记忆。但我不建议完全依赖记忆因为人一忙起来两周后再回来看真的会忘。还有个反直觉的经验用例不要写太多。一个模块写三百条用例执行一遍要两天结果每次回归都没人愿意全跑最后只挑几十条跑剩下的全烂在文档里。我更喜欢把用例分成三层冒烟集十几条覆盖核心链路每次改动必跑、主流程集几十条覆盖主要业务规则每个版本必跑、全量集几百条覆盖所有边界和异常大版本或者重构时跑。这样分层之后用例才真的能被用起来而不是躺在那里当摆设。2.3 用例评审不是走过场用例评审我见过两种极端。一种是开发全程不说话测试念一遍就散会另一种是开发疯狂加需求把评审会开成了范围蔓延会。两种都不对。有效的评审应该聚焦三件事。第一是覆盖度开发看着用例能不能指出哪块逻辑没被覆盖到通常开发最清楚哪些代码是新写的、哪些是改动过的、哪些地方他自己也不确定这些信息对测试来说价值极高。第二是预期结果的准确性有些用例写的预期结果其实是测试人员自己臆想的开发一看就知道不对这种当场就能纠正避免执行时误报。第三是可测性某些需求在当前实现下无法验证比如没有日志、没有查询入口、状态不落库这时候要在评审时提出让开发补上观测手段而不是等到测试时才发现根本没法验。我个人的习惯是评审前把用例按业务模块分类标注出风险最高的那几条会上优先讨论这些。低风险的用例直接在文档里标注让大家异步看不占用会议时间。3. 执行、记录与缺陷管理用例写完只是准备阶段真正的功夫在执行的每一天里。功能测试的执行不是机械地照着步骤点而是带着判断力去观察、去比较、去怀疑。3.1 执行前的环境与数据准备我踩过的坑里有相当一部分跟环境有关。同一个用例在开发环境通过在测试环境失败到了预发环境又是另一个结果。原因通常是环境之间的配置不一致数据库版本不同、缓存没清、定时任务没跑、第三方接口是 mock 的、时区设置不一样。我的做法是在执行前花十分钟做一次环境自检。检查什么检查被测版本是否是最新的构建、检查数据库连接的是哪个库、检查关键配置项比如开关、限额、超时时间是否符合测试预期、检查有没有残留的脏数据会影响判定。这十分钟能省掉后面几个小时的困惑。测试数据同样关键。功能测试里有一类问题特别难查就是数据依赖。你测出来的结果不对追查半天最后发现是历史数据里有一条脏记录导致的。所以我在做重要功能测试时倾向于用干净的数据重新构造场景而不是在别人留下的烂摊子上测。构造数据的方法有几种最简单的是手工在界面上造慢但可靠效率高一点的是写 SQL 直接插快但要注意关联表的完整性和约束再高级一点的是准备数据脚本或者调用接口批量造适合需要大量数据的场景。这里给一个造订单数据的 SQL 思路实际项目里字段名肯定不一样看的是思路-- 先插主表拿到自增 ID INSERT INTO t_order (order_no, user_id, status, total_amount, create_time) VALUES (TEST20240501001, 10086, PENDING_PAY, 199.00, NOW()); SET oid LAST_INSERT_ID(); -- 再插明细表金额要和主表对得上 INSERT INTO t_order_item (order_id, sku_id, quantity, price) VALUES (oid, 2001, 1, 199.00); -- 如果还有状态流水表也要补一条初始记录否则部分页面读不出来 INSERT INTO t_order_log (order_id, from_status, to_status, op_time) VALUES (oid, NULL, PENDING_PAY, NOW());注意直接改数据库造数据一定要确认被测系统有没有缓存或者搜索索引。很多时候你插了数据界面上看不到就是因为缓存没刷新或者数据没同步到搜索服务里。这种情况不要急着报 bug先确认数据同步机制。3.2 缺陷报告怎么写才算合格缺陷报告是测试人员最重要的输出物之一。写得好的 bug开发看一眼就知道去哪查写得烂的 bug开发要来回问三轮最后还可能被驳回。我要求团队的缺陷报告必须包含这几个要素缺一个都算不合格。标题要一句话说清在哪、做什么、出了什么问题比如订单详情页在优惠券已过期时点击使用页面白屏比订单页面有问题强一百倍。环境要写清浏览器版本、系统版本、被测版本号。前置条件要写清楚需要什么账号、什么数据状态。复现步骤要精确到每一步点了什么、填了什么值。实际结果和期望结果要分开写不要混在一起。最后附上截图、录屏、请求报文和日志片段。这里面最容易被忽略的是日志和报文。很多人报 bug 只给一张截图开发只能靠猜。如果你能顺手把 F12 Network 面板里的请求和响应贴出来把后端返回的错误码和堆栈信息带上开发定位速度至少快一倍。这不是讨好开发而是缩短整个团队的修复周期。还有一个细节严重程度和优先级是两回事。严重程度描述的是问题本身对系统的影响比如数据丢失就是严重级别高优先级描述的是修复的紧迫性比如首页文案错别字严重程度很低但因为是用户第一眼看到的优先级可能很高。这两个字段分开填能避免很多扯皮。判断维度严重程度高优先级高说明数据丢失或错乱是通常是直接影响业务可信度核心流程阻断是是用户无法完成关键操作权限越界是是安全性质必须立刻修界面错位、文案错误否视位置而定首页和核心页优先级高极端边界下的异常提示不友好否低可排到后续版本3.3 回归测试的范围怎么定每次开发修完 bug 提交新版本都要面临一个灵魂拷问这次要回归多少全量跑一遍最保险但时间不允许只测改动的那一条风险太大。我的判断依据是改动影响面。具体看三件事这次改了哪些文件、这些文件被哪些功能调用、改动的逻辑是否涉及公共方法或者公共配置。如果改的是一个独立的工具类里的私有方法影响面很小回归相关的一两条用例就够了如果改的是一个被几十处调用的公共校验方法那必须扩大回归范围把所有依赖它的功能都过一遍。还有一个经验围绕本次修改的逆向场景要重点回归。开发修一个问题的时候很容易把原来正常的分支改坏。比如原本是金额大于 0 才允许提交改了之后变成金额大于等于 0那么金额为 0 的场景就要重新验证一遍看看是不是产生了新的漏洞。这种修复引入新问题的情况在快速迭代的项目里非常普遍。4. 常见问题与排查技巧实录功能测试做久了你会发现 bug 的类型其实是有规律的。下面这些是我这些年反复遇到的整理成速查表遇到类似现象时可以直接对照排查。4.1 高频疑难问题速查表现象常见根因排查方向页面数据不刷新浏览器缓存、接口缓存、CDN 缓存强刷、看响应头是否有缓存标识、看服务端是否返回旧数据偶现的提交失败并发冲突、重复提交、异步任务未完成看日志时间戳、看是否有唯一索引冲突、试着重放请求金额对不上浮点精度、四舍五入规则不一致、前后端各算一次对比前端显示值、接口返回值、数据库存储值三处时间显示差几小时时区处理不统一看服务端时区配置、看数据库存储是 UTC 还是本地时间列表分页数据重复或丢失排序字段不唯一、边排序边插入检查排序是否带上了主键作为兜底状态卡住不动异步任务失败、消息没消费、定时任务没触发看任务日志、看消息队列积压、手动触发一次特殊字符导致乱码字符集不一致、未做转义检查数据库字符集、检查接口编码声明权限能绕过前端隐藏但后端未校验直接调接口验证不要只看界面这张表我建议打印出来贴在工位上。新人遇到问题第一反应是这是不是 bug而有经验的人第一反应是这是哪一类问题方向对了排查速度差好几倍。4.2 金额和精度类问题的排查思路金额相关的 bug 是我见过最多、也最容易被误判的一类。典型场景是这样商品单价 19.9买 3 件页面显示 59.7但提交订单后接口返回的金额是 59.699999999999996最后支付的时候又变成了 59.69。测试人员一看就报 bug开发一看说这是浮点数正常现象双方扯半天。正确的做法是先明确项目对金额的处理规范。绝大多数正规项目会用整数分来存储和计算只在展示层转成元。如果是这样那 59.699999 这种值出现在接口返回值里就是设计缺陷应该报。如果项目确实用的是浮点存储那问题就变成了舍入规则不统一——前端用 toFixed(2)后端用 Math.round两者在 .5 的处理上可能不一样这种也要报因为它会导致页面显示的价格和实际扣款不一致属于严重问题。排查步骤我一般是这样第一步记录前端展示值第二步抓接口请求和响应看后端返回的原始值第三步直接查数据库看落库的值第四步看支付渠道返回的回调值。四个值放在一起对比差异出现在哪一层问题就在哪一层。注意涉及金额的用例永远不要在测试环境用看起来对来判断。必须把四个位置的值都取出来对比。差一分钱也是 bug因为线上可能放大成一万块钱的差异。4.3 偶现问题怎么才能复现偶现问题是最折磨人的因为报了出去没法稳定复现开发很容易回复无法复现先关闭。我的经验是处理偶现问题要收集三类信息。第一类是时间信息发生时间点、操作前后的时间戳、系统日志里的时间。很多偶现问题的根源是并发或者定时任务时间信息能帮你把范围缩小到某个时间窗口。第二类是环境信息用的是哪个节点、哪台机器、哪条数据、哪个账号。分布式系统里不同节点上的缓存和状态可能不一样落到哪个节点上结果就不同。第三类是数据信息操作涉及的所有数据的完整快照包括关联数据。收集完之后尝试用二分法定位。比如怀疑是并发导致那就用两个线程同时发请求跑一百次看能不能复现怀疑是数据状态导致那就把数据按各种可疑状态构造出来逐个试怀疑是时序问题那就调整操作之间的间隔时间从 0 秒到几秒逐个试。我印象最深的一次是某个列表页偶尔会出现重复行。开发说不可能SQL 里写得清清楚楚。我们最后发现是分页查询的排序字段只有创建时间而同一秒内创建的多条记录排序不确定翻页时数据库返回的顺序可能变化导致第二页出现第一页的记录。这种情况用单次点击永远复现不了必须快速连续翻页才有可能。找到根因之后修复方案也简单——排序字段后面加上主键 ID 做兜底。4.4 提交类功能的稳定性专项只要是提交类功能我都会做一轮专项测试因为这里出问题的影响最大。专项包含六项。一是重复提交。快速点击两次提交按钮、点击后按 F5 刷新、点击后按浏览器后退再提交、网络卡顿时连续发送。观察系统是否产生了多条记录。理想状态是后端用唯一请求号做幂等前端做按钮置灰两者都要有。二是网络中断。在提交过程中断网观察是提示失败还是卡死刷新后数据状态是否一致有没有产生半成品数据。三是超时。把接口响应时间拉长可以用限速或者让开发加个 sleep 开关观察前端的超时提示是否友好超时后用户重试会不会导致重复。四是并发。多个人同时提交同一份资源比如抢同一个名额、领同一张券观察是否超发。五是回滚。当提交涉及多个步骤时中间某步失败前面的步骤是否被回滚。这个最容易被忽略我见过太多主表写成功、明细表写失败的脏数据。六是异常输入。超长文本、特殊符号、emoji、纯空格、SQL 关键词、HTML 标签逐个塞进去看系统的反应。不是为了找注入漏洞而是为了看有没有因为没做转义导致页面崩溃。5. 提效工具与自动化的合理边界功能测试做到一定量级纯手工一定会遇到瓶颈。但不是所有手工工作都该自动化判断标准很简单这件事是不是稳定、重复、结果可断言。三个条件都满足就值得自动化缺一个就别浪费时间去写脚本。5.1 测试数据构造的几种手段数据构造是功能测试里的隐形工作量。一个复杂业务流程走一遍要造十几张表的数据手工造一次半小时一天下来全耗在这上面了。我的分层策略是这样的。低频使用、结构简单的数据手工造一次五分钟以内能搞定不值得写脚本。高频使用、结构固定的数据写成 SQL 脚本或者接口调用脚本参数化之后一行命令搞定。需要大批量、有规律的数据用脚本循环生成比如造一千个不同状态的订单。依赖外部服务的场景让开发提供 mock 开关直接返回预设数据。用接口造数据比直接写数据库更安全因为它会走完整的业务校验逻辑造出来的数据更干净。写一个简单的 Python 脚本就能搞定import requests BASE http://test-env.example.com/api def create_order(user_id, sku_id, qty): payload {userId: user_id, skuId: sku_id, quantity: qty} resp requests.post(f{BASE}/order/create, jsonpayload, timeout10) resp.raise_for_status() return resp.json()[data][orderNo] if __name__ __main__: for i in range(50): no create_order(10000 i, 2001, 1) print(fcreated: {no})注意造数据的脚本一定要能重复执行而不产生副作用或者至少能一键清理。我见过因为造数据脚本没做清理导致测试库积累了几十万条垃圾数据后来连查询都变慢了。5.2 接口层辅助验证很多功能点从界面上看不出真实状态但从接口层一看就清楚。所以我一直建议做功能测试的人学会用接口工具。不需要多高深会用 curl 或者 Postman 发请求、会看响应就够了。最典型的场景是验证前端做了限制但后端有没有做。比如某个按钮前端置灰了你没法点但你可以直接拿接口请求出来改一下参数重放一次看后端会不会拒绝。如果后端也拒绝了说明校验到位如果后端返回成功那就是一个权限越界问题。这类问题在界面上永远测不出来只有走接口才能发现。curl -X POST http://test-env.example.com/api/order/cancel \ -H Content-Type: application/json \ -H Authorization: Bearer token-A用户 \ -d {orderNo:ORDER_OF_USER_B}上面这条命令的意思是用 A 用户的凭证去取消 B 用户的订单。如果返回成功说明越权漏洞存在。这是功能测试里性价比极高的一类验证写起来只要几秒发现的问题却很关键。5.3 自动化该覆盖哪些功能用例自动化覆盖率是个很有迷惑性的指标。我见过团队追求 90% 的用例自动化率结果脚本天天红维护时间比手工执行还长最后大家集体不信任自动化结果。我的建议是自动化只覆盖三类。第一类是冒烟集也就是每次构建后必须验证的核心链路跑得快、结论明确是自动化的最佳候选。第二类是数据校验类就是那些需要反复查数据库确认字段值的用例人来做又慢又容易出错交给脚本正好。第三类是回归高频用例也就是每个版本都要跑、步骤固定、断言清晰的那批。反过来这几种就别做自动化了界面还在频繁改动的功能脚本写完三天就失效需要人工判断的视觉和体验类用例脚本断言不了一次性的、后续不会再跑的验证。UI 自动化尤其要克制。我个人的经验是UI 自动化只覆盖最核心的三五条链路就够了比如登录、下单、支付。其余的都下沉到接口层去做接口层稳定得多维护成本低一个数量级。团队里经常有人一上来就写几百条 UI 脚本热情很高三个月后全变成僵尸脚本谁也不敢删。6. 我踩过的坑和几条实在的经验做功能测试这些年有几个教训是花钱买的写在这里给同行参考。第一条不要相信上次测过了。一个功能这个版本没改不代表它是安全的。它依赖的下游可能改了它调用公共方法可能被重构了它依赖的数据结构可能变了。我的习惯是只要这次发布的改动清单里出现了这个功能相关的文件哪怕只是格式调整也要跑一遍冒烟。第二条测试环境一定要和线上尽量一致。我经历过一次事故测试环境里某个开关是关的线上是开的导致测试时完全没走到的代码路径在线上被触发了。从那以后我养成了一个习惯每次大版本测试前把测试环境和线上的关键配置项做一次逐条比对列出差异评估每个差异是否会导致测试结论失效。第三条把我以为写下来。测试过程中产生的所有假设比如这个字段应该是必填的这个状态只可能是 A 或 B都要记录下来。因为这些假设如果错了后面所有的测试结论都会跟着错。记录下来的另一个好处是事后复盘的时候能清楚看到是哪一环的判断出了偏差。第四条学会质疑需求本身。需求说用户可以修改手机号你可以问改完之后旧的手机号还能不能登录绑定的第三方账号要不要解绑正在进行的订单通知发到哪个号码这些问题开发往往也没想过。作为测试你不需要给出答案但你有责任把问题提出来。这是功能测试人员往上走的关键能力之一——从验证实现到参与定义。第五条保留证据。每一个重要的验证结果都要留下记录截图、日志、数据快照、时间戳。不是为了甩锅而是为了在出问题时能快速回溯。我见过太多我记得当时测过的争论最后因为没有证据谁都说不清。养成随手存证据的习惯会省掉很多无谓的沟通成本。第六条也是最重要的一条别把通过率当成绩。一个版本测出两百个 bug不代表测试质量高可能只是版本质量差。真正高质量的测试是在版本发布前就把那些一旦上线就会造成损失的问题拦在门外同时不给开发制造大量无效的噪音。这两者之间的平衡比单纯追求 bug 数量难得多也值钱得多。如果手头正在做的项目涉及比较复杂的业务流程我建议先花半天时间把状态机画出来把每个状态的所有可能迁移列成一张表然后对着表设计用例。这个方法用一次之后你会发现以前漏掉的那些边界原来一直就在那里只是从来没人系统地看它们一眼。