Skip to content

第 12 章 · 设备仿真与多浏览器

本章目标:学会用内置设备字典模拟手机/平板,配置地理位置、语言、时区、深色模式等环境特征,并把测试矩阵扩展到 Chromium、Firefox、WebKit 三大内核。

12.1 设备描述符:devices 字典

Playwright 内置了常见设备的参数注册表(user agent、屏幕尺寸、viewport、是否触屏等),一行代码即可"变成"一台 iPhone:

python
from playwright.sync_api import sync_playwright

with sync_playwright() as p:
    iphone = p.devices["iPhone 13"]          # 取出该设备的完整参数字典
    browser = p.webkit.launch()              # 移动端设备建议配 WebKit(iOS 内核)
    context = browser.new_context(**iphone, locale="zh-CN")
    page = context.new_page()
    page.goto("https://m.example.com")

设备与内核的搭配

官方示例中 iPhone 系列用 WebKit 打开——因为真机 iOS Safari 的内核就是 WebKit,这样仿真保真度最高;Android 设备(Pixel 等)则应搭配 Chromium。用错内核不会报错,但会损失仿真真实性。

注意一个细节:预置的桌面设备参数(如 "Desktop Chrome")带的是 Windows 平台的 UA。若想用本机平台的真实 UA,可以显式覆盖为 None

python
desktop = p.devices["Desktop Chrome"]
context = browser.new_context(**desktop, user_agent=None)

12.2 手工定制仿真参数

不套用预设时,context 参数可以直接指定。这些参数在 context 创建时固定,其中 viewport 还能按页动态调整:

python
context = browser.new_context(
    viewport={"width": 1280, "height": 720},     # 视口尺寸
    is_mobile=True,                               # 移动端标志(影响 meta viewport 处理)
    has_touch=True,                               # 启用触屏事件
    device_scale_factor=2,                        # 高 DPI 屏,影响截图清晰度
)

# 单页调整视口(响应式断点测试)
page.set_viewport_size({"width": 375, "height": 812})
expect(page.get_by_test_id("mobile-menu")).to_be_visible()

is_mobilehas_touch 的区别值得强调:前者让页面把自身当作移动设备渲染(响应式布局生效),后者只决定触控事件是否可用——模拟 iPad 竖屏浏览通常两个都要开。

12.3 环境 Locale、时区与权限

国际化与本地化行为是手工测试最容易遗漏的部分,Playwright 可以精确仿真:

python
context = browser.new_context(
    locale="de-DE",                          # 界面语言/格式化区域
    timezone_id="Europe/Berlin",             # IANA 时区
    geolocation={"latitude": 52.52, "longitude": 13.405},
    permissions=["geolocation"],             # 预授权,避免权限弹窗
)

page.goto("https://example.com")
page.evaluate("navigator.language")          # -> 'de-DE'

常用权限还包括 "notifications""camera""microphone"。反过来要测试"用户拒绝定位"的场景,可以先授予权限再用 context.clear_permissions() 撤销。

12.4 深色模式与 UA 伪装

深色模式适配 bug 是高频线上问题,color_scheme 一键切换:

python
dark = browser.new_context(color_scheme="dark")
page = dark.new_page()
page.emulate_media(color_scheme="dark")      # 单页级别随时切换
body_bg = page.evaluate(
    "getComputedStyle(document.body).backgroundColor"
)
print(body_bg)                                # 断言不是纯白即适配了暗色

UA 伪装用于验证"站点对旧浏览器降级"等逻辑:

python
context = browser.new_context(user_agent="Mozilla/5.0 (iPhone; CPU iPhone OS 16_0 like Mac OS X)")
page.goto("https://example.com")
assert page.get_by_text("请升级浏览器").is_visible()

UA 只是字符串

改 UA 并不会改变引擎能力——CSS/JS 特性支持仍由真实内核决定。需要测引擎差异时,老老实实跑多内核矩阵,别指望伪装。

12.5 三内核矩阵测试

pytest-playwright 用 --browser 参数即可把同一批用例铺到多个内核上执行:

bash
pip install pytest-playwright playwright
playwright install chromium firefox webkit   # 安装三个内核

pytest --browser chromium                    # 只跑 Chromium
pytest --browser chromium --browser firefox --browser webkit   # 三内核全跑
pytest --browser-channel chrome              # 甚至可以用系统安装的 Chrome

工程化建议:日常提交只跑 Chromium 保证速度,合并前/夜间任务再跑全矩阵;对已知内核差异(如 WebKit 对某些 CSS 特性的滞后支持)用 @pytest.mark.skip_browser("webkit") 显式标注原因,而不是全局回避。

本章小结

  • p.devices["iPhone 13"] 提供完整设备参数,new_context(**device) 应用;移动端配 WebKit、Android 配 Chromium;
  • is_mobile 管渲染模式、has_touch 管触控事件,两者独立;
  • locale / timezone_id / geolocation / permissions 覆盖国际化与权限场景,clear_permissions() 可撤销;
  • color_scheme="dark" + emulate_media 测试深色模式;UA 伪装不改引擎能力;
  • 多内核矩阵交给 --browser 参数重复传入即可。

🧪 随堂测验

点击你认为正确的选项。答错时会展示正确答案与原因解析。

1. 用 Playwright 模拟 iOS 设备上的 Safari,推荐的内核组合是?

2. context 参数中 is_mobile 和 has_touch 的关系是?

3. 修改 user_agent 字符串能做到什么、不能做到什么?

4. 想让一批用例在 Chromium、Firefox、WebKit 上都跑一遍,最简单的做法是?

🛠️ 动手实践

  1. 用 iPhone 13 + WebKit 访问任一响应式站点,分别在 375px 与 768px 视口下断言导航栏形态切换。
  2. 构造 locale="ja-JP" 的 context,验证日期显示格式随区域变化(或断言 Intl.DateTimeFormat().resolvedOptions().locale)。
  3. 编写深色模式测试:切到 dark 后断言 body 背景色亮度低于浅色模式下的取值。

下一章:数据驱动与参数化 E2E