Playwright 的高级技巧:网络拦截、多页面测试与移动端模拟

这篇文章系统讲解 Playwright 的高阶用法,包括网络拦截的三种处理方式、多页面测试中的 context 管理,以及移动端模拟的设备配置与常见误区,并给出适合工程落地的实践建议。适合已经掌握基础自动化、希望提升端到端测试稳定性和覆盖能力的测试工程师与前端团队。

Playwright 现在已经是很多团队做端到端测试的主流选择。它自动等待、跨浏览器、debug 工具都很顺手,比起早期的 WebDriver 方案确实舒服太多。但真正把测试铺开到复杂业务场景时,总会遇到几个麻烦:外部接口不稳定、页面跳转容易丢上下文、移动端适配问题在桌面端发现不了。这篇文章想围绕 Playwright 的三个高级用法展开:网络拦截、多页面测试和移动端模拟。它们不是炫技,而是用来解决实际工程问题的。

AI technology illustration

很多团队会遇到一个问题:基础测试用例跑通了,但一到复杂场景就不知道怎么写。不是 API 不熟练,而是对 Playwright 的页面模型和网络模型缺少一套系统的理解。这篇内容会从三个常见的方向切入,聊聊它背后的设计思路和在实际工程里的取舍。

网络拦截:把不可控的网络请求变成可控的测试条件

端到端测试最怕的不是代码写错,而是环境抖动。你调用第三方支付接口偶尔超时,开发环境返回的数据不完整,或者某个公共 CDN 资源不稳定,测试就会闪红。这时用例本身没有问题,问题在外部依赖。Playwright 的 route API 就是用来处理这类场景的。

网上关于 route 的介绍,大多数是直接给一段 fulfill 的 mock 示例。但在真实项目里,你需要先弄清一个前提:是拦截整个 context 还是单个 page。context.route 对该上下文内所有页面以及后续新打开的页面生效,page.route 只作用于当前页面。在多页面测试中,如果你只写 page.route,点击后打开的新页面不会继承这个规则。很多测试脚本陷入时好时坏的怪象,根因就在这里。

const context = await browser.newContext();
await context.route('**/api/order/**', route => {
  route.fulfill({
    status: 200,
    contentType: 'application/json',
    body: JSON.stringify({ id: 1, status: 'paid' })
  });
});

这个模式在测试里很常用,但要注意一个度:如果你把所有请求都 mock 掉,测试就变成了契约测试,失去了端到端验证前端与后端集成的意义。我的建议是只拦截两类请求:一类是第三方依赖,另一类是用于构造故障场景的请求。业务核心链路里的接口,能走真实服务就走真实服务。

route 的处理方式其实有三种,不只是 fulfill。

处理方式 用途 示例场景 注意事项
continue 修改后继续原始请求 注入 token、替换 CDN 域名 真实服务仍然会被调用,需要保证其可用性
fulfill 伪造响应 依赖不稳定接口、模拟异常数据 响应结构要和真实接口保持一致
abort 中止请求 模拟断网、资源加载失败 要确认页面是否有兜底逻辑,避免连带崩溃

一个容易被忽略的细节是路由匹配顺序。多个 route 规则如果同时命中同一个请求,Playwright 会按照注册顺序的逆序处理,也就是后注册的先执行。如果你在测试里既想对全局请求做日志记录,又想 mock 某个特定接口,注意不要让全局的 continue 抢先 fulfill。建议把更具体的规则往后注册,或者通过判断 URL 提前分流。

多页面测试:页面切换的坑都在 context 管理上

另一个高频场景是多页面。用户点击“去支付”“查看详情”,浏览器打开新标签页,传统测试框架处理起来很别扭。Playwright 的设计里,page 不是孤立存在的,它由 BrowserContext 统一管理。这个 context 才是会话状态的容器。所以当你处理新开页面时,不应该去猜测窗口序号,而是监听 context 的 page 事件。

const [newPage] = await Promise.all([
  context.waitForEvent('page'),
  page.click('text=打开新窗口')
]);
await newPage.waitForLoadState('domcontentloaded');
console.log(newPage.url());

这里用 Promise.all 是有讲究的。如果你先点击,再等待 page 事件,事件可能在监听注册之间已经触发,你就会错过它。反过来,你先设置监听,再点击,就能在事件发生前挂好钩子。这是一种常见且稳定的 async 事件处理模式。

多页面还有一个容易忽略的地方:每个页面对象是独立的,你不能用旧 page 去操作新页面。很多测试脚本在这个位置出 bug,因为代码里还留着原来的 page 变量。另外,所有页面共享 context 的 cookie 和 localStorage,登录态会自动同步。这对多页面流程是好事,但如果测试用例之间互相干扰,解决方案不是去清理页面,而是为关键场景单独创建 context。

