Playwright BrowserContext:它是什么、为何重要以及如何配置

发布: (2026年2月7日 GMT+8 02:02)
10 分钟阅读
原文: Dev.to

Source: Dev.to

如果你已经使用 Playwright 一段时间了,你一定已经使用过 BrowserContext——即使你并没有完全意识到它的存在。它是那种悄悄影响以下方面的核心概念:

  • 测试隔离
  • 速度与不稳定性
  • 认证状态
  • 并行执行
  • 测试套件的整体可维护性

本文是一篇实用的、面向真实场景的深度解析,内容包括:

  • BrowserContext 实际上是什么(以及它为何存在)
  • Playwright 如何为你创建和管理上下文
  • 如何在 playwright.config.ts 中全局配置上下文
  • 如何在 不共享状态 的前提下在测试之间共享设置
  • 何时以及如何使用多个设置文件 / 项目
  • 常见的反模式和真实案例

受众: 已经了解 Playwright 基础并希望提升测试架构的工程师。

什么是 BrowserContext?

A BrowserContext 是一个隔离的浏览器配置文件。

One Browser  → the actual Chrome / Firefox / WebKit instance
Multiple BrowserContexts → separate incognito‑like sessions

Each context has its own:

FeatureDescription
Cookies私有于该上下文
LocalStorage / SessionStorage每个上下文隔离
IndexedDB独立存储
Cache不共享
Permissions上下文特定
Auth state独立

All contexts share the same browser process, which makes them fast and cheap.
如果你曾经并排打开两个隐身窗口,你实际上已经使用了两个浏览器上下文。

Playwright的默认隔离

test('example', async ({ page }) => {
  // This page lives in a fresh browser context
});

底层实现

  1. Playwright 启动浏览器。
  2. 创建一个新的浏览器上下文。
  3. 在该上下文中创建页面。
  4. 测试结束后销毁该上下文。

好处

  • 测试之间没有状态泄漏
  • 安全的并行执行
  • 可预测的失败

如果你曾经因为残留的 cookie 而与不稳定的 Selenium 测试斗争过,这就是为什么 Playwright 感觉好得多的原因。

注意: 你很少需要手动创建上下文,但了解其用法很有帮助:

import { chromium } from '@playwright/test';

const browser = await chromium.launch();
const context = await browser.newContext();
const page = await context.newPage();

Playwright 的测试运行器会为每个测试执行上述操作,除非你另有指示。

关键要点: 页面永远不会在没有 BrowserContext 的情况下存在。

全局配置 – playwright.config.ts

大多数上下文配置通过 use 块完成。

// playwright.config.ts
import { defineConfig } from '@playwright/test';

export default defineConfig({
  use: {
    baseURL: 'https://my-app.com',
    headless: true,
    viewport: { width: 1280, height: 720 },
    locale: 'en-US',
    timezoneId: 'America/New_York',
  },
});

use 中的所有内容都会成为 默认的 BrowserContext 选项
因此每个测试都会使用相同的视口、语言区域、时区…… 但状态仍然不同

杀手特性:storageState

在每个测试之前登录会导致:

  • 脆弱
  • 冗余

全局设置以创建可重用的身份验证快照

// global-setup.ts
import { chromium } from '@playwright/test';

export default async () => {
  const browser = await chromium.launch();
  const context = await browser.newContext();
  const page = await context.newPage();

  await page.goto('https://my-app.com/login');
  await page.fill('#email', 'user@test.com');
  await page.fill('#password', 'password');
  await page.click('button[type=submit]');

  // Save authenticated state to a file
  await context.storageState({ path: 'auth.json' });
  await browser.close();
};
// playwright.config.ts (continued)
export default defineConfig({
  globalSetup: require.resolve('./global-setup.ts'),
  use: {
    storageState: 'auth.json',
  },
});

现在 每个测试

  • 已登录开始
  • 仍然在全新的浏览器上下文中运行

这是一种兼顾隔离性和便利性的方式。

常见反模式:共享实时页面

let sharedPage;

beforeAll(async ({ browser }) => {
  const context = await browser.newContext();
  sharedPage = await context.newPage();
});

为什么这样不好

  • 破坏测试隔离
  • 破坏并行执行
  • 导致顺序依赖的失败

推荐做法 – 固件

// fixtures.ts
import { test as base } from '@playwright/test';

export const test = base.extend({
  authenticatedPage: async ({ page }, use) => {
    await page.goto('/dashboard');
    await use(page);
  },
});

