本文介绍如何在 playwright 测试配置中实现 web 服务端口的自动分配,避免 azure pipelines 等共享 ci 环境中因固定端口(如 3000)被占用导致的启动失败问题。
本文介绍如何在 playwright 测试配置中实现 web 服务端口的自动分配,避免 azure pipelines 等共享 ci 环境中因固定端口(如 3000)被占用导致的启动失败问题。
在 CI/CD 环境(如 Azure Pipelines)中运行 Playwright 测试时,webServer.port 若硬编码为 3000,极易与其他并行任务或残留进程发生端口冲突,导致 npm run start 启动失败、测试超时中断。虽然可手动轮换端口号(如 3001、3002),但该方案不可扩展且无法彻底规避竞争条件。更健壮的解法是让 Playwright 启动前动态获取一个可用端口,并确保前端开发服务器(如 Webpack Dev Server、Vite、Next.js)同步使用该端口。
Playwright 本身不原生支持 port: "auto" 或端口探测机制,但可通过环境变量 + 预启动逻辑实现等效效果。核心思路是:在 playwright.config.ts 加载阶段,检测当前是否处于 CI 环境,并动态生成一个高范围随机端口(如 10000–30000),将其注入 process.env.PORT 和 BASE_URL,再交由 webServer.command 启动的服务自动读取。
以下为推荐实践(兼容 Webpack、Vite、Create React App 等主流工具):
✅ 正确配置 playwright.config.ts
import { defineConfig } from '@playwright/test';
// 动态分配端口:CI 环境用随机端口,本地开发用固定端口(便于调试)
if (!process.env.BASE_URL) {
const portRange = { min: 10000, max: 30000 };
const port = process.env.CI
? Math.floor(Math.random() * (portRange.max - portRange.min) + portRange.min)
: 8088;
process.env.BASE_URL = `http://localhost:${port}`;
process.env.PORT = String(port);
}
export default defineConfig({
webServer: {
command: 'npm run start', // 依赖脚本需能读取 PORT 环境变量
timeout: 120 * 1000,
reuseExistingServer: !process.env.CI, // 本地可复用已启动服务;CI 必须新建
},
use: {
baseURL: process.env.BASE_URL,
},
});
✅ 前端开发服务器需支持 PORT 环境变量(关键!)
- Webpack Dev Server:默认支持 PORT 环境变量,无需修改配置。
- Vite:开箱即用,vite dev 自动读取 PORT。
- Create React App:react-scripts start 原生支持 PORT 变量。
- 自定义脚本(如 node server.js):确保代码中读取 process.env.PORT,例如:
// server.js 示例
const port = process.env.PORT || 3000;
app.listen(port, () => {
console.log(`Server running on http://localhost:${port}`);
});
⚠️ 注意事项与最佳实践
- 端口范围选择:建议避开系统保留端口(
- CI 环境标识:process.env.CI 是通用约定(Azure Pipelines、GitHub Actions、GitLab CI 均自动设置),若使用其他平台,请替换为对应环境变量(如 process.env.AZURE_PIPELINES)。
- 本地复用优化:启用 reuseExistingServer: true(仅限非 CI)可加速本地开发迭代,避免每次测试都重启服务。
- 端口探测进阶方案(可选):如需 100% 确保端口空闲,可引入 get-port 库替代随机数:
npm install --save-dev get-port
import getPort from 'get-port';
if (!process.env.BASE_URL) {
const port = process.env.CI
? await getPort({ port: getPort.makeRange(10000, 30000) })
: 8088;
process.env.BASE_URL = `http://localhost:${port}`;
process.env.PORT = String(port);
}
? 提示:get-port 方案虽更严谨,但在绝大多数 CI 场景下,随机端口已足够可靠,且避免了异步配置加载的复杂性(playwright.config.ts 默认同步执行)。
通过以上配置,你将彻底告别 Error: listen EADDRINUSE: address already in use :::3000,实现跨环境一致、稳定、可伸缩的 Playwright 测试基础设施。










