1. 项目概述为什么是Playwright如果你正在为UI自动化测试的稳定性、跨浏览器兼容性或者维护成本而头疼那么今天聊的这个工具很可能就是你的“解药”。我说的就是Playwright一个由微软开源近年来在自动化测试领域异军突起的框架。它不像Selenium那样“年事已高”背负着历史包袱也不像一些轻量级工具功能单一。Playwright从设计之初就瞄准了现代Web应用测试的痛点多浏览器支持、自动等待、网络拦截、移动端模拟甚至是无头模式下的高性能截图和录屏。简单来说它试图让你用一套脚本就能在Chromium、Firefox和WebKitSafari的内核三大浏览器引擎上稳定运行这本身就解决了测试工程师一大半的跨平台兼容性焦虑。我第一次接触Playwright是在一个需要频繁回归测试大型单页应用SPA的项目里。当时用的框架光是处理页面元素的异步加载和动态渲染就写满了各种Thread.sleep和复杂的显式等待脚本又脆又慢。换成Playwright后最直观的感受就是“稳”和“快”。它的自动等待机制能智能地等到元素可操作、网络请求完成脚本的健壮性直线上升。而且它提供的录制生成代码、调试工具链让编写和维护测试用例的门槛降低了不少。无论你是刚入门自动化测试的新手还是被老框架折磨已久的资深QAPlaywright都值得你花时间深入了解。这篇教程我就结合自己踩过的坑和实战经验带你从零开始掌握这个强大的工具。2. 核心设计理念与架构解析2.1 与Selenium的根本性差异很多人会把Playwright和Selenium放在一起比较这很正常因为它们都用于浏览器自动化。但理解它们底层的不同是决定技术选型的关键。Selenium的核心是WebDriver协议这是一个W3C标准。它通过一个中间代理WebDriver向浏览器发送命令。这种架构的优点是标准化但缺点也明显速度相对较慢因为多了一层通信对现代浏览器新特性的支持有滞后性需要等待浏览器厂商更新WebDriver实现在处理iframe、弹窗、文件上传等场景时配置较为繁琐。Playwright走了另一条路。它直接通过开发者工具协议如Chrome DevTools Protocol与浏览器内核通信。你可以把它想象成给浏览器装了一个“遥控器”这个遥控器的指令更底层、更直接。带来的好处是执行速度更快减少了协议转换的开销。能力更强大可以轻松实现网络请求拦截与修改、模拟地理位置、设备传感器、触摸事件等Selenium难以实现或实现起来很复杂的功能。自动等待内置Playwright的API设计默认就是“智能”的像click(),fill()这些操作内部会等待元素可交互大大减少了编写显式等待代码的需要。浏览器上下文隔离这是Playwright一个非常棒的设计。每个“浏览器上下文”就像是一个独立的浏览器会话拥有独立的cookies、localStorage互不干扰。这使得并行测试、模拟多用户登录场景变得异常简单和干净。2.2 多语言支持与统一APIPlaywright提供了对Node.jsJavaScript/TypeScript、Python、Java和.NETC#的官方支持。这不仅仅是简单的语言绑定其API在不同语言间保持了高度的一致性。这意味着你学会了一种语言的Playwright用法几乎可以无缝切换到另一种语言。这对于拥有多技术栈团队的公司来说降低了学习和协作成本。官方对TypeScript的支持尤其友好提供了完善的类型定义结合现代IDE编码体验和代码健壮性都很好。2.3 核心组件Browser、Context、Page理解这三个核心对象的关系是玩转Playwright的基础。它们是一个层层包含的关系Browser代表一个浏览器实例。你可以把它想象成你双击打开的那个Chrome或Firefox应用程序。启动浏览器是资源消耗最大的操作。Context浏览器上下文。这是Playwright的精华所在。一个Browser下可以创建多个独立的Context。每个Context都拥有独立的会话状态如cookies、缓存、权限设置如地理位置、通知和视图尺寸。它非常轻量创建和销毁的成本远低于Browser。大多数测试场景中我们会在一个Browser实例上创建多个Context来实现测试隔离和并行。Page标签页。一个Context下可以有一个或多个Page对应浏览器中的一个标签页。我们绝大部分的页面操作如导航、元素定位、点击、输入都是在Page对象上进行的。这种设计带来了极大的灵活性。例如你可以用一个Browser创建两个Context一个模拟桌面端用户一个模拟移动端用户然后在各自的Context里进行测试互不影响。3. 环境搭建与项目初始化实战3.1 安装Playwright这里以最流行的Python和Node.js环境为例。无论哪种都推荐使用虚拟环境Python的venv或Node.js的项目目录来管理依赖避免全局污染。Python环境# 1. 使用pip安装playwright库 pip install playwright # 2. 安装Playwright所需的浏览器内核Chromium, Firefox, WebKit playwright installplaywright install这个命令会下载所有支持的浏览器到本地缓存中。如果你只想安装特定的浏览器可以使用playwright install chromium或playwright install firefox。Node.js环境# 1. 初始化npm项目如果还没有package.json npm init -y # 2. 安装Playwright npm install playwright # 3. 安装浏览器Node.js环境下通常安装库时会自动触发浏览器安装 # 如果需要手动安装或验证可以运行 npx playwright install注意浏览器下载可能需要一些时间并且占用约1GB的磁盘空间如果安装全部三个。在国内网络环境下可能会比较慢或失败。可以尝试设置环境变量来使用国内镜像加速例如对于playwright install可以尝试设置PLAYWRIGHT_DOWNLOAD_HOST环境变量。具体镜像地址需要根据当时可用的资源寻找。3.2 编写第一个测试脚本让我们从一个最简单的例子开始打开百度搜索“Playwright”。这里用Python示例Node.js的API几乎一模一样。import asyncio from playwright.async_api import async_playwright async def main(): # 启动Playwright它负责管理浏览器进程 async with async_playwright() as p: # 启动一个Chromium浏览器实例headlessFalse表示显示浏览器界面 browser await p.chromium.launch(headlessFalse) # 创建一个新的浏览器上下文 context await browser.new_context() # 创建一个新的页面标签页 page await context.new_page() # 导航到百度 await page.goto(https://www.baidu.com) # 定位搜索输入框并输入“Playwright” await page.fill(input#kw, Playwright) # 点击“百度一下”按钮 await page.click(input#su) # 等待页面导航完成到搜索结果页 await page.wait_for_load_state(networkidle) # 截图保存用于验证或报告 await page.screenshot(pathbaidu_search.png) # 关闭浏览器 await browser.close() # 运行异步函数 asyncio.run(main())这个脚本完成了打开浏览器、访问页面、交互、等待和截图的全过程。注意我们使用了异步API (async/await)。Playwright强烈推荐使用异步模式因为它能更好地处理大量并发的IO操作如网络请求提升测试执行效率。当然它也提供了同步API如果你不熟悉异步编程可以使用from playwright.sync_api import sync_playwright。3.3 使用Codegen录制脚本对于初学者或者快速生成测试用例原型Playwright内置的代码生成器Codegen是一个神器。它能把你在浏览器里的操作实时转换成代码。# 打开代码生成器和浏览器 playwright codegen https://www.baidu.com执行上述命令后会弹出一个浏览器窗口和一个代码录制窗口。你在浏览器里的所有点击、输入、跳转操作都会实时在右侧生成对应语言的代码Python、Java、C#、JavaScript。你可以直接复制这些代码到你的测试项目中。虽然生成的代码可能不够优化比如选择器可能不够稳健但它是一个绝佳的起点能帮你快速理解API的使用。4. 核心API与最佳实践详解4.1 元素定位策略稳字当头元素定位是UI自动化的基石不稳定的定位器是测试脚本脆弱的首要原因。Playwright提供了多种定位方式核心思想是优先使用用户可见的属性。文本定位 (text): 这是Playwright非常推荐的方式尤其是对于有唯一文本的按钮、链接。await page.click(text登录) # 点击页面上文本为“登录”的元素 await page.click(text/^Log\s*in$/i) # 使用正则表达式匹配CSS选择器 XPath: 与传统方式一致。但Playwright建议如果要用CSS选择器尽量结合有意义的属性如>await page.click(#submit-button) # ID选择器 await page.click(.btn-primary) # 类选择器 await page.click(//button[namesubmit]) # XPathRole定位 (role): 基于ARIA角色定位这是语义化且稳定的方式特别适合现代Web应用。await page.click(rolebutton[nameSubmit]) await page.fill(roletextbox[nameUsername], myuser)>await page.click([data-testidlogin-submit])实操心得尽量避免使用依赖于页面布局如:nth-child(3)或复杂CSS类名如.col-md-4 .form-control .input-lg的选择器因为它们极易因前端重构而失效。和前端团队约定使用># 这样写就够了Playwright会等待#element出现并可点击 await page.click(#element)如果你需要等待某个特定条件可以使用更明确的等待# 等待元素出现在DOM中 await page.wait_for_selector(#element) # 等待元素从DOM中消失 await page.wait_for_selector(#element, statehidden) # 等待网络请求完成 await page.wait_for_load_state(networkidle) # 等待到网络空闲500ms内无新请求 # 等待特定响应 async with page.expect_response(**/api/user) as response_info: await page.click(#fetch-user) response await response_info.value强制等待 (page.wait_for_timeout)是最后的手段仅在无法通过上述方式等待时使用因为它会让测试变慢且不可靠。4.3 处理弹窗、框架与多标签页弹窗Dialog: Playwright可以监听并处理alert,confirm,prompt。# 在触发弹窗的操作之前先监听 page.on(dialog, lambda dialog: dialog.accept()) # 自动接受确定 await page.click(#trigger-alert) # 点击触发alert的按钮 # 或者更精细的控制 page.on(dialog, lambda dialog: print(dialog.message))文件上传: 非常简单不需要像Selenium那样模拟键盘操作。# 假设有一个 input typefile await page.set_input_files(input[typefile], /path/to/myfile.pdf)iframe: 先定位到iframe元素然后获取其content_frame对象之后的操作都在这个frame对象上进行。frame page.frame(frame-name) # 通过name或URL # 或者 frame_element page.query_selector(iframe) frame await frame_element.content_frame() # 然后在frame内操作 await frame.click(button)新标签页/窗口: 通过监听popup事件来处理。async with page.expect_popup() as popup_info: await page.click(a[target_blank]) # 点击会打开新窗口的链接 new_page await popup_info.value await new_page.wait_for_load_state() # 在新页面操作 await new_page.close() # 操作完后关闭4.4 网络请求拦截与模拟这是Playwright的杀手级功能之一可以用于屏蔽不必要的资源如图片、样式表以加速测试。拦截并修改API请求和响应用于模拟后端数据或测试错误场景。记录网络流量用于分析或断言。# 1. 路由拦截请求 await page.route(**/*.{png,jpg,jpeg}, lambda route: route.abort()) # 拦截并中止所有图片请求 await page.route(**/api/user, lambda route: route.fulfill( status200, content_typeapplication/json, bodyjson.dumps({name: Mock User}) # 返回模拟数据 )) # 2. 监听请求/响应 page.on(request, lambda request: print(f {request.method} {request.url})) page.on(response, lambda response: print(f {response.status} {response.url})) # 3. 等待特定请求完成 async with page.expect_request(**/api/login) as request_info: await page.click(#login-btn) request await request_info.value print(request.post_data) # 查看发送的数据4.5 模拟移动设备与地理位置Playwright可以轻松模拟各种移动设备屏幕尺寸、User-Agent、触摸事件和传感器。# 使用预定义的设备描述符如iPhone 11 from playwright.sync_api import sync_playwright with sync_playwright() as p: # 模拟iPhone 11 iphone_11 p.devices[iPhone 11] browser p.chromium.launch(headlessFalse) # 创建上下文时传入设备参数 context browser.new_context(**iphone_11) page context.new_page() await page.goto(https://mobile.example.com) # 此时页面会以移动端视图打开并支持触摸事件 # 自定义上下文模拟地理位置和权限 context await browser.new_context( viewport{width: 375, height: 667}, user_agent自定义UA, localezh-CN, geolocation{longitude: 116.397, latitude: 39.916}, # 北京坐标 permissions[geolocation] # 授予地理位置权限 )5. 测试框架集成与项目组织单独写脚本可行但构建可维护的测试项目需要框架。Playwright Test是官方推荐的测试运行器它基于流行的测试框架如Jest, Mocha提供了专为Playwright优化的断言、夹具和报告功能。5.1 初始化Playwright Test项目# 在项目根目录执行会创建基础配置和示例测试 npx playwright init # 或对于Python playwright install --with-deps # 确保浏览器已安装 # 然后手动创建 pytest.ini 或 conftest.py 来配置对于Node.js初始化后会生成playwright.config.ts配置文件。对于Python你可以使用pytest并安装pytest-playwright插件。5.2 编写结构化测试用例Node.js Playwright Test示例// tests/example.spec.ts import { test, expect } from playwright/test; // 使用 test fixture 来自动管理 browser, context, page test.describe(百度搜索功能, () { test(应该能搜索到Playwright相关内容, async ({ page }) { // 1. 导航 await page.goto(https://www.baidu.com); // 2. 操作与断言 await expect(page).toHaveTitle(/百度/); // 断言标题包含“百度” await page.fill(input#kw, Playwright); await page.click(input#su); // 等待结果并断言 await expect(page.locator(textPlaywright: Fast and reliable)).toBeVisible(); // 或者使用更灵活的断言 const resultCount await page.locator(#content_left .result).count(); expect(resultCount).toBeGreaterThan(0); }); test(空搜索应提示输入关键词, async ({ page }) { await page.goto(https://www.baidu.com); await page.click(input#su); // 不输入直接点搜索 // 假设百度会有提示这里需要根据实际页面行为调整断言 // await expect(page.locator(text请输入搜索关键词)).toBeVisible(); }); });5.3 配置与运行playwright.config.ts是核心配置文件可以定义多个项目针对不同浏览器、设备、视口的测试配置。全局设置如基础URL、超时时间、截图/视频录制策略、是否headless。报告器生成HTML、JUnit、Allure等格式的报告。全局夹具如登录状态可以在所有测试开始前执行。一个典型的配置片段import { defineConfig, devices } from playwright/test; export default defineConfig({ timeout: 30000, // 每个测试的超时时间 retries: process.env.CI ? 2 : 0, // CI环境下失败重试2次 reporter: [[html, { outputFolder: playwright-report }], [list]], use: { baseURL: https://my-app.example.com, // 基础URL测试中可用相对路径 trace: on-first-retry, // 失败时记录追踪信息用于调试 screenshot: only-on-failure, }, projects: [ { name: chromium, use: { ...devices[Desktop Chrome] }, }, { name: firefox, use: { ...devices[Desktop Firefox] }, }, { name: webkit, use: { ...devices[Desktop Safari] }, }, { name: Mobile Chrome, use: { ...devices[Pixel 5] }, }, ], });运行测试npx playwright test # 运行所有测试 npx playwright test --projectchromium # 只运行Chromium项目 npx playwright test example.spec.ts # 运行特定文件 npx playwright test --grep 搜索 # 运行包含“搜索”的测试 npx playwright test --ui # 打开GUI模式运行和调试6. 高级技巧与性能优化6.1 并行测试与隔离利用Playwright的Browser Context实现轻量级并行和隔离是提升测试套件执行速度的关键。import asyncio from playwright.async_api import async_playwright async def run_test(user_credentials): async with async_playwright() as p: browser await p.chromium.launch() # 每个测试用例拥有自己独立的上下文互不干扰 context await browser.new_context() page await context.new_page() # ... 使用user_credentials登录并执行测试 ... await context.close() await browser.close() async def main(): users [{user:a}, {user:b}, {user:c}] tasks [run_test(user) for user in users] await asyncio.gather(*tasks) # 并行执行 asyncio.run(main())在Playwright Test框架中并行执行是默认行为除非用test.describe.serial且每个测试都有自己的Context天然隔离。6.2 使用追踪Tracing进行调试当测试在CI/CD中失败时光看日志很难定位问题。Playwright的追踪功能可以记录测试过程中的所有操作、网络请求、控制台日志并生成一个可视化的时间线报告。 在配置中启用trace: on-first-retry或trace: retain-on-failure。测试失败后会生成一个trace.zip文件。使用命令npx playwright show-trace trace.zip打开一个图形化界面你可以一步步回放测试过程查看每一步的截图、DOM状态和网络活动是调试复杂失败的利器。6.3 与CI/CD集成在CI环境中如GitHub Actions, GitLab CI, Jenkins通常以无头模式运行测试。关键点缓存浏览器避免每次构建都下载浏览器。CI工具一般有缓存机制可以缓存~/.cache/ms-playwright目录。安装依赖确保安装了所有系统依赖。Playwright提供了playwright install-deps命令来安装必要的系统库如字体、共享库。运行测试使用--headed参数可以在CI中启动有头浏览器需要配置虚拟显示服务器如Xvfb但无头模式是首选。处理失败配置重试机制如retries并收集追踪、截图和视频作为制品上传方便后续分析。一个简单的GitHub Actions配置示例name: Playwright Tests on: [push] jobs: test: runs-on: ubuntu-latest steps: - uses: actions/checkoutv3 - uses: actions/setup-nodev3 with: { node-version: 18 } - name: Cache playwright browsers uses: actions/cachev3 with: path: ~/.cache/ms-playwright key: ${{ runner.os }}-playwright-${{ hashFiles(package-lock.json) }} - run: npm ci - run: npx playwright install --with-deps chromium # 只安装需要的浏览器 - run: npx playwright test - uses: actions/upload-artifactv3 if: failure() with: name: playwright-report path: playwright-report/ retention-days: 77. 常见问题排查与避坑指南在实际项目中你肯定会遇到各种奇怪的问题。这里记录了一些高频问题的排查思路和解决方案。问题现象可能原因排查步骤与解决方案**元素找不到 (TimeoutError) **1. 选择器写错了或元素属性变了。2. 元素在iframe或shadow DOM里。3. 页面还没加载完或元素是动态渲染的。1. 使用Playwright Inspector (playwright open或--ui) 实时验证选择器。2. 检查元素是否在iframe内用page.frame切换。3. 检查是否在shadow root内用element.shadowRoot定位。4. 增加等待await page.wait_for_selector(selector, statevisible, timeout10000)。点击/输入没反应1. 元素被遮挡弹窗、其他元素。2. 元素不可交互disabled,readonly。3. 需要触发其他事件如focus。1. 截图查看当前页面状态确认元素可见。2. 检查元素属性await page.is_enabled(selector)。3. 尝试先hover再click或使用page.dispatch_event直接发送事件。测试在CI上失败本地却成功1. CI环境与本地环境差异网络、资源、数据。2. CI机器性能差超时时间不足。3. 浏览器版本不一致。1. 在CI配置中增加超时时间如timeout: 60000。2. 在CI运行中启用追踪和截图失败后下载分析。3. 确保CI缓存了正确的浏览器版本。4. 检查测试是否依赖本地数据或特定状态改为使用测试专用环境或Mock数据。文件上传失败1. 文件路径错误。2. 上传控件是自定义的非input typefile。1. 使用绝对路径或确认相对路径正确。2. 对于自定义控件可能需要先点击触发文件选择对话框然后使用page.on(filechooser, ...)监听并设置文件。网络请求拦截不生效1. 路由注册时机太晚请求已经发出。2. URL模式匹配不正确。1. 在page.goto或触发请求的操作之前调用page.route。2. 使用page.on(request)监听所有请求打印URL确认匹配模式。并行测试数据污染多个测试用例共享了同一个浏览器上下文Context或页面状态。严格遵守隔离原则每个独立测试用例或测试工作单元必须使用自己独立的BrowserContext。在Playwright Test中这是默认行为。如果手动管理确保为每个用例创建新的Context。避坑技巧一选择器稳定性不要依赖自动录制工具生成的基于索引如:nth-child(5)或复杂CSS路径的选择器。花时间与开发沟通为关键元素添加>
Playwright自动化测试:从核心原理到实战应用的全方位指南
发布时间:2026/6/18 21:47:54
1. 项目概述为什么是Playwright如果你正在为UI自动化测试的稳定性、跨浏览器兼容性或者维护成本而头疼那么今天聊的这个工具很可能就是你的“解药”。我说的就是Playwright一个由微软开源近年来在自动化测试领域异军突起的框架。它不像Selenium那样“年事已高”背负着历史包袱也不像一些轻量级工具功能单一。Playwright从设计之初就瞄准了现代Web应用测试的痛点多浏览器支持、自动等待、网络拦截、移动端模拟甚至是无头模式下的高性能截图和录屏。简单来说它试图让你用一套脚本就能在Chromium、Firefox和WebKitSafari的内核三大浏览器引擎上稳定运行这本身就解决了测试工程师一大半的跨平台兼容性焦虑。我第一次接触Playwright是在一个需要频繁回归测试大型单页应用SPA的项目里。当时用的框架光是处理页面元素的异步加载和动态渲染就写满了各种Thread.sleep和复杂的显式等待脚本又脆又慢。换成Playwright后最直观的感受就是“稳”和“快”。它的自动等待机制能智能地等到元素可操作、网络请求完成脚本的健壮性直线上升。而且它提供的录制生成代码、调试工具链让编写和维护测试用例的门槛降低了不少。无论你是刚入门自动化测试的新手还是被老框架折磨已久的资深QAPlaywright都值得你花时间深入了解。这篇教程我就结合自己踩过的坑和实战经验带你从零开始掌握这个强大的工具。2. 核心设计理念与架构解析2.1 与Selenium的根本性差异很多人会把Playwright和Selenium放在一起比较这很正常因为它们都用于浏览器自动化。但理解它们底层的不同是决定技术选型的关键。Selenium的核心是WebDriver协议这是一个W3C标准。它通过一个中间代理WebDriver向浏览器发送命令。这种架构的优点是标准化但缺点也明显速度相对较慢因为多了一层通信对现代浏览器新特性的支持有滞后性需要等待浏览器厂商更新WebDriver实现在处理iframe、弹窗、文件上传等场景时配置较为繁琐。Playwright走了另一条路。它直接通过开发者工具协议如Chrome DevTools Protocol与浏览器内核通信。你可以把它想象成给浏览器装了一个“遥控器”这个遥控器的指令更底层、更直接。带来的好处是执行速度更快减少了协议转换的开销。能力更强大可以轻松实现网络请求拦截与修改、模拟地理位置、设备传感器、触摸事件等Selenium难以实现或实现起来很复杂的功能。自动等待内置Playwright的API设计默认就是“智能”的像click(),fill()这些操作内部会等待元素可交互大大减少了编写显式等待代码的需要。浏览器上下文隔离这是Playwright一个非常棒的设计。每个“浏览器上下文”就像是一个独立的浏览器会话拥有独立的cookies、localStorage互不干扰。这使得并行测试、模拟多用户登录场景变得异常简单和干净。2.2 多语言支持与统一APIPlaywright提供了对Node.jsJavaScript/TypeScript、Python、Java和.NETC#的官方支持。这不仅仅是简单的语言绑定其API在不同语言间保持了高度的一致性。这意味着你学会了一种语言的Playwright用法几乎可以无缝切换到另一种语言。这对于拥有多技术栈团队的公司来说降低了学习和协作成本。官方对TypeScript的支持尤其友好提供了完善的类型定义结合现代IDE编码体验和代码健壮性都很好。2.3 核心组件Browser、Context、Page理解这三个核心对象的关系是玩转Playwright的基础。它们是一个层层包含的关系Browser代表一个浏览器实例。你可以把它想象成你双击打开的那个Chrome或Firefox应用程序。启动浏览器是资源消耗最大的操作。Context浏览器上下文。这是Playwright的精华所在。一个Browser下可以创建多个独立的Context。每个Context都拥有独立的会话状态如cookies、缓存、权限设置如地理位置、通知和视图尺寸。它非常轻量创建和销毁的成本远低于Browser。大多数测试场景中我们会在一个Browser实例上创建多个Context来实现测试隔离和并行。Page标签页。一个Context下可以有一个或多个Page对应浏览器中的一个标签页。我们绝大部分的页面操作如导航、元素定位、点击、输入都是在Page对象上进行的。这种设计带来了极大的灵活性。例如你可以用一个Browser创建两个Context一个模拟桌面端用户一个模拟移动端用户然后在各自的Context里进行测试互不影响。3. 环境搭建与项目初始化实战3.1 安装Playwright这里以最流行的Python和Node.js环境为例。无论哪种都推荐使用虚拟环境Python的venv或Node.js的项目目录来管理依赖避免全局污染。Python环境# 1. 使用pip安装playwright库 pip install playwright # 2. 安装Playwright所需的浏览器内核Chromium, Firefox, WebKit playwright installplaywright install这个命令会下载所有支持的浏览器到本地缓存中。如果你只想安装特定的浏览器可以使用playwright install chromium或playwright install firefox。Node.js环境# 1. 初始化npm项目如果还没有package.json npm init -y # 2. 安装Playwright npm install playwright # 3. 安装浏览器Node.js环境下通常安装库时会自动触发浏览器安装 # 如果需要手动安装或验证可以运行 npx playwright install注意浏览器下载可能需要一些时间并且占用约1GB的磁盘空间如果安装全部三个。在国内网络环境下可能会比较慢或失败。可以尝试设置环境变量来使用国内镜像加速例如对于playwright install可以尝试设置PLAYWRIGHT_DOWNLOAD_HOST环境变量。具体镜像地址需要根据当时可用的资源寻找。3.2 编写第一个测试脚本让我们从一个最简单的例子开始打开百度搜索“Playwright”。这里用Python示例Node.js的API几乎一模一样。import asyncio from playwright.async_api import async_playwright async def main(): # 启动Playwright它负责管理浏览器进程 async with async_playwright() as p: # 启动一个Chromium浏览器实例headlessFalse表示显示浏览器界面 browser await p.chromium.launch(headlessFalse) # 创建一个新的浏览器上下文 context await browser.new_context() # 创建一个新的页面标签页 page await context.new_page() # 导航到百度 await page.goto(https://www.baidu.com) # 定位搜索输入框并输入“Playwright” await page.fill(input#kw, Playwright) # 点击“百度一下”按钮 await page.click(input#su) # 等待页面导航完成到搜索结果页 await page.wait_for_load_state(networkidle) # 截图保存用于验证或报告 await page.screenshot(pathbaidu_search.png) # 关闭浏览器 await browser.close() # 运行异步函数 asyncio.run(main())这个脚本完成了打开浏览器、访问页面、交互、等待和截图的全过程。注意我们使用了异步API (async/await)。Playwright强烈推荐使用异步模式因为它能更好地处理大量并发的IO操作如网络请求提升测试执行效率。当然它也提供了同步API如果你不熟悉异步编程可以使用from playwright.sync_api import sync_playwright。3.3 使用Codegen录制脚本对于初学者或者快速生成测试用例原型Playwright内置的代码生成器Codegen是一个神器。它能把你在浏览器里的操作实时转换成代码。# 打开代码生成器和浏览器 playwright codegen https://www.baidu.com执行上述命令后会弹出一个浏览器窗口和一个代码录制窗口。你在浏览器里的所有点击、输入、跳转操作都会实时在右侧生成对应语言的代码Python、Java、C#、JavaScript。你可以直接复制这些代码到你的测试项目中。虽然生成的代码可能不够优化比如选择器可能不够稳健但它是一个绝佳的起点能帮你快速理解API的使用。4. 核心API与最佳实践详解4.1 元素定位策略稳字当头元素定位是UI自动化的基石不稳定的定位器是测试脚本脆弱的首要原因。Playwright提供了多种定位方式核心思想是优先使用用户可见的属性。文本定位 (text): 这是Playwright非常推荐的方式尤其是对于有唯一文本的按钮、链接。await page.click(text登录) # 点击页面上文本为“登录”的元素 await page.click(text/^Log\s*in$/i) # 使用正则表达式匹配CSS选择器 XPath: 与传统方式一致。但Playwright建议如果要用CSS选择器尽量结合有意义的属性如>await page.click(#submit-button) # ID选择器 await page.click(.btn-primary) # 类选择器 await page.click(//button[namesubmit]) # XPathRole定位 (role): 基于ARIA角色定位这是语义化且稳定的方式特别适合现代Web应用。await page.click(rolebutton[nameSubmit]) await page.fill(roletextbox[nameUsername], myuser)>await page.click([data-testidlogin-submit])实操心得尽量避免使用依赖于页面布局如:nth-child(3)或复杂CSS类名如.col-md-4 .form-control .input-lg的选择器因为它们极易因前端重构而失效。和前端团队约定使用># 这样写就够了Playwright会等待#element出现并可点击 await page.click(#element)如果你需要等待某个特定条件可以使用更明确的等待# 等待元素出现在DOM中 await page.wait_for_selector(#element) # 等待元素从DOM中消失 await page.wait_for_selector(#element, statehidden) # 等待网络请求完成 await page.wait_for_load_state(networkidle) # 等待到网络空闲500ms内无新请求 # 等待特定响应 async with page.expect_response(**/api/user) as response_info: await page.click(#fetch-user) response await response_info.value强制等待 (page.wait_for_timeout)是最后的手段仅在无法通过上述方式等待时使用因为它会让测试变慢且不可靠。4.3 处理弹窗、框架与多标签页弹窗Dialog: Playwright可以监听并处理alert,confirm,prompt。# 在触发弹窗的操作之前先监听 page.on(dialog, lambda dialog: dialog.accept()) # 自动接受确定 await page.click(#trigger-alert) # 点击触发alert的按钮 # 或者更精细的控制 page.on(dialog, lambda dialog: print(dialog.message))文件上传: 非常简单不需要像Selenium那样模拟键盘操作。# 假设有一个 input typefile await page.set_input_files(input[typefile], /path/to/myfile.pdf)iframe: 先定位到iframe元素然后获取其content_frame对象之后的操作都在这个frame对象上进行。frame page.frame(frame-name) # 通过name或URL # 或者 frame_element page.query_selector(iframe) frame await frame_element.content_frame() # 然后在frame内操作 await frame.click(button)新标签页/窗口: 通过监听popup事件来处理。async with page.expect_popup() as popup_info: await page.click(a[target_blank]) # 点击会打开新窗口的链接 new_page await popup_info.value await new_page.wait_for_load_state() # 在新页面操作 await new_page.close() # 操作完后关闭4.4 网络请求拦截与模拟这是Playwright的杀手级功能之一可以用于屏蔽不必要的资源如图片、样式表以加速测试。拦截并修改API请求和响应用于模拟后端数据或测试错误场景。记录网络流量用于分析或断言。# 1. 路由拦截请求 await page.route(**/*.{png,jpg,jpeg}, lambda route: route.abort()) # 拦截并中止所有图片请求 await page.route(**/api/user, lambda route: route.fulfill( status200, content_typeapplication/json, bodyjson.dumps({name: Mock User}) # 返回模拟数据 )) # 2. 监听请求/响应 page.on(request, lambda request: print(f {request.method} {request.url})) page.on(response, lambda response: print(f {response.status} {response.url})) # 3. 等待特定请求完成 async with page.expect_request(**/api/login) as request_info: await page.click(#login-btn) request await request_info.value print(request.post_data) # 查看发送的数据4.5 模拟移动设备与地理位置Playwright可以轻松模拟各种移动设备屏幕尺寸、User-Agent、触摸事件和传感器。# 使用预定义的设备描述符如iPhone 11 from playwright.sync_api import sync_playwright with sync_playwright() as p: # 模拟iPhone 11 iphone_11 p.devices[iPhone 11] browser p.chromium.launch(headlessFalse) # 创建上下文时传入设备参数 context browser.new_context(**iphone_11) page context.new_page() await page.goto(https://mobile.example.com) # 此时页面会以移动端视图打开并支持触摸事件 # 自定义上下文模拟地理位置和权限 context await browser.new_context( viewport{width: 375, height: 667}, user_agent自定义UA, localezh-CN, geolocation{longitude: 116.397, latitude: 39.916}, # 北京坐标 permissions[geolocation] # 授予地理位置权限 )5. 测试框架集成与项目组织单独写脚本可行但构建可维护的测试项目需要框架。Playwright Test是官方推荐的测试运行器它基于流行的测试框架如Jest, Mocha提供了专为Playwright优化的断言、夹具和报告功能。5.1 初始化Playwright Test项目# 在项目根目录执行会创建基础配置和示例测试 npx playwright init # 或对于Python playwright install --with-deps # 确保浏览器已安装 # 然后手动创建 pytest.ini 或 conftest.py 来配置对于Node.js初始化后会生成playwright.config.ts配置文件。对于Python你可以使用pytest并安装pytest-playwright插件。5.2 编写结构化测试用例Node.js Playwright Test示例// tests/example.spec.ts import { test, expect } from playwright/test; // 使用 test fixture 来自动管理 browser, context, page test.describe(百度搜索功能, () { test(应该能搜索到Playwright相关内容, async ({ page }) { // 1. 导航 await page.goto(https://www.baidu.com); // 2. 操作与断言 await expect(page).toHaveTitle(/百度/); // 断言标题包含“百度” await page.fill(input#kw, Playwright); await page.click(input#su); // 等待结果并断言 await expect(page.locator(textPlaywright: Fast and reliable)).toBeVisible(); // 或者使用更灵活的断言 const resultCount await page.locator(#content_left .result).count(); expect(resultCount).toBeGreaterThan(0); }); test(空搜索应提示输入关键词, async ({ page }) { await page.goto(https://www.baidu.com); await page.click(input#su); // 不输入直接点搜索 // 假设百度会有提示这里需要根据实际页面行为调整断言 // await expect(page.locator(text请输入搜索关键词)).toBeVisible(); }); });5.3 配置与运行playwright.config.ts是核心配置文件可以定义多个项目针对不同浏览器、设备、视口的测试配置。全局设置如基础URL、超时时间、截图/视频录制策略、是否headless。报告器生成HTML、JUnit、Allure等格式的报告。全局夹具如登录状态可以在所有测试开始前执行。一个典型的配置片段import { defineConfig, devices } from playwright/test; export default defineConfig({ timeout: 30000, // 每个测试的超时时间 retries: process.env.CI ? 2 : 0, // CI环境下失败重试2次 reporter: [[html, { outputFolder: playwright-report }], [list]], use: { baseURL: https://my-app.example.com, // 基础URL测试中可用相对路径 trace: on-first-retry, // 失败时记录追踪信息用于调试 screenshot: only-on-failure, }, projects: [ { name: chromium, use: { ...devices[Desktop Chrome] }, }, { name: firefox, use: { ...devices[Desktop Firefox] }, }, { name: webkit, use: { ...devices[Desktop Safari] }, }, { name: Mobile Chrome, use: { ...devices[Pixel 5] }, }, ], });运行测试npx playwright test # 运行所有测试 npx playwright test --projectchromium # 只运行Chromium项目 npx playwright test example.spec.ts # 运行特定文件 npx playwright test --grep 搜索 # 运行包含“搜索”的测试 npx playwright test --ui # 打开GUI模式运行和调试6. 高级技巧与性能优化6.1 并行测试与隔离利用Playwright的Browser Context实现轻量级并行和隔离是提升测试套件执行速度的关键。import asyncio from playwright.async_api import async_playwright async def run_test(user_credentials): async with async_playwright() as p: browser await p.chromium.launch() # 每个测试用例拥有自己独立的上下文互不干扰 context await browser.new_context() page await context.new_page() # ... 使用user_credentials登录并执行测试 ... await context.close() await browser.close() async def main(): users [{user:a}, {user:b}, {user:c}] tasks [run_test(user) for user in users] await asyncio.gather(*tasks) # 并行执行 asyncio.run(main())在Playwright Test框架中并行执行是默认行为除非用test.describe.serial且每个测试都有自己的Context天然隔离。6.2 使用追踪Tracing进行调试当测试在CI/CD中失败时光看日志很难定位问题。Playwright的追踪功能可以记录测试过程中的所有操作、网络请求、控制台日志并生成一个可视化的时间线报告。 在配置中启用trace: on-first-retry或trace: retain-on-failure。测试失败后会生成一个trace.zip文件。使用命令npx playwright show-trace trace.zip打开一个图形化界面你可以一步步回放测试过程查看每一步的截图、DOM状态和网络活动是调试复杂失败的利器。6.3 与CI/CD集成在CI环境中如GitHub Actions, GitLab CI, Jenkins通常以无头模式运行测试。关键点缓存浏览器避免每次构建都下载浏览器。CI工具一般有缓存机制可以缓存~/.cache/ms-playwright目录。安装依赖确保安装了所有系统依赖。Playwright提供了playwright install-deps命令来安装必要的系统库如字体、共享库。运行测试使用--headed参数可以在CI中启动有头浏览器需要配置虚拟显示服务器如Xvfb但无头模式是首选。处理失败配置重试机制如retries并收集追踪、截图和视频作为制品上传方便后续分析。一个简单的GitHub Actions配置示例name: Playwright Tests on: [push] jobs: test: runs-on: ubuntu-latest steps: - uses: actions/checkoutv3 - uses: actions/setup-nodev3 with: { node-version: 18 } - name: Cache playwright browsers uses: actions/cachev3 with: path: ~/.cache/ms-playwright key: ${{ runner.os }}-playwright-${{ hashFiles(package-lock.json) }} - run: npm ci - run: npx playwright install --with-deps chromium # 只安装需要的浏览器 - run: npx playwright test - uses: actions/upload-artifactv3 if: failure() with: name: playwright-report path: playwright-report/ retention-days: 77. 常见问题排查与避坑指南在实际项目中你肯定会遇到各种奇怪的问题。这里记录了一些高频问题的排查思路和解决方案。问题现象可能原因排查步骤与解决方案**元素找不到 (TimeoutError) **1. 选择器写错了或元素属性变了。2. 元素在iframe或shadow DOM里。3. 页面还没加载完或元素是动态渲染的。1. 使用Playwright Inspector (playwright open或--ui) 实时验证选择器。2. 检查元素是否在iframe内用page.frame切换。3. 检查是否在shadow root内用element.shadowRoot定位。4. 增加等待await page.wait_for_selector(selector, statevisible, timeout10000)。点击/输入没反应1. 元素被遮挡弹窗、其他元素。2. 元素不可交互disabled,readonly。3. 需要触发其他事件如focus。1. 截图查看当前页面状态确认元素可见。2. 检查元素属性await page.is_enabled(selector)。3. 尝试先hover再click或使用page.dispatch_event直接发送事件。测试在CI上失败本地却成功1. CI环境与本地环境差异网络、资源、数据。2. CI机器性能差超时时间不足。3. 浏览器版本不一致。1. 在CI配置中增加超时时间如timeout: 60000。2. 在CI运行中启用追踪和截图失败后下载分析。3. 确保CI缓存了正确的浏览器版本。4. 检查测试是否依赖本地数据或特定状态改为使用测试专用环境或Mock数据。文件上传失败1. 文件路径错误。2. 上传控件是自定义的非input typefile。1. 使用绝对路径或确认相对路径正确。2. 对于自定义控件可能需要先点击触发文件选择对话框然后使用page.on(filechooser, ...)监听并设置文件。网络请求拦截不生效1. 路由注册时机太晚请求已经发出。2. URL模式匹配不正确。1. 在page.goto或触发请求的操作之前调用page.route。2. 使用page.on(request)监听所有请求打印URL确认匹配模式。并行测试数据污染多个测试用例共享了同一个浏览器上下文Context或页面状态。严格遵守隔离原则每个独立测试用例或测试工作单元必须使用自己独立的BrowserContext。在Playwright Test中这是默认行为。如果手动管理确保为每个用例创建新的Context。避坑技巧一选择器稳定性不要依赖自动录制工具生成的基于索引如:nth-child(5)或复杂CSS路径的选择器。花时间与开发沟通为关键元素添加>