Playwright C 入门实战:从 dotnet new 到跑通首个端到端测试
本篇指南基于 Playwright 仓库中的官方 C# 安装文档 intro-csharp.md 展开,带你完整走通「创建 .NET 测试项目 → 安装 Playwright 依赖 → 下载浏览器 → 编写并运行首个端到端测试」的全流程,并深入解析 MSTest、NUnit、xUnit、xUnit v3 四套测试框架基类的差异、PageTest 生命周期设计,以及 BROWSER、HEADED、PWDEBUG 等关键运行参数的底层行为。读完你将在 Windows、Linux 或 macOS 上具备独立搭建 Playwright C# 测试工程并按需切换浏览器引擎与运行模式的能力。
一、Playwright 的定位与 C# 集成方式
Playwright 是专为端到端(E2E)测试设计的框架,支持 Chromium、WebKit、Firefox 三大现代渲染引擎,可在 Windows、Linux、macOS 上本地或 CI 中运行,支持 headless/headed 模式与原生移动端模拟。
在 .NET 生态中,Playwright 提供两条使用路径(详见 intro-csharp.md):
| 路径 | 说明 | 适用场景 |
|---|---|---|
| 测试框架基类 | 通过 Microsoft.Playwright.MSTest / Microsoft.Playwright.NUnit / Microsoft.Playwright.Xunit / Microsoft.Playwright.Xunit.v3 包提供 PageTest、ContextTest 等基类,开箱即得「每个测试独立的 Page / BrowserContext」、跨引擎运行、测试并行化、launch/context 选项调整 |
标准 E2E 测试工程(推荐入门路径) |
| Playwright 库 | 直接使用 Microsoft.Playwright 包手动管理 Playwright、Browser、BrowserContext 生命周期 |
自研测试基建或与其他测试框架集成,详见 library-csharp.md |
二、系统要求
根据 intro-csharp.md 的官方要求,当前版本的运行环境前提为:
- Playwright 以 .NET Standard 2.0 库形式分发,官方推荐 .NET 8;
- Windows 11+、Windows Server 2019+ 或 WSL;
- macOS 14 (Sonoma) 或更高版本;
- Debian 12 / 13、Ubuntu 22.04 / 24.04 / 26.04(x86-64 或 arm64)。
三、安装流程四步走
第 1 步:用 dotnet new 创建测试项目
四种测试框架各自对应一个 dotnet new 模板,执行后会生成 PlaywrightTests 目录,其中包含示例测试文件 UnitTest1.cs:
| 测试框架 | 创建命令 |
|---|---|
| MSTest | dotnet new mstest -n PlaywrightTests |
| NUnit | dotnet new nunit -n PlaywrightTests |
| xUnit (v2) | dotnet new xunit -n PlaywrightTests |
| xUnit v3 | dotnet new xunit3 -n PlaywrightTests |
# 以 MSTest 为例
dotnet new mstest -n PlaywrightTests
cd PlaywrightTests
注意 xUnit v3 的模板名是
xunit3(不是xunit),模板命名差异是常见踩坑点。
第 2 步:安装对应的 Playwright NuGet 包
| 测试框架 | NuGet 包 |
|---|---|
| MSTest | dotnet add package Microsoft.Playwright.MSTest |
| NUnit | dotnet add package Microsoft.Playwright.NUnit |
| xUnit (v2) | dotnet add package Microsoft.Playwright.Xunit |
| xUnit v3 | dotnet add package Microsoft.Playwright.Xunit.v3 |
每个包对应一个独立命名空间(Microsoft.Playwright.MSTest、Microsoft.Playwright.NUnit、Microsoft.Playwright.Xunit、Microsoft.Playwright.Xunit.v3),基类位于同名命名空间内,详见 test-runners-csharp.md。
第 3 步:构建项目,产出 playwright.ps1
dotnet build
构建完成后,bin 目录(如 bin/Debug/net8.0/)下会出现 playwright.ps1 脚本。这个脚本的作用等价于 JS 生态的 npx playwright install——它驱动 Playwright 自带的 driver 下载对应平台的浏览器二进制。从源码可以印证这一机制:registry/index.ts 中,当检测到 .NET 环境时会提示用户执行 pwsh bin/Debug/netX/playwright.ps1 <parameters>,即该脚本是浏览器注册表(registry)安装流程的 .NET 入口。
第 4 步:安装浏览器
pwsh bin/Debug/net8.0/playwright.ps1 install
- 示例使用
net8.0,如果你的 .NET 版本不同,需将路径中的net8.0替换为实际输出目录名(如net9.0); - 如果系统没有
pwsh,需要先安装 PowerShell 才能执行该脚本。
四、编写示例端到端测试
编辑项目中的 UnitTest1.cs。以最典型的 MSTest 版本为例,核心要点是:继承 PageTest 基类,直接使用注入的 Page 属性,无需手动启动浏览器或创建上下文:
using System.Text.RegularExpressions;
using Microsoft.Playwright;
using Microsoft.Playwright.MSTest;
namespace PlaywrightTests;
[TestClass]
public class ExampleTest : PageTest
{
[TestMethod]
public async Task HasTitle()
{
await Page.GotoAsync("https://playwright.dev");
// Expect a title "to contain" a substring.
await Expect(Page).ToHaveTitleAsync(new Regex("Playwright"));
}
[TestMethod]
public async Task GetStartedLink()
{
await Page.GotoAsync("https://playwright.dev");
// Click the get started link.
await Page.GetByRole(AriaRole.Link, new() { Name = "Get started" }).ClickAsync();
// Expects page to have a heading with the name of Installation.
await Expect(Page.GetByRole(AriaRole.Heading, new() { Name = "Installation" })).ToBeVisibleAsync();
}
}
其余三种框架的写法差异仅在于 using 命名空间、特性标注和基类来源,示例(摘自 intro-csharp.md):
- NUnit:
using Microsoft.Playwright.NUnit;,类上标注[Parallelizable(ParallelScope.Self)]与[TestFixture],测试方法用[Test]; - xUnit v2:
using Microsoft.Playwright.Xunit;,测试方法用[Fact],类无需额外特性; - xUnit v3:
using Microsoft.Playwright.Xunit.v3;,写法与 xUnit v2 一致。
基类家族:PageTest / ContextTest / BrowserTest / PlaywrightTest
官方测试运行器文档 定义了四个可用的基类,理解它们的生命周期是正确使用 C# 版 Playwright 的关键:
| 基类 | 每个测试获得的资源 | 说明 |
|---|---|---|
PageTest |
一个独立 BrowserContext 中新建的 Page |
最简路径,写单页 E2E 测试首选;可重写 ContextOptions() 方法控制上下文选项(即传给 browser.newContext() 的参数),按文件粒度设置各类模拟选项 |
ContextTest |
一个新的 BrowserContext |
适合多标签页场景,可自建任意多个 Page;同样支持重写 ContextOptions() |
BrowserTest |
一个 Browser 实例 |
可自建多个 Context,但每个测试需自行负责清理所创建的上下文 |
PlaywrightTest |
一个 Playwright 对象 |
最底层,测试可自行启动/停止任意多个浏览器 |
性能与隔离策略上,官方明确说明:Playwright 和 Browser 实例会在测试之间复用(提升性能),而推荐每个测试使用全新的 BrowserContext,以实现浏览器状态隔离。
五、运行测试与常用参数
基础运行
dotnet test
默认行为(来自 intro-csharp.md「Running the Example Tests」):
- 默认在 Chromium 上运行;
- 默认 headless 模式——不会弹出浏览器窗口,结果与日志输出到终端;
- 可通过
BROWSER环境变量切换引擎,或调整 launch 配置项。
切换浏览器引擎
方式一:BROWSER 环境变量(各平台写法,摘自 running-tests-csharp.md):
# bash
BROWSER=webkit dotnet test
# batch
set BROWSER=webkit
dotnet test
# powershell
$env:BROWSER="webkit"
dotnet test
方式二:runsettings 参数直接指定:
dotnet test -- Playwright.BrowserName=webkit
需要在多个浏览器/配置上批量回归时,分别执行多次 dotnet test 并配合各自的 runsettings 文件:
dotnet test --settings:chromium.runsettings
dotnet test --settings:firefox.runsettings
dotnet test --settings:webkit.runsettings
对应的 runsettings 示例:
<?xml version="1.0" encoding="utf-8"?>
<RunSettings>
<Playwright>
<BrowserName>chromium</BrowserName>
</Playwright>
</RunSettings>
有头模式(headed)
HEADED=1 dotnet test # bash
set HEADED=1 && dotnet test # Windows batch
$env:HEADED="1"; dotnet test # PowerShell
选择性运行
# 按测试类名过滤单个文件
dotnet test --filter "ExampleTest"
# 多个类
dotnet test --filter "ExampleTest1|ExampleTest2"
# 按测试方法名过滤
dotnet test --filter "Name~GetStartedLink"
并行 worker 数量
各框架的默认并行行为与调参方式(摘自 test-runners-csharp.md):
| 框架 | 默认行为 | 调参命令 | 限制 |
|---|---|---|---|
| NUnit | 所有测试文件并行,文件内顺序执行(ParallelScope.Self) |
dotnet test -- NUnit.NumberOfTestWorkers=5 |
仅支持 ParallelScope.Self |
| MSTest | 所有测试类并行,类内顺序执行(ExecutionScope.ClassLevel) |
dotnet test --settings:.runsettings -- MSTest.Parallelize.Workers=4 |
不支持方法级并行(MethodLevel) |
| xUnit | 所有测试类并行,类内顺序执行 | dotnet test -- xUnit.MaxParallelThreads=5 |
推荐 xUnit 2.8+,默认使用 conservative 并行算法 |
| xUnit v3 | 同 xUnit | dotnet test -- xUnit.MaxParallelThreads=5 |
默认 conservative 并行算法 |
官方建议:CPU 密集型测试的 worker 数取核心数的一半,IO 密集型可取全部核心数。
六、进阶配置:ContextOptions 与 Browser launch 选项
重写 ContextOptions 定制浏览器上下文
在派生自 PageTest / ContextTest 的测试类中重写 ContextOptions() 方法,即可为该文件下所有测试统一注入模拟选项:
using System.Threading.Tasks;
using Microsoft.Playwright;
using Microsoft.Playwright.MSTest;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace PlaywrightTests;
[TestClass]
public class ExampleTest : PageTest
{
[TestMethod]
public async Task TestWithCustomContextOptions()
{
// 此时 Page(及其 BrowserContext)已应用自定义的 colorScheme、viewport 与 baseURL:
await Page.GotoAsync("/login");
}
public override BrowserNewContextOptions ContextOptions()
{
return new BrowserNewContextOptions()
{
ColorScheme = ColorScheme.Light,
ViewportSize = new()
{
Width = 1920,
Height = 1080
},
BaseURL = "https://github.com",
};
}
}
注意 BaseURL 生效后可直接用相对路径 GotoAsync("/login")。
用 runsettings 定制 Browser / launch 选项
浏览器级选项(引擎名、headless、channel 等)通过 runsettings 文件或 CLI 参数覆盖:
<?xml version="1.0" encoding="utf-8"?>
<RunSettings>
<Playwright>
<BrowserName>chromium</BrowserName>
<LaunchOptions>
<Headless>false</Headless>
<Channel>msedge</Channel>
</LaunchOptions>
</Playwright>
</RunSettings>
dotnet test -- Playwright.BrowserName=chromium Playwright.LaunchOptions.Headless=false Playwright.LaunchOptions.Channel=msedge
完整的 runsettings 参考(含并行设置、ExpectTimeout、调试环境变量,摘自 test-runners-csharp.md):
<RunSettings>
<!-- MSTest adapter -->
<MSTest>
<Parallelize>
<Workers>4</Workers>
<Scope>ClassLevel</Scope>
</Parallelize>
</MSTest>
<!-- General run configuration -->
<RunConfiguration>
<EnvironmentVariables>
<!-- For debugging selectors, it's recommended to set the following environment variable -->
<DEBUG>pw:api</DEBUG>
</EnvironmentVariables>
</RunConfiguration>
<!-- Playwright -->
<Playwright>
<BrowserName>chromium</BrowserName>
<ExpectTimeout>5000</ExpectTimeout>
<LaunchOptions>
<Headless>false</Headless>
<Channel>msedge</Channel>
</LaunchOptions>
</Playwright>
</RunSettings>
NUnit / xUnit / xUnit v3 版本仅 adapter 节点不同:<NUnit><NumberOfTestWorkers>24</NumberOfTestWorkers></NUnit> 或 <xUnit><MaxParallelThreads>1</MaxParallelThreads></xUnit>,Playwright 节点与上相同。
七、调试 Playwright C# 测试
Playwright 运行在 .NET 进程中,可直接用 Visual Studio 或 VS Code 调试器断点调试。此外 Playwright 自带 Playwright Inspector:
PWDEBUG=1 dotnet test
开启后可逐步单步执行 Playwright API 调用、查看调试日志并探索 locator 状态。
若开启了 verbose API 日志(通过 DEBUG=pw:api 环境变量),消息会输出到标准错误流;在 Visual Studio 中对应 Output 窗口的 Tests 窗格,同时也会出现在每个测试的 Test Log 中。从仓库源码看,PWDEBUG 相关的调试钩子实现在浏览器上下文层面,可参考 browserContext.ts 与 CLI 入口 program.ts,它们对 C# 与 JS 生态是共享的同一套 driver。
八、替代路径:直接使用 Microsoft.Playwright 库
如果不使用测试框架基类,可以按 library-csharp.md 手动搭建:
dotnet new console -n PlaywrightDemo
cd PlaywrightDemo
dotnet add package Microsoft.Playwright
dotnet build
# 安装浏览器(netX 替换为实际输出目录名,如 net8.0)
pwsh bin/Debug/netX/playwright.ps1 install
最小可运行示例:
using Microsoft.Playwright;
using var playwright = await Playwright.CreateAsync();
await using var browser = await playwright.Chromium.LaunchAsync();
var page = await browser.NewPageAsync();
await page.GotoAsync("https://playwright.dev/dotnet");
await page.ScreenshotAsync(new()
{
Path = "screenshot.png"
});
库模式下同样可用 web-first 断言(自动重试直到条件满足或超时):
using Microsoft.Playwright;
using static Microsoft.Playwright.Assertions;
// 按需修改默认 5 秒超时
SetDefaultExpectTimeout(10_000);
using var playwright = await Playwright.CreateAsync();
await using var browser = await playwright.Chromium.LaunchAsync();
var page = await browser.NewPageAsync();
await page.GotoAsync("https://playwright.dev/dotnet");
await Expect(page.GetByRole(AriaRole.Link, new() { Name = "Get started" })).ToBeVisibleAsync();
跨平台发布时,Playwright 默认只打包发布目标运行时对应的 driver,可在项目文件中通过 PlaywrightPlatform 覆盖(取值 all、none、linux、win、osx,可用 ; 组合):
<PropertyGroup>
<PlaywrightPlatform>all</PlaywrightPlatform>
</PropertyGroup>
九、仓库内延伸阅读
| 主题 | 路径 |
|---|---|
| C# 安装指南(本文主体文档) | docs/src/intro-csharp.md |
| 测试框架基类与并行策略 | docs/src/test-runners-csharp.md |
| 运行与调试测试 | docs/src/running-tests-csharp.md |
| 库模式使用与断言 | docs/src/library-csharp.md |
| Web-first 断言与 locator 写法 | docs/src/writing-tests-csharp.md |
| Codegen 生成测试 | docs/src/codegen-intro.md |
| Trace Viewer 查看测试轨迹 | docs/src/trace-viewer-intro-csharp.md |
| CI 上运行 C# 测试 | docs/src/ci-intro.md |
| 文档校验与 API 文档工具链 | utils/doclint/ |
以上链接中的文档由仓库自带的文档工具链统一校验(见 package.json 中的 doc 脚本),保证示例命令与 API 描述和实现保持一致。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0627
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00