☰
自动化测试的九个常见错误:从设计到运维的避坑指南
2026/10/12 0:20:18 网站建设 项目流程

做自动化测试这些年,我带过十几个项目团队,也接手过不少别人做不下去的自动化框架。见得最多的一个现象是:项目刚启动时信心满满,三个月后团队开始默默改脚本,半年后这套“自动化”基本只剩下 CI 里那几个没人敢动的红点。

这不是工具的问题,也不是人的能力问题,而是大多数人都踩在了同样的几个坑里。我常说,自动化测试是一场高速公路驾驶,起步谁都会,但真正能保持高速、长时间不翻车的,靠的不是一脚油门踩到底,而是知道哪里有弯道、哪里有积水、哪里必须提前减速。

这篇就把我这些年亲眼见过、亲身踩过、又亲手填平的九个最常见的自动化测试错误,一个一个掰开揉碎讲清楚。每个错误都会说“为什么错”“会带来什么后果”“正确的姿势是什么”,最后还会给一套可以直接照做的自查清单。不管你是刚准备引入自动化的测试负责人,还是已经在维护一套半死不活脚本的倒霉蛋,这篇文章应该都能帮你省下至少两个月的时间。

1. 九个错误的整体视图,先看清全局再动手

1.1 自动化测试为什么总在“写脚本”阶段翻车

先聊一个很有意思的现象:绝大多数自动化测试项目不是死在技术难题上,而是死在常识问题上。我甚至见过有的团队连被测系统的业务逻辑都没理清,就急着开始写 UI 自动化脚本。等脚本写到一半,开发把页面结构调整了一下,几百个用例全部红掉,然后项目就这么黄了。

所以在我眼里,自动化测试最核心的问题从来不是“用什么框架”“写什么代码”,而是“你到底为什么自动化”“自动化哪些东西”“怎么让自动化跑得稳定”。这三个问题想不清楚,后面无论怎么写都是给别人填坑。

我梳理了一下,最常见的错误可以分成三个层面:

  • 设计层面:金字塔结构建错、用例筛选拍脑袋、只测快乐路径。
  • 代码层面:选择和页面结构强绑定、同步策略混乱、用例相互依赖。
  • 运维层面:测试数据一塌糊涂、不稳定测试污染 CI、报告和失败信息完全不可用。

这三个层面不是孤立的,它们会在项目运行中互相放大。比如测试数据管理不好会把用例变不稳定,用例不稳定会让 CI 大面积报红,CI 报红多了团队就会开始跳过测试,最后整个自动化体系失去信任,没人再关注它。

1.2 九个错误不是堆在一起,而是一条完整的因果链

这几个错误,表面上看起来是“九条并列的问题”,实际它们是一条因果链。设计期没想清楚,代码期就会埋雷;代码期埋了雷,运行维护期就天天爆;等到了维护期,你已经不是在做自动化,而是在做“自动化保洁”。

我习惯把这条链画成这样的逻辑:

  • 设计期错 → 写出的用例本身就带病(比如大量低价值 UI 用例)。
  • 实现期错 → 带病的用例变成不稳定用例(比如选择器脆弱、同步混乱)。
  • 运维期错 → 不稳定用例进入 CI,团队每天被失败淹没,自动化废弃。

所以纠正也要从源头开始。很多团队只知道天天修“不稳定的选择器”,却从没想过“这个用例本身是不是就不该出现在这套自动化里”。这就是为什么我特别坚持:自动化测试里的稳定性和维护成本,至少有 50% 是由早期设计决策决定的,后面再怎么优化,也只是在减轻症状,而不是治疗病根。

2. 设计期的三个坑,决定你的自动化能不能活过三个月

2.1 错误一:UI 自动化头重脚轻,整座金字塔是倒着盖的

我每次接手存量自动化项目,第一件事就是看他们的用例分布。最触目惊心的场景是:一套自动化里 70% 以上都是 UI 层用例,单元测试几乎没有,API 层接口测试也寥寥无几。

这个比例为什么是灾难?

我给你打一个比方。假设你要测一栋楼的水管系统,最稳妥的办法是分三层来测:水管阀门本身(单元测试)、楼层之间的主管道(接口测试)、最后是你家水龙头打开有没有水(UI 测试)。如果一上来就只盯着每个房间的水龙头做测试,一旦楼上某节管道出了问题,所有水龙头都会一起不出水,但你根本分辨不出来问题到底出在哪一层。

