首页
/ Playwright 官方迁移指南:从 Puppeteer 切换到 Playwright 的 API 对照、逐行改写与最佳实践

Playwright 官方迁移指南:从 Puppeteer 切换到 Playwright 的 API 对照、逐行改写与最佳实践

2026-09-06 13:59:02作者:邓越浪Henry

本文基于 Playwright 仓库中的官方迁移文档 puppeteer-js.md 整理,覆盖从 Puppeteer 迁移到 Playwright Library 与 Playwright Test 的核心原则、完整的 API 对照表,以及自动化脚本和测试用例两类场景的逐行改写方法。读完本文后,你可以直接对照 Cheat Sheet 完成存量 Puppeteer 代码的迁移,并理解 Playwright 的自动等待(auto-waiting)、严格模式 Locator 与 web-first 断言在源码层面是如何工作的。

一、迁移原则:四条核心准则

Playwright 与 Puppeteer 的 API 有诸多相似之处,但 Playwright 在 Web 测试与跨浏览器自动化方面提供了更大的能力空间。官方迁移文档给出的迁移原则如下:

  1. 大多数 Puppeteer API 可以原样使用——两个框架的 API 命名与调用风格高度接近,迁移成本主要集中在少数差异点上;
  2. 不推荐使用 ElementHandle——应改用 Locator 对象和 web-first 断言(Assertions);
  3. Playwright 是跨浏览器的——Chromium、Firefox、WebKit 三种引擎共用同一套 API;
  4. 你大概率不再需要显式等待——Playwright 的自动等待机制会让许多 waitForNavigationwaitForSelector 调用变得多余。

这四条原则贯穿后文所有示例,也是评估存量代码是否值得迁移的判断依据。

二、API Cheat Sheet:Puppeteer 到 Playwright 的完整对照表

官方文档提供了一张完整的 API 映射表,是迁移工作中最高频的参考资料。下表完整保留原文档的全部映射关系:

Puppeteer Playwright Library
await puppeteer.launch() await playwright.chromium.launch()
puppeteer.launch({product: 'firefox'}) await playwright.firefox.launch()
WebKit is not supported by Puppeteer await playwright.webkit.launch()
await browser.createIncognitoBrowserContext(...) await browser.newContext(...)
await page.setViewport(...) await page.setViewportSize(...)
await page.waitForXPath(XPathSelector) await page.waitForSelector(XPathSelector)
await page.waitForNetworkIdle(...) await page.waitForLoadState('networkidle')
await page.$eval(...) Assertions can often be used instead to verify text, attribute, class...
await page.$(...) Discouraged, use Locators instead
await page.$x(xpath_selector) Discouraged, use Locators instead
No methods dedicated to checkbox or radio input await page.locator(selector).check()
await page.locator(selector).uncheck()
await page.click(selector) await page.locator(selector).click()
await page.focus(selector) await page.locator(selector).focus()
await page.hover(selector) await page.locator(selector).hover()
await page.select(selector, values) await page.locator(selector).selectOption(values)
await page.tap(selector) await page.locator(selector).tap()
await page.type(selector, ...) await page.locator(selector).fill(...)
await page.waitForFileChooser(...)
await elementHandle.uploadFile(...)
await page.locator(selector).setInputFiles(...)
await page.cookies([...urls]) await browserContext.cookies([urls])
await page.deleteCookie(...cookies) await browserContext.clearCookies()
await page.setCookie(...cookies) await browserContext.addCookies(cookies)
page.on(...) page.on(...)
In order to intercept and mutate requests, see Page.route

对照表之外,官方还补充了三点关键说明:

  • page.waitForNavigationpage.waitForSelector 在 Playwright 中依然存在,但由于自动等待机制,很多场景下它们已不再必要;
  • ElementHandle 的使用被不推荐,应使用 Locator 对象和 web-first 断言替代;
  • Locator 是 Playwright 自动等待与可重试能力(retry-ability)的核心构件,并且是"严格"(strict)的:所有隐含目标 DOM 元素的 Locator 操作,如果选择器匹配到多于一个元素,都会抛出异常。这一严格性并非文档空谈——从源码看,严格性校验发生在注入脚本层面,injectedScript.ts 中会生成形如 strict mode violation: <selector> resolved to N elements 的错误并列出全部冲突元素,帮助你精确定位问题选择器。

此外可以从源码确认表中几个高频 API 的真实实现位置:

  • page.setViewportSizepage.waitForLoadState 都在 page.ts 中定义,前者通过 channel 协议将视口尺寸下发到浏览器端;
  • locator.check()locator.uncheck()locator.setInputFiles() 分别实现在 locator.ts 的对应方法中,即 Puppeteer 缺失的复选框/单选框专用方法与文件上传能力的落地位置;
  • 关于 waitForLoadState('networkidle')frame.ts 中的参数校验逻辑显示,合法的生命周期事件只有 loaddomcontentloadednetworkidlecommit 四种,并且源码显式将 Puppeteer 风格的 'networkidle0' 归一化为 'networkidle'——这意味着部分带后缀的旧写法也能被兼容处理。

三、自动化示例:逐行迁移

Puppeteer 原始代码

const puppeteer = require('puppeteer');

