使用 Playwright 处理自动化测试中的身份认证问题

在 Web 自动化测试中,身份认证(如登录、Token 验证)是高频且关键的环节。重复执行登录操作不仅降低测试效率,还可能因状态残留导致测试失败。Playwright 通过灵活的浏览器上下文(Browser Context)管理和状态持久化机制,提供了高效的身份认证解决方案。

Playwright 身份认证的核心原理

浏览器上下文(Browser Context)

Playwright 在隔离的浏览器上下文中执行测试,每个上下文包含独立的 Cookie、LocalStorage 和 Session Storage,确保测试间的独立性。通过复用已认证的上下文状态,可避免重复登录。

认证状态存储

Playwright 支持将认证状态持久化为文件(如 JSON),存储以下内容:

  • Cookies:跨会话的持久化认证标识
  • LocalStorage:应用本地存储的 Token 或用户数据
  • Session Storage(需特殊处理):会话级临时存储

推荐将状态文件保存在 playwright/.auth 目录并添加到 .gitignore,防止敏感信息泄露。


基础方法:复用认证状态

使用 storage_state 持久化状态

通过 browser_context.storage_state() 方法保存认证状态,并在新上下文中加载:

# 同步模式示例
from playwright.sync_api import sync_playwright

def test_authenticated_flow():
    with sync_playwright() as p:
        browser = p.chromium.launch()
        # 创建已认证的上下文
        auth_context = browser.new_context()
        auth_page = auth_context.new_page()
        auth_page.goto("https://example.com/login")
        # 执行登录操作...
        
        # 保存状态到文件
        auth_context.storage_state(path="auth_state.json")
        
        # 创建新上下文并加载状态
        new_context = browser.new_context(storage_state="auth_state.json")
        new_page = new_context.new_page()
        new_page.goto("https://example.com/dashboard")
        # 验证认证状态...

异步模式实现

异步代码适用于高并发场景,需结合 async/await 语法:

# 异步模式示例
import asyncio
from playwright.async_api import async_playwright

async def async_auth():
    async with async_playwright() as p:
        browser = await p.chromium.launch()
        auth_context = await browser.new_context()
        auth_page = await auth_context.new_page()
        await auth_page.goto("https://example.com/login")
        # 异步登录操作...
        
        await auth_context.storage_state(path="async_auth_state.json")
        
        new_context = await browser.new_context(storage_state="async_auth_state.json")
        new_page = await new_context.new_page()
        await new_page.goto("https://example.com/dashboard")

Session Storage 与动态注入

某些应用使用 Session Storage 存储临时 Token,但 Playwright 默认不持久化该状态。需通过 JavaScript 手动保存和注入:

保存 Session Storage

def save_session_storage(page):
    session_data = page.evaluate("() => JSON.stringify(sessionStorage)")
    with open("session_storage.json", "w") as f:
        f.write(session_data)

加载 Session Storage

def load_session_storage(context, hostname):
    with open("session_storage.json", "r") as f:
        session_data = f.read()
    context.add_init_script(f"""
        (storage => {
     
     {
            if (window.location.hostname === '{
      
      hostname}') {
     
     {
                const entries = JSON.parse(storage);
                Object.entries(entries).forEach(([key, value]) => {
     
     {
                    window.sessionStorage.setItem(key, value);
                }});
            }}
        }})('{
      
      session_data}')
    """)

自动化登录与脚本录制

4使用 playwright codegen 录制登录流程

通过命令行工具自动生成登录脚本并保存认证状态:

# 录制并保存认证状态
playwright codegen --save-storage=auth.json https://example.com/login

# 复用认证状态启动浏览器
playwright open --load-storage=auth.json https://example.com/dashboard

集成到测试框架(Pytest)

通过 Fixture 实现全局认证状态管理:

# conftest.py
import pytest
from playwright.sync_api import Page, BrowserContext

@pytest.fixture(scope="session")
def auth_context(browser: BrowserContext):
    context = browser.new_context()
    page = context.new_page()
    page.goto("https://example.com/login")
    page.fill("#username", "[email protected]")
    page.fill("#password", "password123")
    page.click("#submit")
    context.storage_state(path="auth_state.json")
    yield context
    context.close()

@pytest.fixture
def authenticated_page(auth_context):
    return auth_context.new_page()