一、测试环境不稳定
哪怕你的脚本撰写得至极完美, 要是测试环境自身并非稳定, 那也会致使测试失败。
常见原因:
-
网络波动:测试机与被测系统之间的网络不稳定。
-
被测系统所依赖的微服务, 出现了问题, 数据库也出现了问题, 消息队列同样出现了问题, 导致依赖服务宕机或者异常。
-
是这样一来, 被测系统的服务器资源处于紧张状态的缘故, 致使响应变得缓慢, 进而造成了服务器负载是过高的这种情况, 在测试环境里也是如此。
-
环境配置存在差异, 其中, CI/CD环境的配置, 和本地开发环境的配置, 并不相同。
定位与解决技巧:
- 容器化测试环境
采用容器化技术达成一致、可重复部署的测试环境构建, 官方针对CI/CD部署推出的镜像是极为便利的。
好处是, 能够确保每一次运行, 都处于相同的、干净的环境之中, 将因环境配置差异而导致的问题排除掉句号。
# 示例:使用 Playwright 官方 Docker 镜像运行测试
docker run -it --rm -v $(pwd):/app -w /app mcr.microsoft.com/playwright/python:latest /bin/bash -c "pip install -r requirements.txt && pytest"
- 监控与告警
对于基础设施进行监控, 具体是要实时监控, 针对测试环境来监控, 所监控的内容存在CPU指标, 还有内存指标, 以及网络指标, 另外还有磁盘IO指标。
服务健康检查:监控所有被测系统及其依赖服务的健康状态。
告警的机制是, 一旦察觉到存在异常, 便要即刻借助邮件或短信或者企业微信去通知那些相关的人员, 使其知晓。
- 依赖服务 Mock/Stub
针对面对外部的、具有不稳定特点的依赖服务, 要思考采用 Mock 技术或者 Stub 技术去模拟它的响应, 以此来降低外部因素所产生的干扰, 进而提高测试的稳定性, 它还具备强大的网络拦截功能。
# 拦截并 Mock 特定 API 响应
page.route("**/api/user/**", lambda route: route.fulfill(
status=200,
content_type="application/json",
body='{"username": "mocked_user", "id": 123}'
))
# 也可以拦截图片、字体等资源,加速测试或模拟加载失败
page.route("**/*.{png,jpg,jpeg,svg}", lambda route: route.abort())
二、闪烁测试
有这样一种测试用例, 被称作"闪烁测试", 即便代码以及环境均无任何变动, 它却时而能够通过, 时而又会失败。这种测试可着实是自动化测试工程师的"噩梦", 它会极大程度地削减测试结果所具备的信任度。
常见原因:
-
竞态条件: 脚本跟应用互相之间, 或者应用自身内部, 存在异步操作去竞争资源, 进而致使执行顺序变得没法确定。
-
等待的不充分情形: 显式等待之时, 时间或者条件的设置并不合理, 没有能够完全覆盖住异步操作完成所需的时间。
-
测试逻辑存在对系统时间、随机数等非确定性因素的依赖, 其中包括随机数或者时间。
-
作为外部系统不稳定状况的体现, 所依赖的第三方服务不稳定, 另, 所依赖的外部 API 也不稳定。
-
浏览器存在问题, 驱动也存在问题, 浏览器会出现自身偶尔出现的非预期行为,驱动也会出现自身偶尔出现的非预期行为。
-
用于测试的用例耦合, 在用例彼此之间存有隐式的依赖关系, 致使一个用例出现失败的情况进而对其他用例产生影响。
定位与解决技巧:
它所具备的测试框架, 也就是Test, 给出了好多功能强大的东西, 以此来对抗那种不稳定的测试情况, 也就是Flaky Tests:
- Test 的自动重试机制
在"试"中, 有内置的重试机制, 于"点点点.ts"(或者"杠"配置里)设置"两个反引号"参数。
在 `..ts` (JS/TS):
import { defineConfig } from '@playwright/test';
exportdefaultdefineConfig({
// ...
retries: process.env.CI ? 2 : 0, // 在 CI/CD 环境下重试2次,本地不重试
// ...
})
在 `` () 中:
可以利用`-` 插件,或在 CI/CD 中配置重试逻辑。
# 命令行运行
pytest --reruns 2 --reruns-delay 1 # 失败后重试2次,每次间隔1秒
关键在于, 重试仅仅是一种权宜的办法, 它只能解决表面问题, 无法触及根本。它会给予你一定的时间, 让你设法去确定并修正根本的问题, 而绝非是将这些问题掩盖起来。
- 强大的调试与分析工具
Trace(追踪查看器), 它能够记录完整的测试执行轨迹, 这里面涵盖着每一步的操作, 以及网络请求, 还有DOM快照, 同时包括日志以及失败堆栈。它可是诊断闪烁测试的终极武器。
开启Trace, 于 `..ts` 里(或者运行参数那里), 去配置 `trace: 'on-first-retry'` 或者 `trace: 'on'`。
运行, 之后进行分析, 在失败以后, 通过使用 ` show-trace trace.zip` 这个命令来打开分析,你能够一步步地回放测试, 进而查看每一步的 DOM 状态, 以及元素是不是可见、可交互的。
视频录制与截屏:失败时自动录制视频和截屏。
配置: 于`()`之中或者`()`里面对``以及``选项予以配置。
# video_path 是视频保存的目录
context = browser.new_context(record_video_dir="videos/", record_video_size={'width': 640, 'height': 480})
# ...
# 在测试失败时自动截图 (pytest-playwright 默认会做)
# page.screenshot(path="failed_screenshot.png")
这些视觉证据对于理解非预期行为至关重要。
- 优化等待策略和断言
回顾"同步等待问题"部分,确保所有的等待都是显式且精确的。
运用的"库"来开展健壮性断定, 比如说`().()`而非` .()`, 前头那个会自行等候, 直至条件达成或者超出时间限制。
from playwright.sync_api import expect
# 断言元素可见,Playwright 会自动等待直到可见或超时
expect(page.locator(".success-message")).to_be_visible()
# 断言元素不可见
expect(page.locator(".loading-spinner")).to_be_hidden()
# 断言元素文本内容
expect(page.locator("#username-display")).to_have_text("John Doe")
- 消除非确定性因素
分离使用示例: 要保证每一个测试示例都是单独的, 不会取决于别的示例的执行次序, 或者状态, 依靠相关的机制妥善处理好 `setup` 和 ``。
进行清理会话操作: 在每一次测试之前, 要保证使得浏览器会话处于干净的状态。`.()` 通常情况下会给出干净的页面, 然而对于 `.()` 是需要加以留意的。
有关时间以及那随机数, 要防止测试去依赖当下的时间或者随机数。倘若非得如此, 那么就在测试过程当中对它们施以固定亦或是模拟的操作。