(async () => {
  const browser = await puppeteer.launch();
  const page = await browser.newPage();
  await page.setViewport({ width: 1280, height: 800 });
  await page.goto('https://playwright.dev/', {
    waitUntil: 'networkidle2',
  });
  await page.screenshot({ path: 'example.png' });
  await browser.close();
})();

逐行迁移到 Playwright

const { chromium } = require('playwright'); // 1

(async () => {
  const browser = await chromium.launch();
  const page = await browser.newPage(); // 2
  await page.setViewportSize({ width: 1280, height: 800 }); // 3
  await page.goto('https://playwright.dev/', {
    waitUntil: 'networkidle', // 4
  });
  await page.screenshot({ path: 'example.png' });
  await browser.close();
})();

迁移要点(对应代码内注释编号)

  1. 显式引入 chromium:Playwright Library 的每个文件都需要显式导入要使用的浏览器,也可以选择 webkitfirefox
  2. 浏览器状态隔离:如需隔离的浏览器状态,建议使用浏览器上下文browser.newContext());
  3. setViewport 更名为 setViewportSize,参数结构保持一致;
  4. networkidle2 更名为 networkidle:需要注意,得益于自动等待机制,大多数场景下使用 networkidle 并无实际收益,官方建议在多数情况下省略它。

四、测试示例:从 Jest + Puppeteer 到 Playwright Test

Puppeteer + Jest 原始代码

import puppeteer from 'puppeteer';

describe('Playwright homepage', () => {
  let browser;
  let page;

  beforeAll(async () => {
    browser = await puppeteer.launch();
    page = await browser.newPage();
  });

  it('contains hero title', async () => {
    await page.goto('https://playwright.dev/');
    await page.waitForSelector('.hero__title');
    const text = await page.$eval('.hero__title', e => e.textContent);
    expect(text).toContain('Playwright enables reliable end-to-end testing'); // 5
  });

  afterAll(() => browser.close());
});

逐行迁移到 Playwright Test

import { test, expect } from '@playwright/test'; // 1

test.describe('Playwright homepage', () => {
  test('contains hero title', async ({ page }) => { // 2, 3
    await page.goto('https://playwright.dev/');
    const titleLocator = page.locator('.hero__title'); // 4
    await expect(titleLocator).toContainText( // 5
        'Playwright enables reliable end-to-end testing'
    );
  });
});

迁移要点

  1. 显式导入 testexpect:每个 Playwright Test 文件都需要显式导入这两个函数;
  2. 测试函数标记为 async
  3. page 通过参数注入page 是 Playwright Test 众多实用 fixture之一。Playwright Test 会为每个测试创建独立的 Page 对象;若希望多个测试复用同一个 Page,可以在 Test.beforeAll 中自行创建、并在 Test.afterAll 中关闭;
  4. Locator 的创建是少数同步方法之一page.locator(selector) 立即返回 Locator 对象而不发起浏览器调用,真正的解析与等待发生在后续动作上;
  5. 用断言替代 page.$eval() 来验证状态:web-first 断言如 toContainText 自带自动等待与重试,比手动取值再断言更可靠也更易读。从源码看,toContainText / toHaveText 等匹配器在 matchers.ts 中实现,代码生成器(javascript.ts)在录制脚本时也会自动为文本类动作生成这类断言语句,说明"用断言替代 $eval"正是官方工具链的默认风格。

五、测试实践建议:Locator 与 Web-first 断言

官方文档"Testing"部分给出的建议值得单独强调:

  • 优先使用 Locator 与 web-first 断言,完整写法见写作测试指南
  • 在 Puppeteer 中,常用 page.evaluate()page.$eval() 检查 ElementHandle、提取文本内容、属性、class 等值。web-first 断言为此目的提供了多种匹配器,更可靠、可读性更好
  • Playwright Test 是官方第一方推荐的测试运行器,配套提供 Page Object Model、并行执行、fixture、reporter 等能力。

结合前文第二节的严格模式说明可以得出一个实操结论:迁移时遇到 page.$ / page.$x 这类返回 ElementHandle 的调用,替换为 page.locator(...) 不只是改名——它还引入了"选择器必须唯一匹配"的严格约束。从 injectedScript.ts 的错误信息结构看,一旦违反严格模式,报错会直接列出所有冲突元素,这比 Puppeteer 中"静默取第一个匹配元素"的行为更能提前暴露测试脚本中的歧义选择器。

六、迁移到 Playwright Test 后获得的能力

一旦完成迁移,除了 API 本身,还会获得一整套 Playwright Test 附带的生产能力:

  • 零配置的完整 TypeScript 支持
  • 在所有 Web 引擎上运行测试(Chrome、Firefox、Safari),覆盖主流操作系统(Windows、macOS、Ubuntu);
  • 完整支持多来源(multi-origin)、(i)frame、标签页与上下文(见 pages 文档);
  • 跨多个浏览器隔离并行运行测试
  • 内置的测试产物采集artifact collection),如截图、视频、trace 等。

此外还有与 Playwright Test 捆绑提供的工具:

七、延伸阅读

围绕本文的迁移主题,官方文档推荐的后续阅读材料(已转换为仓库根目录相对路径):

以上资源与本文的迁移原则、Cheat Sheet 配合使用,可以覆盖从 API 替换到测试体系重构的完整迁移路径。

登录后查看全文
热门项目推荐
相关项目推荐