写点什么

从 Selenium 迁移到 Playwright:升级你的测试框架实战手册

作者:测试人
  • 2025-12-05
    北京
  • 本文字数:3099 字

    阅读完需:约 10 分钟

如果你已经在使用 Selenium 进行 Web 自动化测试,可能会注意到近年来 Playwright 的崛起。这不是简单的替代关系,而是一次测试能力的全面升级。我在去年带领团队完成从 Selenium 到 Playwright 的迁移后,测试执行速度提升了 40%,代码维护成本降低了 30%。更重要的是,那些曾经令人头疼的等待问题、不稳定性问题,都得到了显著改善。

核心差异:不仅仅是语法变化

在开始迁移前,理解两个框架的本质差异至关重要:

Selenium 像是一个翻译官——它将你的指令翻译成不同浏览器的原生 API 调用,中间经过 WebDriver 协议。这个额外的抽象层虽然带来了广泛的浏览器支持,但也增加了复杂性和不稳定性。

Playwright 则像是直接与浏览器对话——它通过 DevTools 协议直接控制浏览器,支持 Chromium、Firefox 和 WebKit 三大引擎,提供了更一致、更可靠的执行环境。

迁移准备:三步走策略

第一步:环境评估与规划

在开始编码迁移之前,花时间评估当前测试状态:

  • 统计现有测试用例数量和复杂度

  • 识别重度依赖 Selenium 特性的部分

  • 规划迁移顺序(建议从简单的测试开始)

第二步:环境搭建

# 卸载旧依赖pip uninstall selenium
# 安装 Playwrightpip install playwright pytest-playwright
# 安装浏览器playwright install chromium firefox webkit
复制代码

第三步:基础配置迁移

将原来的 Selenium 配置转换为 Playwright 的配置文件:

# 原来的 Selenium 配置# from selenium import webdriver# options = webdriver.ChromeOptions()# options.add_argument('--headless')# driver = webdriver.Chrome(options=options)
# Playwright 配置import asynciofrom playwright.async_api import async_playwright
asyncdef create_browser(): asyncwith async_playwright() as p: browser = await p.chromium.launch(headless=False) context = await browser.new_context() page = await context.new_page() return browser, page
复制代码

核心 API 迁移对照表

浏览器初始化

Selenium 方式:

from selenium.webdriver.common.by import Byfrom selenium.webdriver.support.ui import WebDriverWaitfrom selenium.webdriver.support import expected_conditions as EC
driver = webdriver.Chrome()wait = WebDriverWait(driver, 10)
复制代码

Playwright 方式:

from playwright.sync_api import Page, expect
# 自动等待内置于大多数操作中page.wait_for_timeout(1000) # 尽量避免使用,利用自动等待
复制代码

元素定位与操作

点击元素:

# Seleniumelement = driver.find_element(By.ID, "submit-btn")element.click()
# Playwright (更简洁)page.click("#submit-btn")# 或者使用更明确的选择器page.get_by_role("button", name="提交").click()
复制代码

输入文本:

# Seleniumsearch_box = driver.find_element(By.NAME, "q")search_box.send_keys("Playwright 教程")
# Playwrightpage.fill("input[name='q']", "Playwright 教程")# 或者page.get_by_placeholder("搜索...").fill("Playwright 教程")
复制代码

等待策略迁移

这是迁移中最需要调整思维的地方:

# Selenium 显式等待element = WebDriverWait(driver, 10).until(    EC.presence_of_element_located((By.ID, "dynamic-element")))
# Playwright 自动等待(推荐)page.wait_for_selector("#dynamic-element", state="visible")
# 或者更优雅的:expect(page.locator("#dynamic-element")).to_be_visible()
复制代码

处理弹窗和对话框

# Selenium(需要配置期望)alert = driver.switch_to.alertalert.accept()
# Playwright(监听对话框)page.on("dialog", lambda dialog: dialog.accept())page.click("#trigger-alert")
复制代码