每个测试仍然会得到:

  • 它自己的上下文
  • 它自己的页面

但是 设置逻辑 能够干净地共享。

多用户 / 角色

有时你需要多个已认证的用户(例如,聊天应用、管理员流程)。

test('admin invites user', async ({ browser }) => {
  const adminContext = await browser.newContext({ storageState: 'admin.json' });
  const userContext = await browser.newContext({ storageState: 'user.json' });

  const adminPage = await adminContext.newPage();
  const userPage = await userContext.newPage();

  await adminPage.goto('/admin');
  await userPage.goto('/dashboard');
});

使用项目进行基于角色的测试

// playwright.config.ts (continued)
export default defineConfig({
  projects: [
    {
      name: 'guest',
      use: { storageState: undefined },
    },
    {
      name: 'user',
      use: { storageState: 'auth.json' },
    },
    {
      name: 'admin',
      use: { storageState: 'admin.json' },
    },
  ],
});
  • 每个项目运行相同的测试
  • 启动不同的浏览器上下文。
  • 可以并行运行。

运行特定项目:

npx playwright test --project=admin

什么应该/不应该共享

✅ 应该共享❌ 永不共享
Config (use)实时页面
Storage snapshots (storageState)实时上下文
Fixtures & helpers夹具和帮助函数

记住: 一个测试 = 一个浏览器上下文(除非你显式创建更多)。此规则解释了 Playwright 为什么可扩展,为什么并行运行有效,以及为什么测试不会泄漏状态。

不稳定测试快速检查清单

❌ 不良模式✅ 修复方案
beforeAll 创建页面让 Playwright 为每个测试创建全新的上下文
全局变量持有 page/context改用 fixtures 或 storageState
测试依赖于之前的导航保持每个测试独立
重复的 UI 登录使用专用的身份验证快照(storageState

TL;DR

  • BrowserContext = 隔离的配置文件(cookies、storage、auth 等)
  • Playwright 默认为每个测试创建一个全新的上下文。
  • playwright.config.ts 中配置默认值 → use
  • 使用 global setup + storageState 来避免重复登录。
  • 切勿共享实时页面/上下文;仅共享不可变的数据(config、snapshots、fixtures)。
  • 利用 projects 进行基于角色或设备的测试。

测试愉快! 🚀

Source:

在 Playwright 中管理浏览器上下文

beforeAll – 方便但有风险

// 示例(**不要**在共享状态中使用此模式)
beforeAll(async () => {
  // …
});

使用 beforeAll 看似方便,但它会破坏:

  • 并行执行 – 同时运行的测试可能相互干扰。
  • 隔离保证 – 共享状态会在测试之间泄漏。
  • 可调试性 – 失败更难追溯到具体的测试。

解决方案: 更倾向于使用带有 fixture 的每个测试的设置。如果真的必须只运行一次,请确保不创建共享的浏览器状态

安全的共享配置方式

  • Playwright 配置选项useprojectsfixtures)是安全的共享对象。
  • 浏览器上下文、页面以及可变的全局变量 安全,不能在测试之间共享。

如果某个失败仅在测试一起运行时出现,通常是共享的浏览器状态导致的。

多用户流程:常见陷阱

有些团队试图把复杂的多用户场景强行放进单个页面。这会导致:

  • 测试难以阅读
  • 使用假 mock 而非真实行为
  • 漏掉 bug

解决方案: 为每个用户单独创建浏览器上下文(或独立页面),有意识地进行建模。

为什么 BrowserContext 很重要

BrowserContext 是 Playwright 最核心的架构概念之一。
当你理解上下文的工作方式时,测试套件自然会变得:

  • 更快 – 上下文可以并行创建并高效复用。
  • 更可靠 – 每个测试都在隔离的环境中运行。
  • 更易扩展 – 添加新场景不需要纠结的状态。
  • 更易推理 – 每个用户的流程都是显式的。

结论

如果你的 Playwright 测试显得脆弱或难以维护,根本原因往往是浏览器上下文的管理方式

  • 围绕隔离的上下文进行设计。
  • 使用fixture进行每个测试的设置。
  • 仅在真正全局、不可变的配置时才使用 beforeAll

当你正确处理上下文时,Playwright 的其他部分就会变得轻而易举

祝测试愉快!

Back to Blog

相关文章

阅读更多 »

Nim

配置文件 nim ~/.config/nim/config.nims import std/strutils, strformat switch'nimcache', fmt'{getCurrentDir}/nimcache/{projectName}/{CompileTime.toHex...