多页面测试典型的问题,我整理了一下:

  • 新页面还没加载完就去操作元素,导致选择器匹配不到
  • 使用了全局 page 变量,而不是当前活动页面的引用
  • 忽略了 popup 事件的监听,导致新窗口没有正确关闭
  • 多个测试复用一个 context,导致 cookie 和存储污染

最后一个问题尤其隐蔽。比如测试 A 在支付页面设置了一个优惠券状态,测试 B 打开的却是同一个 context,就可能会继承这个状态。如果你不想费心去清理,在测试中为每个用例创建独立的 context 是最稳妥的做法。

移动端模拟:改视口只是第一步

移动端模拟是另一个容易被低估的功能。很多人以为把 viewport 改成 375×812 就等于模拟 iPhone。实际上,响应式网站会根据 UA、isMobile、hasTouch 等特征决定渲染哪种版本。只改视口,浏览器仍会以桌面版 UA 请求页面,很多站点会直接返回电脑版内容。

Playwright 提供了一整套设备描述符,分布在 devices 数组中。你可以直接取用,也可以在此基础上覆盖自己需要的参数。下面这段配置模拟了一台 Galaxy S9+ 的设备特征,同时开启地理位置权限,并往页面注入了定位信息。

const { devices } = require('playwright');
const galaxy = devices['Galaxy S9+'];
const context = await browser.newContext({
  ...galaxy,
  permissions: ['geolocation'],
  geolocation: { longitude: 116.39, latitude: 39.9 },
  hasTouch: true
});
const page = await context.newPage();

这里的 hasTouch 在大多数设备描述符里已经包含,但如果你自己构造移动端配置,很容易漏掉。hasTouch 决定浏览器是否触发触摸事件,isMobile 则会影响 meta viewport 的解析。这两个字段加上 deviceScaleFactor,共同决定了渲染行为。在你排查移动端截图和布局差异的时候,先从这几个字段入手。

设备模拟不是没有局限。一些移动端特有的系统行为,比如软键盘弹出、手势返回、原生弹窗,模拟器并不会完整复现。更现实的问题是,桌面浏览器中的触摸事件和真机上的物理滚动、像素密度仍有差异。所以设备模拟更适合做功能回归和前端适配的初筛,最终上真机或云测设备依然有必要。

常见误区与落地建议

最后聊几个我在代码评审里经常见到的误区,如果能避开,你的测试稳定性和维护性都会有明显提升。

  • 滥用 waitForTimeout,固定等几秒。应该优先使用 Playwright 的自动等待和 expect 的轮询断言,让测试的节奏跟随真实状态,而不是拍脑袋的数字。
  • 在移动端模拟中只设置 viewport,忽略 userAgent 和 isMobile,导致拿到的还是桌面版页面。
  • 网络拦截规则留在全局,测试用例间互相污染。更好的做法是在 beforeEach 中注册规则,在 afterEach 中卸载,保证每个用例从干净的网络环境开始。
  • 在多页面测试里仍然用 page.waitForTimeout 去等待新页面,而不是监听 page 事件。

落地方面,我的建议是:

  • 把设备模型、路由规则这些公共配置抽成 fixture 或者独立的常量文件,而不是散落在每个测试里。
  • 为网络拦截命名场景。比如 mock 订单、mock 支付,测试用例只需要选择场景,不需要重复写 route 回调。
  • 在 CI 中适当控制并发,因为每个 context 都会占用真实浏览进程,worker 数过高会带来资源竞争。
  • 配合 trace 查看器分析多页面跳转和网络请求时序,很多时候误报其实来自浏览器内部行为,而不是测试逻辑。

我自己的经验是,高级技巧的核心不是熟悉更多 API,而是理解底层模型:context 管理页面和网络状态,设备模拟负责还原终端环境。模型清楚了,各种 API 只是不同问题下的对应解法。

回到最初的问题。网络拦截把不可控的依赖变成可验证的输入,多页面测试把复杂的用户路径拆成可追踪的页面流转,移动端模拟则让前端适配问题提前暴露。这三个能力单独使用都能解决一类问题,组合起来可以让你的测试环境更接近真实用户环境。Playwright 真正厉害的地方不在于某个 API,而是它给了你一层对浏览器行为的抽象,让你有能力把不确定性变成测试中的可控变量。

原创文章,作者:fudengji,如若转载,请注明出处:https://fudengji.cn/article/756/

(0)
上一篇 54分钟前
下一篇 49分钟前

相关推荐