Playwright技能进阶:MCP模式下的高效自动化测试实践

2次阅读
没有评论

共计 3007 个字符,预计需要花费 8 分钟才能阅读完成。

image.webp

从单上下文困境到 MCP 破局

最近在电商促销活动的测试中,我们的 Playwright 脚本遇到了棘手问题:当同时运行购物车结算和优惠券验证测试时,前者的登录状态总会意外覆盖后者。更糟的是,200 个测试用例串行执行需要 47 分钟——这远超出 CI/CD 流水线的时限。

Playwright 技能进阶:MCP 模式下的高效自动化测试实践

// 典型问题复现代码
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} 个页面 `);
    }
  });
});

生产环境关键策略

  1. Cookie 隔离:为每个测试用户创建独立上下文

    // 复用登录状态但不污染全局
    const authContext = await browser.newContext();
    await login(authContext);
    const cookies = await authContext.cookies();
    
    // 派生测试上下文
    const testContext = await browser.newContext();
    await testContext.addCookies(cookies);

  2. 视频录制优化

  3. 仅对失败用例开启录制
  4. 使用 saveAs 替代默认临时文件

    context.route('**/*.mp4', route => {if (process.env.RECORD_FAILED_ONLY) {return route.abort();
      }
      route.continue();});

  5. CI/CD 实践

  6. 根据 navigator.hardwareConcurrency 动态设置并发数
  7. 使用 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)
);

期待社区能分享更多实战数据来优化这个算法。

正文完
 0
评论(没有评论)