1. 项目概述:为什么Playwright跑TS脚本需要专属配置?
如果你是从Selenium或者Puppeteer转战Playwright,或者刚开始接触Web自动化测试,可能会遇到一个典型问题:为什么我的JavaScript脚本跑得好好的,换成TypeScript就报一堆找不到模块、语法错误的幺蛾子?尤其是在使用Playwright Test这个官方测试运行器时,直接运行一个.ts文件,大概率会碰壁。这个问题的核心,就在于Playwright的运行环境需要一个“翻译官”和“导航图”,来理解并执行你的TypeScript代码。这个“导航图”,就是playwright.config.ts(或.js)配置文件。
简单来说,Playwright Test是一个基于Node.js的测试运行器。Node.js原生只能执行JavaScript。当你写下import { test, expect } from '@playwright/test';这样的TypeScript代码时,Node.js是看不懂的。我们需要一个“预处理”环节,把TypeScript编译成JavaScript。这个预处理工作,可以交给ts-node、tsx或者esbuild等工具。而playwright.config.ts文件,正是告诉Playwright Test:“嘿,我这里的测试文件是TypeScript写的,请你用我指定的这个编译器(比如ts-node)先处理一下,再去执行。”
所以,配置playwright.config.ts不是可选项,而是当你决定在Playwright项目中使用TypeScript时,必须完成的基础设施搭建。它定义了项目的“游戏规则”:在哪里找测试文件、用什么浏览器、如何编译TypeScript、测试失败时要不要截图录屏、用什么报告格式等等。没有它,Playwright Test就像失去了操作手册,无法正确处理你的TS脚本。
2. 核心需求解析:从零搭建一个TS驱动的Playwright项目
要跑通一个TypeScript的Playwright脚本,我们的目标不仅仅是“能运行”,而是要建立一个可靠、可维护、高效的自动化测试环境。这背后对应着几个核心需求:
2.1 类型安全与开发体验TypeScript的核心价值是静态类型检查。在编写元素定位器(如page.locator('button'))或断言(如expect(title).toContainText('Hello'))时,我们希望编辑器能提供智能补全、参数提示,并在编码阶段就发现潜在的类型错误(比如错误地使用了click方法的参数)。这能极大减少运行时错误,提升代码质量。
2.2 统一的测试执行与报告我们需要一个统一的命令(如npx playwright test)来执行所有测试,并能够方便地按标签、按文件名过滤测试用例。测试完成后,需要清晰直观的报告(如HTML报告、Allure报告)来查看通过率、失败原因、执行日志和录屏,便于问题追溯。
2.3 可配置的运行环境不同的测试场景需要不同的配置:本地调试时可能想用headed模式看着浏览器跑,CI/CD流水线中则需要无头模式;可能需要对某些慢动作设置更长的超时时间;或者需要为不同的项目配置不同的基础URL。这些灵活多变的配置项,需要一个中心化的地方来管理。
2.4 高效的编译与执行流程我们不希望每次修改代码后,都要手动执行一次tsc编译,再去运行生成的.js文件。理想的流程是:修改*.spec.ts文件后,保存,然后一条命令就能直接运行最新的测试。这要求我们的配置能集成一个快速的TypeScript执行器。
基于以上需求,一个标准的Playwright + TypeScript项目配置,就需要围绕playwright.config.ts文件展开,解决编译、执行、报告、环境配置等一系列问题。
3. 环境准备与项目初始化
在开始配置之前,确保你的本地环境已经就绪。这里假设你从零开始。
3.1 Node.js与包管理器首先,你需要安装Node.js(建议LTS版本,如18.x或20.x)。Node.js自带了npm包管理器。你也可以选择yarn或pnpm,它们在某些场景下速度更快。本文将以npm为例。
打开终端,检查安装是否成功:
node --version npm --version3.2 初始化项目并安装Playwright创建一个新的项目目录并进入,然后初始化npm项目。这里我们直接生成一个package.json文件。
mkdir my-playwright-project cd my-playwright-project npm init -y接下来,安装Playwright的核心测试库。使用--save-dev将其作为开发依赖安装。
npm install --save-dev @playwright/test3.3 安装Playwright浏览器Playwright需要它自己管理的浏览器二进制文件(Chromium, Firefox, WebKit)来运行测试。安装核心库时不会自动下载浏览器。你需要运行以下命令来安装:
npx playwright install这个命令会下载所有三个浏览器(Chromium, Firefox, WebKit)到本地缓存中。如果你只想安装特定的浏览器,可以加上参数,例如npx playwright install chromium。
注意:第一次安装浏览器可能会比较慢,因为需要从网络下载几百MB的文件。如果遇到网络问题,可以尝试设置环境变量
PLAYWRIGHT_DOWNLOAD_HOST使用国内镜像源,或者使用--with-deps参数并配置npm镜像。例如,在某些网络环境下,可以尝试:npm config set registry https://registry.npmmirror.com/ npx playwright install --with-deps
3.4 安装TypeScript及相关依赖现在安装TypeScript编译器以及让Playwright能够直接运行TS的工具。最常用的组合是typescript和ts-node。
npm install --save-dev typescript ts-node @types/nodetypescript: TypeScript编译器本身。ts-node: 一个TypeScript执行引擎,可以直接在Node.js中运行.ts文件,无需预先编译成.js。@types/node: 提供Node.js API的类型定义,让TS能识别process,setTimeout等Node原生模块。
初始化TypeScript配置,生成tsconfig.json文件:
npx tsc --init生成的tsconfig.json文件包含了很多默认配置。对于Playwright项目,我们通常需要调整其中几项。
4. 核心配置文件详解:playwright.config.ts
这是整个项目的“大脑”。Playwright Test运行时会自动在项目根目录寻找这个文件。让我们一步步拆解一个功能完备的配置。
4.1 基础结构搭建首先,在项目根目录创建playwright.config.ts文件。Playwright配置支持TypeScript,所以我们直接使用.ts后缀,以便在配置中也能享受类型提示。
打开playwright.config.ts,开始编写:
import { defineConfig, devices } from '@playwright/test'; /** * 阅读 https://playwright.dev/docs/test-configuration 了解更多信息。 */ export default defineConfig({ // 测试文件所在的目录。可以使用全局模式。 testDir: './tests', // 匹配所有测试文件的最大超时时间(毫秒)。 timeout: 30 * 1000, // 每个测试用例的默认超时时间(毫秒)。 expect: { timeout: 5000 }, // 并行运行测试。可以设置为 false 以禁用,或指定最大并行工作进程数。 fullyParallel: true, // 在CI环境中,如果测试失败,则禁止重试,以便快速反馈。 forbidOnly: !!process.env.CI, // 失败测试的重试次数。在CI中设置得更高一些有助于稳定性。 retries: process.env.CI ? 2 : 0, // 并行运行测试的工作进程数。可以指定一个数字,或使用 '50%' 来使用一半的CPU核心。 workers: process.env.CI ? 1 : '50%', // 测试报告器:'html' 会生成漂亮的本地HTML报告。 reporter: 'html', // 项目级别的配置,允许你针对不同的浏览器或设备运行测试。 projects: [ { name: 'chromium', use: { ...devices['Desktop Chrome'] }, }, { name: 'firefox', use: { ...devices['Desktop Firefox'] }, }, { name: 'webkit', use: { ...devices['Desktop Safari'] }, }, ], // 全局运行测试前/后可以执行的脚本,常用于启动/停止服务器。 // webServer: { // command: 'npm run start', // url: 'http://127.0.0.1:3000', // reuseExistingServer: !process.env.CI, // }, // 最重要的部分:告诉Playwright如何运行你的TypeScript文件。 use: { // 基础URL,这样在测试中可以使用相对路径,如 `await page.goto('/admin');` baseURL: 'http://localhost:3000', // 收集执行失败时的追踪信息。on-first-retry'表示只在第一次重试时收集,以平衡信息量和性能。 trace: 'on-first-retry', // 收集执行失败时的截图。'only-on-failure'表示只在失败时截图。 screenshot: 'only-on-failure', // 收集执行失败时的视频。'retain-on-failure'表示失败时保留视频,成功则删除。 video: 'retain-on-failure', }, });4.2 关键配置项深度解析
testDir: ‘./tests’: 指定你的测试用例文件存放的目录。Playwright会递归查找这个目录下所有匹配.*(test|spec)\.(js|ts|mjs)模式的文件。你可以根据习惯改为./e2e或./specs。timeout与expect.timeout: 这是两个容易混淆的超时设置。timeout是针对整个测试文件的最大执行时间(从test.beforeAll到test.afterAll)。expect.timeout是针对单个断言(如expect(locator).toBeVisible())的等待时间。根据你的应用响应速度合理设置,本地调试可以设长些,CI环境可以设短些以快速失败。fullyParallel与workers: 这是Playwright强大的并发测试能力。fullyParallel: true允许同一个文件内的不同测试用例并行运行。workers控制并行的工作进程数。‘50%’意味着使用一半的CPU核心数。注意:并行测试要求测试用例之间是独立的,不能有共享状态(如依赖同一个全局变量)。在CI环境中,有时为了稳定性会设置为workers: 1。forbidOnly与retries:test.only()用于只运行一个测试,在提交代码时务必避免。forbidOnly: !!process.env.CI确保在CI环境中如果存在.only测试,则会失败。retries用于重试失败的测试,对于处理网络抖动或测试环境不稳定的“脆性测试”很有用。在CI中设置为2,可以增加通过率,但会掩盖真正的稳定性问题,需谨慎使用。projects: 这是Playwright配置的精华。它允许你定义多套测试环境。上面的例子定义了三个项目,分别针对三大浏览器。你可以轻松地只运行npx playwright test --project=chromium。你还可以在这里为移动端(‘iPhone 13’)、不同视口、不同权限(如地理位置)等定义不同的项目。use: 这里定义的选项会应用到所有测试中。baseURL非常实用,它允许你在测试中使用相对路径。trace,screenshot,video是强大的调试三件套。我强烈建议至少开启trace: ‘on-first-retry’和screenshot: ‘only-on-failure’。Trace文件可以在Playwright Trace Viewer (npx playwright show-trace)中可视化地回放整个测试操作,是排查“为什么点击没反应”这类问题的神器。
4.3 配置TypeScript支持上面的配置已经可以运行JS文件了,但对于TS文件,我们还需要关键一步:告诉Playwright使用ts-node(或其他加载器)来预处理.ts文件。这通常不需要在playwright.config.ts中显式设置,因为Playwright Test会自动检测到项目根目录存在tsconfig.json文件,并尝试使用ts-node/register来加载TS文件。
但是,为了更明确和应对一些复杂情况,我们可以在配置中指定加载器。修改或添加以下配置:
export default defineConfig({ // ... 其他配置 ... // 指定用于加载测试文件的加载器。对于TypeScript项目,ts-node是常见选择。 // 从Playwright 1.28版本开始,通常不需要显式设置,除非遇到问题。 // loader: { // '.ts': 'ts-node', // }, // 更重要的是,确保你的 tsconfig.json 配置正确。 });真正的关键在于tsconfig.json的配置。打开之前生成的tsconfig.json,进行如下关键调整:
{ "compilerOptions": { "target": "ES2022", // 或更新版本,匹配现代Node.js特性 "module": "commonjs", // Playwright Test 运行在Node.js环境,通常使用CommonJS "lib": ["ES2022", "DOM"], // 包含DOM类型定义,因为我们在测试中操作浏览器DOM "outDir": "./dist", // 编译输出目录(如果使用tsc编译) "rootDir": "./", // 源代码根目录 "strict": true, // 启用所有严格类型检查选项 "esModuleInterop": true, // 允许与CommonJS模块的互操作性 "skipLibCheck": true, // 跳过库文件的类型检查,加速编译 "forceConsistentCasingInFileNames": true, "resolveJsonModule": true // 允许导入JSON文件 }, "include": [ "tests/**/*.ts", // 包含所有测试文件 "playwright.config.ts" // 包含配置文件本身 ], "exclude": [ "node_modules", "dist" // 排除输出目录 ] }最重要的几项是“module”: “commonjs”和“lib”中包含“DOM”。commonjs确保编译后的模块系统与Node.js兼容。DOM库提供了window,document,HTMLElement等浏览器环境类型,这样你在写page.evaluate()等代码时就不会报类型错误。
5. 编写并运行第一个TypeScript测试脚本
配置完成后,让我们写一个简单的测试来验证一切是否正常。
5.1 创建测试目录和文件根据配置中的testDir: ‘./tests’,创建目录和测试文件:
mkdir tests touch tests/example.spec.ts5.2 编写测试用例打开tests/example.spec.ts,写入以下内容:
import { test, expect } from '@playwright/test'; // 一个简单的测试用例 test('has title', async ({ page }) => { // 导航到Playwright官网 await page.goto('https://playwright.dev/'); // 断言页面标题包含“Playwright” await expect(page).toHaveTitle(/Playwright/); }); // 另一个测试用例,演示定位器和交互 test('get started link', async ({ page }) => { await page.goto('https://playwright.dev/'); // 点击“Get started”链接 const getStarted = page.getByRole('link', { name: 'Get started' }); await getStarted.click(); // 断言URL包含“/docs/intro” await expect(page).toHaveURL(/.*intro/); });5.3 运行测试现在,在终端运行以下命令:
npx playwright test如果一切配置正确,你将看到Playwright启动浏览器(默认无头模式),运行两个测试,并在控制台输出结果。运行完成后,它会提示你HTML报告已生成,通常可以通过npx playwright show-report来打开一个漂亮的本地HTML报告,查看详细的测试结果、时间线、以及我们配置的截图和Trace(如果失败)。
5.4 带UI运行测试如果你想看着浏览器执行,可以添加--headed参数:
npx playwright test --headed5.5 运行特定浏览器如果你想只针对Chrome运行测试,可以使用--project参数:
npx playwright test --project=chromium6. 高级配置与优化技巧
基础配置能跑通,但要打造一个高效的测试工程,还需要一些进阶配置。
6.1 环境变量与多环境配置我们经常需要区分本地、测试、生产环境。环境变量是很好的方式。可以在playwright.config.ts中动态读取:
import { defineConfig, devices } from '@playwright/test'; // 从环境变量读取基础URL,默认为开发环境 const baseURL = process.env.BASE_URL || 'http://localhost:3000'; // 判断是否为CI环境 const isCI = !!process.env.CI; export default defineConfig({ use: { baseURL, trace: isCI ? 'on-first-retry' : 'on', screenshot: isCI ? 'only-on-failure' : 'on', }, retries: isCI ? 2 : 0, workers: isCI ? 1 : '50%', // ... 其他配置 });然后运行测试时传入环境变量:
BASE_URL=https://staging.example.com npx playwright test # 或者在CI脚本中设置 CI=true CI=true npx playwright test6.2 使用ESBuild提升执行速度ts-node在开发时很方便,但在CI中或需要更快启动时,esbuild是更快的选择。你需要安装@playwright/test的esbuild插件(如果版本支持)或手动配置。 首先安装esbuild和相关插件:
npm install --save-dev esbuild esbuild-register然后,在playwright.config.ts中配置加载器(注意:具体配置方式可能随Playwright版本变化,请查阅最新文档):
export default defineConfig({ // Playwright 1.40+ 版本可以这样配置 // build: { // // 使用esbuild打包测试文件(实验性功能) // // 这可以显著提升大型测试套件的启动速度 // }, // 或者通过loader配置(如果支持) // loader: { // '.ts': 'esbuild-register/register', // }, // ... 其他配置 });更常见的做法是在CI流水线中,先使用tsc或esbuild将整个项目编译成JavaScript,然后直接运行编译后的.js文件,这样可以完全避免运行时的编译开销。
6.3 配置全局Setup与Teardown有些操作需要在所有测试运行前/后执行一次,比如登录获取认证token、初始化数据库、启动本地服务器。这可以通过globalSetup和globalTeardown选项实现。 首先,创建一个全局设置文件,例如tests/global-setup.ts:
// tests/global-setup.ts import { FullConfig } from '@playwright/test'; async function globalSetup(config: FullConfig) { // 例如:从环境变量或外部服务获取一个认证令牌 const token = process.env.AUTH_TOKEN; // 将这个令牌存储起来,供所有测试项目使用 process.env.AUTH_TOKEN_FOR_TESTS = token; // 或者启动一个本地开发服务器 // const server = startServer(); // await server.ready(); // globalThis.__SERVER__ = server; } export default globalSetup;然后在playwright.config.ts中引用它:
export default defineConfig({ globalSetup: require.resolve('./tests/global-setup.ts'), // ... 其他配置 });globalTeardown的配置方式类似,用于清理资源。
6.4 配置Web Server如果你的测试依赖于一个本地开发服务器,Playwright可以帮你自动启动和停止。这在playwright.config.ts中非常方便:
export default defineConfig({ // ... 其他配置 webServer: { command: 'npm run dev', // 启动服务器的命令 url: 'http://localhost:3000', // 用于检测服务器是否就绪的URL timeout: 120 * 1000, // 启动超时时间(毫秒) reuseExistingServer: !process.env.CI, // 非CI环境下,如果服务器已在运行则复用 stdout: 'pipe', // 或 'ignore',将服务器日志输出到标准输出 stderr: 'pipe', }, });这样,当你运行npx playwright test时,Playwright会先执行npm run dev,并不断轮询http://localhost:3000直到收到成功响应(如200状态码),然后再开始运行测试。测试结束后,它会终止这个服务器进程(除非reuseExistingServer为true且服务器已存在)。
7. 常见问题排查与实战心得
即使配置正确,在实际操作中还是会遇到各种问题。这里记录一些高频问题和解决思路。
7.1 错误:Cannot find module ‘@playwright/test’ 或其类型声明
- 症状:在
*.spec.ts文件中,import { test, expect } from ‘@playwright/test’;这一行报红,提示找不到模块。 - 原因:
- 没有安装
@playwright/test包。运行npm list @playwright/test检查。 - TypeScript的
tsconfig.json中moduleResolution设置不正确。对于Node.js项目,应设置为“node”(这是“commonjs”模块下的默认值)。 - VS Code使用的TypeScript版本不是项目本地的。按下
Ctrl+Shift+P,输入“TypeScript: Select TypeScript Version”,选择“Use Workspace Version”。
- 没有安装
- 解决:
- 确保已运行
npm install --save-dev @playwright/test。 - 在
tsconfig.json中检查或添加“moduleResolution”: “node”。 - 重启VS Code的TypeScript语言服务。
- 确保已运行
7.2 错误:ts-node 未安装或配置失败
- 症状:运行
npx playwright test时,报错Cannot find module ‘ts-node’或类似加载错误。 - 原因:Playwright尝试用
ts-node加载.ts文件,但ts-node未安装或版本不兼容。 - 解决:
- 运行
npm install --save-dev ts-node@latest确保已安装。 - 如果问题依旧,可以尝试在
playwright.config.ts中显式禁用自带加载器,并使用@playwright/test的experimental的ct(组件测试)配置中的加载器,或者回退到先编译再运行的方式:在package.json中添加脚本“build:tests”: “tsc -p tsconfig.json”,然后运行npm run build:tests && npx playwright test dist/tests/。
- 运行
7.3 测试运行缓慢,尤其是首次运行
- 症状:第一次运行测试,或者清理缓存后运行,启动非常慢。
- 原因:
ts-node每次运行都需要编译TypeScript,有一定开销。- Playwright浏览器启动本身需要时间。
- 优化:
- 使用esbuild:如前所述,用
esbuild-register替代ts-node可以显著提升启动速度。 - 预热:在CI流水线中,可以添加一个预热步骤,先运行一个最简单的测试(如
test(‘warmup’, () => {})),让浏览器和编译缓存加载起来。 - 复用浏览器上下文:在测试中,如果测试用例是独立的,尽量复用
browser和context,而不是为每个测试都重新启动。Playwright Test的test.beforeAll和test.afterAll钩子可以帮助管理共享状态。
- 使用esbuild:如前所述,用
7.4 HTML报告中没有视频或Trace
- 症状:测试失败了,但HTML报告里看不到视频或Trace文件,或者提示文件找不到。
- 原因:
- 配置中未开启
video和trace选项,或开启的时机不对(如trace: ‘off’)。 - 输出目录
test-results被清理了。 - 路径配置问题。
- 配置中未开启
- 解决:
- 确保配置中至少设置了
trace: ‘on-first-retry’和video: ‘retain-on-failure’。 - 检查
playwright.config.ts中的outputDir配置(默认是test-results),确保该目录存在且可写。 - 运行测试时,使用
--trace on和--video on参数可以强制开启(会覆盖配置文件)。
- 确保配置中至少设置了
7.5 在CI/CD中运行Playwright TS测试的最佳实践在CI环境中,稳定性、速度和资源消耗是关键。
- 缓存:充分利用CI系统的缓存功能,缓存
node_modules、Playwright浏览器二进制文件(~/.cache/ms-playwright)以及可能的编译输出目录(如dist)。这能极大缩短流水线时间。 - 依赖安装:使用
npm ci代替npm install,它能根据package-lock.json提供确定性的安装,更快更可靠。 - 并行与分片:使用Playwright的
shard功能,可以将测试套件分割成多个分片,在多个CI机器上并行运行。命令如npx playwright test --shard=1/3。 - 使用官方Docker镜像:Playwright提供了官方的Docker镜像(如
mcr.microsoft.com/playwright),里面已经预装了所有依赖和浏览器,可以避免环境不一致问题。 - 资源清理:CI任务结束后,确保清理掉生成的
test-results、playwright-report等目录,除非你需要归档它们。
7.6 一个实用的、功能齐全的配置模板最后,分享一个我经过多个项目锤炼后,觉得比较实用的playwright.config.ts模板。它集成了环境变量、多项目、Web服务器、报告和性能优化考量。
import { defineConfig, devices } from '@playwright/test'; import path from 'path'; // 从环境变量读取配置,提供默认值 const baseURL = process.env.PLAYWRIGHT_TEST_BASE_URL || 'http://localhost:3000'; const isCI = !!process.env.CI; const isHeadless = process.env.HEADLESS !== 'false'; // 默认无头,可通过 HEADLESS=false 覆盖 const traceMode = process.env.TRACE_MODE || (isCI ? 'on-first-retry' : 'on'); const videoMode = process.env.VIDEO_MODE || (isCI ? 'retain-on-failure' : 'off'); export default defineConfig({ testDir: './tests', outputDir: './test-results', timeout: 60 * 1000, // 全局超时设长一些,应对复杂场景 expect: { timeout: 10000 }, // 单个断言超时10秒 fullyParallel: true, // 充分利用并行 forbidOnly: isCI, // CI环境下禁止 test.only retries: isCI ? 2 : 1, // CI下重试2次,本地重试1次(帮助发现脆性测试) workers: isCI ? 2 : '75%', // CI下固定2个worker,本地用75%的CPU核心 reporter: [ ['list'], // 简洁的控制台输出 ['html', { outputFolder: 'playwright-report', open: 'never' }], // 生成HTML报告,不自动打开 isCI ? ['blob'] : [], // CI环境下生成blob报告,可用于上传到Playwright云服务 ], use: { baseURL, trace: traceMode, screenshot: 'only-on-failure', video: videoMode as 'off' | 'on' | 'retain-on-failure', // 类型断言 actionTimeout: 15000, // 每个操作(click, fill)的超时 navigationTimeout: 30000, // 页面导航的超时 viewport: { width: 1920, height: 1080 }, // 统一的视口大小 ignoreHTTPSErrors: true, // 忽略HTTPS证书错误,便于测试内部环境 }, projects: [ // 桌面端 Chrome 测试 { name: 'chromium', use: { ...devices['Desktop Chrome'], channel: 'chrome', // 使用系统安装的Chrome,而非Chromium }, }, // 桌面端 Firefox 测试 { name: 'firefox', use: { ...devices['Desktop Firefox'] }, // 可以针对特定项目设置不同的重试策略或超时 // retries: 0, }, // 移动端 Chrome 测试 (模拟) { name: 'Mobile Chrome', use: { ...devices['Pixel 5'] }, }, // 一个专门用于API测试的项目,不需要浏览器 { name: 'api', testMatch: /.*\.api\.spec\.ts/, // 只运行以 .api.spec.ts 结尾的文件 use: { baseURL: process.env.API_BASE_URL || baseURL, }, }, ], // 本地开发服务器 webServer: !isCI ? { command: 'npm run dev', url: baseURL, timeout: 120 * 1000, reuseExistingServer: true, stdout: 'pipe', stderr: 'pipe', } : undefined, });这个模板提供了良好的默认值,并通过环境变量实现了高度的可配置性。你可以将它复制到你的项目中,根据实际情况微调,它应该能覆盖绝大多数Web自动化测试场景的需求。记住,配置不是一成不变的,随着项目复杂度的增加,你需要持续调整和优化它。