共计 3007 个字符,预计需要花费 8 分钟才能阅读完成。
从单上下文困境到 MCP 破局
最近在电商促销活动的测试中,我们的 Playwright 脚本遇到了棘手问题:当同时运行购物车结算和优惠券验证测试时,前者的登录状态总会意外覆盖后者。更糟的是,200 个测试用例串行执行需要 47 分钟——这远超出 CI/CD 流水线的时限。

// 典型问题复现代码
const browser = await chromium.launch();
const context = await browser.newContext();
// 测试 A 修改了全局 Cookie
const pageA = await context.newPage();
await pageA.goto('/login');
await pageA.fill('#username', 'testA');
// 测试 B 因此失败
const pageB = await context.newPage();
await pageB.goto('/profile'); // 此处预期是未登录状态
浏览器上下文隔离机制解析
通过 Chrome DevTools 协议可以看到,单个 Browser 实例实际上可以创建多个完全隔离的执行环境:
flowchart TD
Browser-->| 创建 |Context1
Browser-->| 创建 |Context2
Context1-->| 独立 |Page1
Context1-->| 独立 |Page2
Context2-->| 独立 |Page3
每个 Context 拥有独立的:
– Cookie 和 LocalStorage
– 网络请求缓存
– JavaScript 执行环境
MCP 核心实现方案
上下文工厂类(带错误处理)
/**
* 创建具备异常恢复能力的浏览器上下文
* @param browser - Playwright 浏览器实例
* @param config - 上下文配置项
*/
class ContextFactory {
static async createContext(
browser: Browser,
config: ContextConfig
): Promise<BrowserContext> {
const retryOptions = {
attempts: 3,
delay: 1000
};
return retry(async () => {
const context = await browser.newContext({viewport: { width: 1920, height: 1080},
...config
});
// 注册自动清理钩子
context.on('close', () => {console.log(`Context ${context} released`);
});
return context;
}, retryOptions);
}
}
interface ContextConfig {
userAgent?: string;
locale?: string;
permissions?: string[];}
并行测试执行器
// 启动 4 个并行测试上下文
const testRunners = Array(4).fill(null).map(async (_, idx) => {
const context = await ContextFactory.createContext(browser, {userAgent: `MCP-Runner-${idx}`
});
const testCase = new TestCase(context);
return testCase.run();});
// 错误不会跨上下文传播
const results = await Promise.allSettled(testRunners);
跨上下文通信方案
/**
* 使用 Window.postMessage 实现安全通信
* @param source - 发送方 Page 实例
* @param target - 接收方 Page 实例
* @param message - 结构化消息体
*/
function postCrossContextMessage(
source: Page,
target: Page,
message: CrossContextMessage
): Promise<void> {return source.evaluate(([target, msg]) => {target.window.postMessage(msg, '*');
}, [target, message]);
}
interface CrossContextMessage {
type: 'DATA_SYNC' | 'STATE_UPDATE';
payload: unknown;
timestamp: number;
}
性能优化实战数据
在 AWS c5.xlarge 实例上的测试结果:
| 模式 | 测试用例数 | 总耗时 | CPU 使用率 | 内存峰值 |
|---|---|---|---|---|
| 单上下文 | 200 | 47min | 35% | 1.2GB |
| MCP(4 并发) | 200 | 29min | 72% | 2.8GB |
| MCP(8 并发) | 200 | 22min | 89% | 4.1GB |
内存泄漏检测方案:
// 在 jest 环境中的检测示例
afterEach(async () => {
const leaks = await Promise.all(
contexts.map(ctx =>
ctx.pages().then(pages => ({
context: ctx,
leakedPages: pages.length
}))
)
);
leaks.forEach(({context, leakedPages}) => {if (leakedPages > 0) {console.error(`Context ${context} 泄露了 ${leakedPages} 个页面 `);
}
});
});
生产环境关键策略
-
Cookie 隔离:为每个测试用户创建独立上下文
// 复用登录状态但不污染全局 const authContext = await browser.newContext(); await login(authContext); const cookies = await authContext.cookies(); // 派生测试上下文 const testContext = await browser.newContext(); await testContext.addCookies(cookies); -
视频录制优化:
- 仅对失败用例开启录制
-
使用
saveAs替代默认临时文件context.route('**/*.mp4', route => {if (process.env.RECORD_FAILED_ONLY) {return route.abort(); } route.continue();}); -
CI/CD 实践:
- 根据
navigator.hardwareConcurrency动态设置并发数 - 使用 Docker 镜像预装浏览器
FROM mcr.microsoft.com/playwright:v1.25.0 RUN npx playwright install-deps
未解决的挑战
在 8 并行上下文时,我们观察到 Chrome 进程出现明显的调度延迟。通过 chrome://tracing 分析发现,当超过 vCPU 核心数时,GPU 进程的等待时间增长了 300%。这引出一个深层问题:如何建立上下文数量与硬件资源的动态平衡模型? 我们正在尝试用以下公式进行动态调整:
max_contexts = Math.floor((CPU_CORES * 0.8) /
(MEMORY_PER_CONTEXT / TOTAL_MEM)
);
期待社区能分享更多实战数据来优化这个算法。
正文完
发表至: 技术分享
近一天内