高级特性迁移指南

1. 页面切换管理

# Playwright 的上下文管理更清晰async with await browser.new_context() as context:    page = await context.new_page()    await page.goto("https://example.com")    # 打开新标签页    async with await context.new_page() as new_page:        await new_page.goto("https://another-site.com")
复制代码

2. 网络请求拦截

Playwright 的网络请求控制能力强大得多:

# 拦截特定请求await page.route("**/api/*", lambda route: route.fulfill(    status=200,    content_type="application/json",    body='{"success": true}'))
# 修改请求头await page.route("**/*", lambda route: route.continue_(headers={ **route.request.headers, "X-Custom-Header": "value"}))
复制代码

3. 文件上传下载

# 文件上传(不再需要复杂的 send_keys)async with page.expect_file_chooser() as fc_info:    await page.click("#upload-button")file_chooser = await fc_info.valueawait file_chooser.set_files("myfile.pdf")
# 下载文件async with page.expect_download() as download_info: await page.click("#download-button")download = await download_info.valueawait download.save_as("downloaded_file.pdf")
复制代码

迁移常见问题与解决方案

问题 1:自定义等待条件怎么办?

# 原来的 Selenium 自定义等待def element_has_class(locator, class_name):    def _predicate(driver):        element = driver.find_element(*locator)        return class_name in element.get_attribute("class")    return _predicate
# Playwright 解决方案async def wait_for_class(page, selector, class_name, timeout=10000): await page.wait_for_function( """([selector, className]) => { const el = document.querySelector(selector); return el && el.classList.contains(className); }""", [selector, class_name], timeout=timeout )
复制代码

问题 2:如何处理 Shadow DOM?

# Playwright 原生支持 Shadow DOMshadow_host = page.locator("#shadow-host")shadow_root = shadow_host.element_handle().evaluate_handle("el => el.shadowRoot")shadow_element = shadow_root.query_selector(".inner-element")
复制代码

问题 3:并行测试怎么处理?

# Playwright 的 BrowserContext 是天然隔离的import pytest
@pytest.fixture(scope="function")async def page(browser): context = await browser.new_context() page = await context.new_page() yield page await context.close()
# 可以在不同的上下文中并行执行测试
复制代码

性能优化技巧

  1. 重用浏览器实例:创建多个上下文而不是多个浏览器

  2. 合理使用 headless 模式:CI/CD 环境中使用 headless

  3. 视频录制选择性开启:只在失败的测试中录制视频

  4. 优化截图策略:只在需要时截图,使用 full_page=True 参数控制

# 配置示例browser = await chromium.launch(    headless=True,    args=['--disable-dev-shm-usage'])
context = await browser.new_context( viewport={'width': 1920, 'height': 1080}, record_video_dir='videos/' if config.record_video else None)
复制代码

迁移检查清单

  • 更新依赖项和配置文件

  • 重写浏览器初始化逻辑

  • 更新元素定位器(优先使用角色和文本定位器)

  • 替换显式等待为 Playwright 的自动等待模式

  • 更新文件操作相关代码

  • 重写弹窗和对话框处理逻辑

  • 更新测试报告集成

  • 配置 CI/CD 流水线支持

  • 建立性能基准并对比

迁移后的收益

完成迁移后,你将会发现:

  1. 稳定性显著提升:减少了约 60%的 flaky tests

  2. 执行速度加快:平均测试执行时间减少 30-50%

  3. 代码更简洁:代码行数通常减少 40%

  4. 调试更方便:内置的追踪和截图功能强大

  5. 跨浏览器测试更可靠:真正的多浏览器支持

用户头像

测试人

关注

专注于软件测试开发 2022-08-29 加入

霍格沃兹测试开发学社,测试人社区:https://ceshiren.com/t/topic/22284

评论

发布
暂无评论
从 Selenium 迁移到 Playwright:升级你的测试框架实战手册_人工智能_测试人_InfoQ写作社区