三、性能与效率低下
测试套件一旦变得臃肿庞大, 执行程序所需时间就会变得过长, 这会对反馈速度造成极其严重的影响, 进而降低自动化测试自身所具备的宝贵价值。
常见原因:
-
测试用例冗余:大量重复或低价值的测试用例。
-
这种定位器效率很低, 它运用了太过宽泛或者复杂的XPath/CSS, 进而致使查找元素花费时间。
-
非必要的用户界面之操作行为, 频繁出现的页面间跳转状况, 以及非必要的点击与输入动作场景。
-
串行执行:未充分利用多核 CPU 或分布式资源。
-
环境性能瓶颈:测试机或被测系统性能不足。
定位与解决技巧:
- 遵循测试金字塔原则
优先编写单元测试和接口测试,它们速度快、成本低。
关键业务路径以及用户场景会被 UI 自动化测试所覆盖, 仅仅只是这些而已。对于每个微小的功能而言, UI 自动化都不会在其上面进行, 要避免这种情况出现。
- 优化 配置
无头模式, 也就是所谓的一种模式, 在CI/CD环境之中, 它默认处于开启的状态, 这种状态能够极为显著地提升执行这一行为的速度。
browser = playwright.chromium.launch(headless=True) # 默认就是 True
并行执行, Test, 它具备支持多进程并行执行测试用例的能力, 能够充分地利用 CPU 核心。
# 运行所有测试,使用 4 个 worker 并行
pytest --workers 4
- 利用 `` 和 `page` 的生命周期
处于""里, "page"默认是""范围, ""默认是""范围。
假如存在多个测试用例, 这些测试用例是需要在相同的浏览器上下文当中运行的, 比如说处于已登录的状态, 那么可以思考在 `` 或者 `class` 级别去创建 ``, 在 `` 级别创建 `page`。通过这种方式能够防止每次进行测试的时候都要重新登录或者初始化上下文。
- 最小化 UI 交互
在仅关乎数据校验的情形下, 优先借由 的 '' 实施接口调用予以数据的就绪或校验达成, 防止出现没啥必要的用户界面交互情况。此种方式较用户界面操作要快出好几个数量级。
- 精简定位器
力求规避运用太过宽泛或者繁杂的XPath, 特别是那种以 `//` 起始、对整个DOM树予以遍历的XPath。要优先选用ID、`data - test - id` 等直接并且唯一的属性。

四、系统性排查自动化测试问题的通用方法论
对于除了处理特定问题的解决方案之外, 去掌握一套通用的关于问题排查的方法论这件事, 它有着至关重要的性质。
- 观察与收集证据
自动化录屏/截屏:配置 失败时自动录制视频和截屏。
Trace: 在运行遭遇失败之后, 即刻于第一时间将Trace回放测试予以打开, 对每一步的DOM状态、网络请求以及日志展开观察。
以下是详细日志: 在脚本里面加上详细的日志输出, 像操作步骤、变量值、错误信息这些, 在失败的时候能够快速地进行定位。
打开浏览器, 并且, 到Trace里去查看情况, 浏览器控制台那儿有没有错误, 网络请求是不是正常, 也就是HTTP状态码以及响应时间方面的情况。
- 隔离与复现
为了排除其他用例造成的干扰, 去尝试单独运行已经失败的测试用例, 以此来做运行单个用例这件事。
将和失败没有关联的代码进行注释, 一步步地简化用例, 探寻出最小的复现路径。
于本地进行复现, 要优先选择在本地出现的环境, 也就是那种被称为(`-- --debug` 模式)的环境来复现问题, 以此达到方便交互式调试的目的。
- 验证与调试
以手动方式进行复现, 依据失败之际所呈现的信息, 于浏览器里面手动开展相关操作, 查看是否能够实现稳定复现。
在IDE里设置断点, 通过逐步执行代码, 来观察变量值以及元素状态, 借助其Step-by-step模式逐步调试。
断言增加:在关键步骤增加断言,验证中间状态是否符合预期。
- 定位根因
莫非是应用自身所存在的Bug, 亦或是测试数据方面出现的问题, 又或者是环境问题, 再不然就是自动化脚本(定位器、等待、逻辑)所引发的问题呢问号?
与开发团队沟通,了解最近的代码变更,是否有影响到被测功能。
- 修复与验证
针对所定位到的问题, 对自动化脚本予以调整, 修复应用 Bug, 或者与开发人员取得联系实施修复。
先对运行失败的用例展开验证修复操作, 接着要保证问题已然得到解决, 随后还得思考增添新的断言, 或是添加新的用例, 以此来避免类似问题再度出现。