Playwright 认证(Authentication)
本章介绍如何在 Playwright 中处理认证,避免每个测试都重复登录,同时保持测试的隔离性。
认证的核心问题
大多数 Web 应用的测试都需要用户登录。
如果每个测试都从头执行登录流程,会显著拖慢测试速度。
Playwright 的解决方案是:认证一次,保存状态,所有测试复用。
准备工作:.auth 目录
实例
# 创建认证状态存储目录
mkdir -p playwright/.auth
# 添加到 .gitignore(认证状态包含机密信息)
echo 'playwright/.auth' >> .gitignore
mkdir -p playwright/.auth
# 添加到 .gitignore(认证状态包含机密信息)
echo 'playwright/.auth' >> .gitignore
.auth文件包含敏感 Cookie 和认证 Token,绝对不能提交到代码仓库。
基本方案:共享账号 + Setup 项目
这是 Playwright 推荐的认证方案,适用于所有测试共享同一个测试账号的场景。
第一步:创建认证 Setup
实例
// 文件路径:tests/auth.setup.ts
import { test as setup } from '@playwright/test';
// 指定认证状态保存路径
const authFile = 'playwright/.auth/user.json';
setup('authenticate', async ({ page }) => {
// 执行登录操作
await page.goto('https://www.runoob.com/login');
await page.getByLabel('用户名').fill('testuser');
await page.getByLabel('密码').fill('password123');
await page.getByRole('button', { name: '登录' }).click();
// 等待登录完成(确认页面跳转或出现用户信息)
await page.waitForURL(/dashboard/);
// 将当前的认证状态(Cookie、localStorage)保存到文件
await page.context().storageState({ path: authFile });
});
import { test as setup } from '@playwright/test';
// 指定认证状态保存路径
const authFile = 'playwright/.auth/user.json';
setup('authenticate', async ({ page }) => {
// 执行登录操作
await page.goto('https://www.runoob.com/login');
await page.getByLabel('用户名').fill('testuser');
await page.getByLabel('密码').fill('password123');
await page.getByRole('button', { name: '登录' }).click();
// 等待登录完成(确认页面跳转或出现用户信息)
await page.waitForURL(/dashboard/);
// 将当前的认证状态(Cookie、localStorage)保存到文件
await page.context().storageState({ path: authFile });
});
第二步:配置 Setup 项目
实例
// 文件路径:playwright.config.ts
import { defineConfig } from '@playwright/test';
export default defineConfig({
projects: [
// Setup 项目 —— 只运行一次,生成认证状态文件
{
name: 'setup',
testMatch: /auth\.setup\.ts/,
},
// 实际测试项目 —— 复用认证状态
{
name: 'chromium',
use: {
storageState: 'playwright/.auth/user.json',
},
dependencies: ['setup'], // 依赖 setup 项目,确保 setup 先运行
},
],
});
import { defineConfig } from '@playwright/test';
export default defineConfig({
projects: [
// Setup 项目 —— 只运行一次,生成认证状态文件
{
name: 'setup',
testMatch: /auth\.setup\.ts/,
},
// 实际测试项目 —— 复用认证状态
{
name: 'chromium',
use: {
storageState: 'playwright/.auth/user.json',
},
dependencies: ['setup'], // 依赖 setup 项目,确保 setup 先运行
},
],
});
第三步:测试直接使用认证状态
实例
// 文件路径:tests/dashboard.spec.ts
import { test, expect } from '@playwright/test';
test('查看仪表盘', async ({ page }) => {
// 直接访问需要登录的页面,page 已经带着认证状态
await page.goto('/dashboard');
await expect(page.getByText('欢迎回来,testuser')).toBeVisible();
});
import { test, expect } from '@playwright/test';
test('查看仪表盘', async ({ page }) => {
// 直接访问需要登录的页面,page 已经带着认证状态
await page.goto('/dashboard');
await expect(page.getByText('欢迎回来,testuser')).toBeVisible();
});
多账号场景
如果不同测试需要使用不同账号,可以创建多个认证状态文件。
实例
// 管理员测试
test.describe('管理员功能', () => {
test.use({ storageState: 'playwright/.auth/admin.json' });
test('管理用户', async ({ page }) => {
// 以管理员身份操作
});
});
// 普通用户测试
test.describe('普通用户功能', () => {
test.use({ storageState: 'playwright/.auth/user.json' });
test('浏览内容', async ({ page }) => {
// 以普通用户身份操作
});
});
test.describe('管理员功能', () => {
test.use({ storageState: 'playwright/.auth/admin.json' });
test('管理用户', async ({ page }) => {
// 以管理员身份操作
});
});
// 普通用户测试
test.describe('普通用户功能', () => {
test.use({ storageState: 'playwright/.auth/user.json' });
test('浏览内容', async ({ page }) => {
// 以普通用户身份操作
});
});
通过 API 请求认证(推荐)
通过 UI 执行登录流程较慢,更高效的方式是直接调用登录 API。
实例
// 文件路径:tests/auth.setup.ts
import { test as setup } from '@playwright/test';
const authFile = 'playwright/.auth/user.json';
setup('通过 API 认证', async ({ request }) => {
// 直接调用登录 API,不通过浏览器 UI
const response = await request.post('https://www.runoob.com/api/login', {
data: {
username: 'testuser',
password: 'password123',
},
});
// 确认登录成功
const json = await response.json();
console.log('登录成功,Token:', json.token);
// 将 Token 保存为 Cookie(模拟浏览器认证状态)
// 注意:storageState 需要在浏览器 Context 中保存
});
import { test as setup } from '@playwright/test';
const authFile = 'playwright/.auth/user.json';
setup('通过 API 认证', async ({ request }) => {
// 直接调用登录 API,不通过浏览器 UI
const response = await request.post('https://www.runoob.com/api/login', {
data: {
username: 'testuser',
password: 'password123',
},
});
// 确认登录成功
const json = await response.json();
console.log('登录成功,Token:', json.token);
// 将 Token 保存为 Cookie(模拟浏览器认证状态)
// 注意:storageState 需要在浏览器 Context 中保存
});
API 认证比 UI 认证更快更可靠,推荐在实际项目中优先使用。
认证方案的适用场景总结
| 场景 | 推荐方案 |
|---|---|
| 所有测试用同一个测试账号 | Setup 项目 + storageState |
| 不同类型测试需要不同账号 | 多个 storageState + test.use |
| 每个测试需要独立账号 | 每个测试内 API 创建账号并登录 |
| 账号资源有限 | Setup 项目认证一次,全部复用 |