UI 自动化是所有自动化里最贵、最慢、最脆弱的一层。它依赖页面元素、依赖网络延迟、依赖浏览器版本、甚至依赖前端框架的渲染时机。一个页面样式调整,可能导致几十个用例同时失败,而失败原因只是某个按钮的位置变了,功能一点问题都没有。

合理的金字塔应该是:底层单元测试占大头,接口测试做骨架,UI 测试只覆盖核心冒烟路径和关键用户旅程。

我给大家一个可以直接抄的参考比例:

  • 单元测试:约 70%
  • 接口测试:约 20%
  • UI 层端到端测试:约 10%

注意,这只是参考,不是教条。有些业务系统因为历史原因没办法补单元测试,那接口测试的比例可以适当上调。但无论如何,不要让 UI 自动化成为你唯一的自动化防线。

值得记住的一句话:UI 测试的数量应该少到“即使它全部失败,你也能在 10 分钟之内手工回归一遍”。如果做不到,说明你的自动化金字塔已经建歪了。

2.2 错误二:只测快乐路径,好像系统永远不会犯错

第二个设计期的大坑,是测试用例全部写在整个系统最正常的路径上。用户输入正确信息、服务器正常返回、数据库正常连接、第三方接口稳定响应。整条链路看起来完美无缺,跑起来绿油油一片,但你的自动化根本没有测出系统真实存在的大部分风险。

我接过一个支付模块的自动化项目,原有用例 120 多条,覆盖率报告上看漂亮得很,可是上线前还是出现了严重的生产事故。后来复盘发现:所有的用例只测了正常扣款流程,完全没人测余额不足、重复支付、超时回滚、字段缺失这些异常分支。

自动化测试的价值,其实不在于验证“系统能做它该做的事”,而在于验证“当事情变糟糕时,系统会不会优雅地处理”。只写快乐路径,等于把最容易出错的那一半逻辑完全留给了生产环境去发现。

怎么破?我的建议是,给每个“功能点”至少配两个维度的用例:

  • 正面用例:验证主流程能正常走通。
  • 反面用例:验证异常分支能被捕获、提示、回滚或容错。

还有个更细节的小技巧:写用例时,要习惯性地问自己“如果这里返回超时怎么办”“如果这条数据已经存在怎么办”“如果用户连点两次提交怎么办”。不要觉得这是在抬杠,边界条件恰恰是线上故障的第一来源。

2.3 错误三:用例筛选拍脑袋,把不该自动化的东西塞进脚本

第三个设计期的常犯错误,是自动化用例的选择完全没有流程和标准。常见的操作是,领导一拍板说“我们要上自动化测试”,然后团队把手工测试用例里看着最顺眼的几百条直接拿过来,照着录成脚本。我的评价只有四个字:自寻死路。

不是所有手工用例都适合自动化。我在项目里筛选自动化用例时,一般会从四个维度打分:

维度说明不适合自动化的信号
执行频率需要在每个版本回归中反复执行一个月才跑一次甚至更少的低频用例
结果稳定性输入固定时,预期结果是否固定依赖主观视觉判断、依赖模糊逻辑的用例
环境依赖是否能在独立环境中稳定运行依赖外部服务、弱网、地理位置等不确定因素
业务风险失败后对业务的影响范围低风险低影响的功能点,不值得投入维护成本

这套筛选逻辑做下来,你会发现真正适合自动化的用例数量,通常只占手工用例总数的一小部分。这完全正常。自动化的目标是“用最少成本守住最重要的功能”,而不是“把所有手工用例都翻译成脚本”。

我还见过一种反向错误:团队自动化了半天,只挑最稳定、最简单、永远不会出问题的用例来做,比如登录、退出、改个密码,结果覆盖率数字很好看,实际风险一点没降低。这类跑得再绿也没有价值。选自动化用例的核心原则就一条:优先选那些“出了问题会死人、但流程本身又很固定”的场景。

3. 写代码时埋下的三个雷,一碰就炸

3.1 错误四:测试代码跟页面结构死死绑在一起,重构就等于重写

设计期的问题没想清楚,后面写代码的时候,通常就会在实现细节上继续犯错。其中最让团队头大的是“页面结构耦合”。

什么叫页面结构耦合?就是你的测试选择器表达式直接写死在脚本里,而且依赖的是页面最细节的实现。比如某些团队写自动化时,喜欢靠元素的绝对位置或者复杂 XPath 去定位,像下面这样:

# 反面案例:和浏览器结构强绑定的写法 driver.find_element(By.XPATH, "/html/body/div[2]/div[3]/form/div[1]/input")

这种写法看着能用,但只要开发人员在页面前面加了一个div,或者调整了一下列表结构,你的定位就失效了。前端代码只要做一次正常重构,测试脚本就得跟着改几十处。时间一长,测试代码的维护成本甚至超过了产品代码本身。

