
本文详解如何在 playwright 测试中保持浏览器可见(headed 模式)、避免自动关闭,并通过 storage state 或持久化上下文实现登录状态复用,提升调试效率与测试稳定性。
本文详解如何在 playwright 测试中保持浏览器可见(headed 模式)、避免自动关闭,并通过 storage state 或持久化上下文实现登录状态复用,提升调试效率与测试稳定性。
在 Playwright 端到端测试中,开发者常遇到两个典型问题:一是 npx playwright test 默认以无头(headless)模式运行,浏览器窗口不可见,无法人工干预或观察交互细节;二是每次测试启动都创建全新上下文,导致登录态丢失,需重复执行登录逻辑——这不仅拖慢执行速度,还易触发验证码风控,降低测试鲁棒性。
一、让浏览器“看得见”:启用 headed 模式
Playwright 默认启用 headless: true,这是为 CI/CD 环境优化的设计,但本地调试时需显式切换为有界面模式。有两种推荐方式:
✅ 方式 1:配置文件全局启用(推荐)
编辑 playwright.config.ts,在 use 配置项中设置:
import { defineConfig } from '@playwright/test';
export default defineConfig({
use: {
headless: false, // ← 关键:启用图形界面
viewport: { width: 1280, height: 720 },
// 其他通用配置...
}
});
之后直接运行 npx playwright test 即可看到浏览器窗口。
✅ 方式 2:命令行临时启用(快速调试)
npx playwright test --headed
更进一步,使用 --debug 启动调试模式,它会自动启用 headed 模式,并暂停在每个测试步骤,支持断点、控制台交互和实时 DOM 检查:
npx playwright test --debug
⚠️ 注意:若使用 VS Code 的 Playwright Test Runner 插件,默认可能仍走 headless。请确保插件配置或终端命令明确指定
--headed或已更新配置文件。
二、让登录“只做一次”:复用认证状态
针对你代码中已使用 storageState: "./auth.json" 的场景,关键在于确保该文件真实存在且有效。auth.json 并非自动生成,必须由首次手动登录后显式保存:
▶ 步骤 1:生成合法的 auth.json(仅需一次)
运行以下脚本(建议 headless: false),完成真实登录并导出状态:
# generate_auth.py
from playwright.sync_api import sync_playwright
def save_auth_state():
with sync_playwright() as p:
browser = p.chromium.launch(headless=False) # 可视化操作
context = browser.new_context()
page = context.new_page()
page.goto("https://sina.exirbroker.com/mobile/#view/login.html")
page.get_by_label("user").fill("ab23")
page.get_by_label("pass").fill("csds")
page.get_by_text("btn").click()
# 等待跳转至受保护页面(如 newOrder.html)
page.wait_for_url("**/OnlineTrades/newOrder.html?**", timeout=15000)
# ✅ 保存完整认证状态(Cookies + LocalStorage + SessionStorage)
context.storage_state(path="./auth.json")
print("✅ Login state saved to auth.json")
browser.close()
if __name__ == "__main__":
save_auth_state()
运行后,auth.json 将包含该域名下的全部认证数据。后续测试即可直接加载复用。
▶ 步骤 2:在测试中正确加载状态
你的测试代码中已正确使用:
const context = await browser.newContext({ storageState: "./auth.json" });
✅ 但务必确认:
-
auth.json文件路径正确(建议使用绝对路径或检查工作目录); - 文件未过期(如 Token 失效、Cookie 过期)——可定期重新生成;
- 目标网站未变更登录逻辑(如新增 MFA、刷新 Cookie 机制)。
三、进阶方案:持久化上下文(适用于长期会话场景)
当需模拟“用户长期在线”行为(如定时爬虫、多日驻留测试),推荐使用 launch_persistent_context(Python)或 chromium.launchPersistentContext()(JS),它将整个用户数据目录(含 IndexedDB、缓存等)落盘,比 storage_state 更完整:
# Python 示例
from playwright.sync_api import sync_playwright
with sync_playwright() as p:
# 指定用户数据目录,自动持久化所有状态
context = p.chromium.launch_persistent_context(
user_data_dir="./my_user_data",
headless=False,
args=["--start-maximized"]
)
page = context.pages[0] if context.pages else context.new_page()
page.goto("https://sina.exirbroker.com/mobile/#view/login.html")
# 后续操作同上… 登录后关闭,下次启动自动恢复
总结与最佳实践
| 场景 | 推荐方案 | 说明 |
|---|---|---|
| 本地调试 & 人工介入 |
--headed 或 --debug
|
实时观察 UI、点击元素、排查定位问题 |
| 稳定复用登录态(主流) |
storage_state 文件 |
轻量、跨环境、易版本管理,适合 CI |
| 模拟真实用户长期会话 | launch_persistent_context |
数据最完整,但目录体积大,不便于共享 |
| 避免资源泄漏 | 显式 context.close() / browser.close()
|
即使使用持久化,也应在 afterAll 或 teardown 中清理非必要实例 |
最后提醒:对于你测试中需“用户点击某元素”的特殊交互,除开启 headed 模式外,还可结合 page.pause() 在关键步骤暂停,或使用 page.getByText("submit order").click({ force: true }) 绕过可见性校验(慎用,仅限调试)。真正的自动化应追求零人工依赖——建议将该交互封装为可预测的 API 触发或通过条件等待(await expect(locator).toBeVisible())保障稳定性。