正确的姿势是分层。UI 测试代码理论上应该和页面实现彻底解耦,你只应该关心“页面上有什么业务元素”,而不应该关心“这个元素在 DOM 里处于什么位置”。我建议团队在写 UI 自动化时,强制使用 Page Object 模式。

Page Object 的核心思想,是把每个页面的元素定位和操作方法封装成一个独立的类。测试用例调用的方法,而不是元素本身。这样即使前端改了样式和结构,你也只需要修改对应页面的类,而不需要动几百个用例。

好的定位策略,优先级我一般这样排:

  1. 使用业务语义的># 显式等待的正确姿势 from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC from selenium.webdriver.common.by import By # 等待登录按钮处于可点击状态,最多等 10 秒,每 500 毫秒轮询一次 login_button = WebDriverWait(driver, 10, poll_frequency=0.5).until( EC.element_to_be_clickable((By.CSS_SELECTOR, ".login-submit")) )

    需要注意,显式等待虽然好用,但“等待条件”也得选对。很多同学喜欢等“元素存在”,但元素存在并不代表它可见,更不代表它可操作。等你真的点下去的时候,脚本就报“element not interactable”。所以等待的条件要等到业务上真正需要的状态,而不是只要能定位到就算成功。

    还有一个坑是前后端异步请求。页面加载完了,但数据还没通过 Ajax 返回。这时候我会先等一个“页面标志元素出现”,再等一个“业务数据元素出现”,最后再断言。顺序很重要,宁可多认识几个等待条件,也不要靠堆 sleep 去碰运气。

    3.3 错误六:用例之间相互依赖,每个测试都带着前一个测试的影子

    代码层面的第三个大坑,是测试用例没有隔离性。比如有条用例必须先走完“创建订单”流程,才能测“订单支付”,于是后一条用例直接调前面那条用例的函数。看起来节省了代码量,实际上你已经把原本独立的两个用例焊死在一起了。

    这种耦合产生的典型问题包括:

    • 如果“创建订单”失败了,“订单支付”也会跟着失败,但你根本看不出来支付功能本身有没有问题。
    • 用例只能按顺序执行,无法做到数据饥饿式的并行加速。
    • 测试代码的阅读理解门槛变高了,新成员根本不敢动这些“连环雷”。

    更严重的,是有些用例依赖共享的静态数据或全局状态。比如用例 A 把系统参数改成“开启某个开关”,用例 B 在没复位的情况下也去跑,结果 B 失败。你排查了半天才意识到,问题根本不在 B 的执行步骤里,而是被前面的用例“污染”了。

    正确做法是,每条用例都应该像是“在干净的房间里重新开始”一样:

    1. 每个用例独立创建自己需要的数据,而不是复用上一条用例留下的数据。
    2. 每个用例执行前,尽可能执行环境复位或数据清理。
    3. 用例之间不允许有任何调用关系,公共逻辑可以抽成“助手方法”,但不要用“测试用例去调用另一测试用例”的方式。

    这样做的代价是脚本写起来会稍微多一点,但它换回来的确定性是非常值得的。毕竟测试的第一目标不是“写得少”,而是“结果可信”。

    4. 运行维护阶段接着踩的三个坑,跑起来比不跑还累

    4.1 错误七:测试数据硬编码和共享环境,跑一次脏一次

    运行维护期的第一个大坑,是测试数据完全不可控。

    我见过这样的项目:所有的测试用例都用同一套账号,用同一个邮箱,用同一个手机号。第一次跑没问题,第二次跑的时候系统提示“该邮箱已被注册”,于是脚本开始失败。排查了很久才发现,是昨天测试自己在库里留下了脏数据。

    这就是典型的共享数据导致的脏环境问题。硬编码数据的危害不只是跑一次就失败,更严重的是:用例之间会互相干仗,并行执行时冲突更是家常便饭。为了规避问题,团队会加一堆“先清理再创建”的流程,但这种清理逻辑本身又会成为新的不稳定点。

    正确的测试数据管理,核心就两个字:隔离。具体拆开来看,有三层:

    第一层是数据生成隔离。每个用例执行时,自己动态生成独一无二的数据,不会和别的用例撞车。比如往测试数据工厂里传入时间戳或随机数:

    def generate_unique_email(prefix="test"): timestamp = str(int(time.time() * 1000)) return f"{prefix}_{timestamp}@example.com"

    第二层是数据清理隔离。用例执行完后,最好把自己创建的数据删掉,或者至少标记成“可回收”。如果实在因为外键关系不能删除,就要建立定期清理任务,防止数据越积越多。

    第三层是数据环境隔离。开发环境、测试环境、预发布环境必须严格分开,自动化测试不应该跑在开发人员随手造数据的公共环境里。有条件的话,最好给自动化测试单独开一套环境,哪怕配置低一点都行,关键是环境稳定、数据可控。

    4.2 错误八:不稳定测试混进 CI,红灯变成家常便饭

    很多团队对自动化测试的预期是这样的:脚本写完、代码里配置好、往 CI 一挂,就大功告成了。实际操作起来,第一个礼拜很开心,从第二个礼拜开始,CI 里头就开始出现各种“时好时坏”的用例。有人早上跑一次通过了,下午跑一次又失败,再点一次重跑又绿了。

    刚开始大家还会较真地去查,但查来查去找不到原因。等到 CI 开始天天飘红,团队的心态就变成了“这个红灯我知道,是网络波动,不用管”。到这一步,自动化测试就已经彻底失去价值了。

    不稳定测试混入 CI,害处不只是“多几个红点”这么简单,它会摧毁整个测试体系的信任。当 CI 的红灯变成狼来了之后,真正的回归缺陷也会被当作“又是那个坏用例”忽略掉。

    我自己在管理自动化项目时,有一条铁律:从不允许“不稳定”测试长时间停留在 CI 流水线里。具体执行是这样操作的:

    • 同一个用例首次失败,可以去查原因并重跑确认。
    • 如果同一个用例在一周内出现两次以上“偶发失败”,不管有没有找到根因,先把它从 CI 套件里移出,移入“问题观察清单”。
    • 只有彻底修复并通过连续 10~20 次稳定运行后,才允许回归主套件。

    这条规矩看上去很麻烦,但它能逼着团队认真对待每一次偶发失败。宁可暂时少几个用例,也不能让 CI 的红灯常态化。

    同时,我还建议团队给 CI 加上“重试策略”的上限。有些团队的思路是“失败就重试三次,三次都失败才算失败”,这可以理解,但重试本身会掩盖问题。如果用例需要靠重试才能稳定,它本身就是不稳定的,应该被移出而不是被纵容。

    4.3 错误九:报告和失败信息一团糟,修一个用例要半天

    最后一个错误,也是让很多团队“做不下去自动化”的致命一击,就是失败信息不可用。每次用例红了,你点开日志,只看到一句“expected to find element,but not found”,连截图都没有,连是哪个页面的哪个操作失败都不知道。

    然后排查流程就变成了:重新手动执行一遍用例,看它卡在哪一步,再根据猜想去翻产品源码,最后才定位到原因。一次失败排查花掉半小时以上,经历两三次之后,没人想继续维护这套测试了。

    我一直跟团队强调一个概念:测试失败报告不仅要告诉我们“哪里红了”,还要尽可能告诉我们“为什么红”。这需要从源头上去做几件很朴素的事:

    • 断言失败时,异常信息必须包含预期的值和实际的值,而不是一句笼统的“元素未找到”。比如“‘订单金额’文本显示为 99.90,预期为 100.00”。
    • UI 自动化在用例例失败时,自动截图并保存页面源码,有条件的话录制一段浏览器执行录像。
    • 日志里加入步骤编号和业务操作描述,让人一眼能看得出来失败的是“登录后点击提交”这个动作,而不是一串看不懂的函数调用栈。
    • 按模块给测试分类分组,报告和趋势统计都跟着模块走,不要所有用例混在一起。

    如果你们用的是 JUnit、TestNG 这种框架,完全可以接入 Allure 或者 ReportPortal 这类报告工具,把截图、录像、日志、步骤一层层挂到报告里。只花一天时间做接入,后面省下的排查时间会是十个一天。

    5. 老鸟纠偏指南:从错误清单到自查习惯

    5.1 给现有自动化套件做一次体检

    很多人读到这,可能会想“完了,我这套自动化项目九条几乎全占了”。也不用慌,只要它还能跑,就有救。我最推荐的做法,是先做一次系统性体检,而不是今天改一条、明天改一条,最后什么都没改透。

    体检的清单我可以直接给出来:

    体检项检查方法健康标准
    金字塔比例统计用例层级分布UI 用例不超过 30%
    用例价值抽样 20 条用例,人工评估业务风险高风险用例占比超过一半
    同步策略全局搜索 sleep,统计使用次数单个测试文件 sleep 不超过 2 次
    选择器质量统计 XPath 绝对路径和>

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

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

    立即咨询