Playwright自动化测试实战:从环境搭建到高级应用

发布时间:2026/6/30 20:31:23

Playwright自动化测试实战:从环境搭建到高级应用 1. 项目概述为什么是Playwright如果你做过Web自动化测试或者尝试过用脚本去操作浏览器那你大概率听说过Selenium。几年前我几乎所有的UI自动化项目都基于它。但说实话维护起来挺头疼的浏览器驱动版本不匹配、元素定位不稳定、异步加载页面处理起来费劲。直到我遇到了Playwright感觉像是从手动挡换成了自动挡。Playwright是微软开源的一个现代化浏览器自动化库。它和Selenium的目标一样——让你能用代码控制浏览器但实现方式和体验截然不同。它原生支持Chromium、Firefox和WebKitSafari的引擎这意味着你可以用同一套脚本测试你的网站在三大浏览器引擎上的表现这对于确保跨浏览器兼容性来说是个巨大的福音。更关键的是它不像Selenium那样依赖一个独立的驱动进程而是通过更底层的协议如Chrome DevTools Protocol直接与浏览器通信这让它的执行速度更快、更稳定。我选择用它来做自动化实战核心就三点稳、快、全。稳指的是脚本的可靠性高很少出现那种“时灵时不灵”的诡异问题快不仅是执行速度快编写和调试脚本的效率也高全它提供的API功能非常丰富从基本的点击输入到文件上传下载、拦截网络请求、模拟移动设备、录制操作视频几乎你能想到的浏览器操作它都能覆盖。这个系列我会从一个真实的、可运行的实战脚本开始带你一步步搭建环境、编写代码、处理各种实际场景中的“坑”最终目标是让你能独立用Playwright解决工作中的自动化需求。无论你是测试工程师、开发人员还是想用自动化提升效率的任何人这套方法都能直接上手。2. 环境搭建与核心工具链解析工欲善其事必先利其器。Playwright的环境搭建比想象中简单但里面有些选择会影响后续开发的体验。2.1 语言与包管理器的选择Playwright官方支持多种语言绑定Node.jsJavaScript/TypeScript、Python、Java和.NET。我的实战将以Python为主原因很简单Python在自动化、数据分析和脚本编写领域生态成熟语法简洁社区资源丰富对于大多数从业者来说学习曲线更平缓。当然如果你前端背景深厚用Node.js版本也完全没问题API设计几乎一致。首先确保你安装了Python建议3.8及以上版本。使用pip进行包管理。这里有个关键选择是全局安装还是使用虚拟环境我强烈推荐使用虚拟环境。这能避免项目间的依赖冲突是Python开发的最佳实践。# 创建项目目录并进入 mkdir playwright-automation-lab cd playwright-automation-lab # 创建Python虚拟环境以venv为例 python -m venv venv # 激活虚拟环境 # Windows: venv\Scripts\activate # macOS/Linux: source venv/bin/activate激活后你的命令行提示符前通常会显示(venv)表示你正在虚拟环境中工作。2.2 安装Playwright与浏览器接下来安装Playwright的Python包。这里有个小技巧直接使用playwright命令安装它会同时安装所需的浏览器。# 安装playwright库 pip install playwright # 安装Playwright所需的浏览器Chromium, Firefox, WebKit playwright install执行playwright install会下载所有支持的浏览器到本地缓存中。这个过程可能会花费一些时间因为需要下载几百MB的浏览器二进制文件。如果网络环境不佳你可以选择只安装需要的浏览器比如只安装Chromiumplaywright install chromium注意playwright install命令下载的浏览器是Playwright专门打包的版本与我们日常使用的Chrome或Firefox不同。它们经过了优化去除了不必要的UI组件和服务更适合自动化运行同时也保证了环境的一致性。2.3 验证安装与第一个脚本安装完成后写一个最简单的脚本来验证一切是否就绪。创建一个名为first_test.py的文件。from playwright.sync_api import sync_playwright with sync_playwright() as p: # 启动Chromium浏览器headlessFalse表示显示浏览器界面 browser p.chromium.launch(headlessFalse) # 创建一个新的浏览器上下文类似于一个独立的隐身会话 context browser.new_context() # 在新上下文中打开一个页面 page context.new_page() # 导航到百度 page.goto(https://www.baidu.com) # 等待3秒方便我们观察 page.wait_for_timeout(3000) # 关闭浏览器 browser.close()在命令行运行这个脚本python first_test.py如果一切正常你会看到一个Chromium浏览器窗口自动打开并跳转到百度首页停留3秒后关闭。恭喜你的Playwright环境已经成功运行起来了这个简单的流程揭示了Playwright的核心对象模型Playwright实例 -Browser-BrowserContext-Page。BrowserContext是一个非常有用的概念它代表一个独立的“会话”相互之间的Cookie、本地存储等都是隔离的这在测试多用户场景或避免状态污染时非常方便。3. 核心API与元素操作实战环境跑通了我们来深入最常用的部分如何与页面上的元素进行交互。Playwright提供了多种定位元素的方式和丰富的交互API。3.1 元素定位策略比XPath更优的选择在Selenium时代很多人依赖XPath但它往往脆弱且性能不佳。Playwright推荐使用面向用户的定位器优先级从高到低如下get_by_role(): 通过ARIA角色定位如button、link、textbox。这是最语义化的方式强烈推荐。get_by_text(): 通过元素内的文本内容定位。get_by_label(): 通过关联的label标签文本来定位表单控件。get_by_placeholder(): 通过输入框的占位符文本定位。get_by_alt_text(): 通过图片的alt属性定位。get_by_title(): 通过title属性定位。get_by_test_id(): 通过开发者专门为测试添加的>from playwright.sync_api import sync_playwright with sync_playwright() as p: browser p.chromium.launch(headlessFalse) page browser.new_page() page.goto(https://www.baidu.com) # 定位搜索输入框 - 使用get_by_role角色是“textbox”并且name属性对应aria-label或placeholder包含“搜索” # 实际中需要查看页面结构这里用placeholder更直接 search_box page.get_by_placeholder(百度一下) # 在输入框里键入关键词 search_box.fill(Playwright自动化测试) # 定位“百度一下”按钮并点击 # 可以先尝试用rolebutton和name百度一下 search_button page.get_by_role(button, name百度一下) search_button.click() # 等待搜索结果页面加载 page.wait_for_load_state(networkidle) # 等待网络基本空闲 page.wait_for_timeout(2000) # 再等2秒确保内容渲染 browser.close()如果面向用户的定位器不奏效我们再考虑CSS选择器。Playwright对CSS选择器的支持非常强大和快速。# 使用CSS选择器定位示例不一定是百度实际结构 search_box page.locator(#kw) # 假设搜索框id是kw search_button page.locator(#su) # 假设按钮id是su实操心得在项目初期就和前端开发团队约定为关键交互元素添加唯一的># 假设我们有一个测试登录页面 page.goto(https://example.com/login) # 输入用户名和密码 page.get_by_label(用户名或邮箱).fill(test_user) page.get_by_label(密码).fill(secure_password123) # 勾选“记住我”复选框 page.get_by_text(记住我).check() # 点击登录按钮 page.get_by_role(button, name登录).click() # 等待登录成功后的跳转可以通过等待某个成功后才出现的元素来确认 page.wait_for_selector(#welcome-message, statevisible) # 等待欢迎信息出现3.3 等待策略告别“sleep”的智慧自动化脚本不稳定的罪魁祸首之一就是“竞态条件”代码执行速度比页面渲染或网络请求快。新手最容易犯的错误就是到处用page.wait_for_timeout(3000)相当于time.sleep(3)。这是极其不推荐的因为它固定等待既浪费快场景的时间又在慢场景下可能不够。Playwright提供了智能的等待机制你应该始终优先使用它们自动等待Playwright的核心操作如click,fill在执行前会自动等待元素满足可操作状态可见、启用、稳定、未被遮挡。大多数时候你只需要这个。显式等待当你需要等待某个特定条件达成时使用。page.wait_for_selector(selector, state...): 等待选择器对应的元素达到特定状态如visible可见、hidden隐藏、attached添加到DOM、detached从DOM移除。page.wait_for_url(url_or_pattern): 等待页面导航到某个URL。page.wait_for_load_state(state): 等待页面加载状态常用loadload事件触发、domcontentloadedDOM加载完成、networkidle网络基本空闲约500ms无网络请求。page.wait_for_function(js_function): 在页面上下文中执行JavaScript函数直到其返回真值。page.wait_for_event(event): 等待特定事件发生如request、response。# 好的等待示例 page.goto(https://example.com) # 等待主要内容区域加载完成 page.wait_for_selector(main.content, statevisible) # 点击一个按钮该操作本身会自动等待按钮可点击 page.get_by_role(button, name加载更多).click() # 点击后等待新加载的内容项出现 page.wait_for_selector(.new-item, statevisible) # 对比不好的做法除非万不得已 # page.wait_for_timeout(5000) # 避免这样注意事项networkidle状态在单页应用SPA中要谨慎使用因为SPA可能长期有后台心跳请求。更可靠的方式是等待某个代表加载完成的特定UI元素出现。4. 高级特性与实战场景拆解掌握了基本操作我们来攻克那些让自动化变得真正强大的高级特性。4.1 处理弹窗、新窗口与iframe对话框alert, confirm, prompt Playwright可以监听并接受或驳回这些原生对话框。# 在点击可能触发alert的操作前先监听对话框事件 page.on(dialog, lambda dialog: dialog.accept()) # 自动接受点击“确定” # 或者 page.on(dialog, lambda dialog: dialog.dismiss()) # 自动驳回点击“取消” # 如果需要获取提示信息或输入内容 page.on(dialog, lambda dialog: print(dialog.message); dialog.accept(输入的文字))新窗口/标签页 点击一个链接可能打开新标签页。Playwright可以轻松捕获这个新页面对象。# 监听新页面打开事件 with page.expect_popup() as popup_info: page.get_by_text(在新窗口打开).click() # 点击会打开新窗口的链接 new_page popup_info.value # 获取新页面的Page对象 # 现在可以操作new_page了 new_page.wait_for_load_state() print(new_page.title())iframe iframe是页面中的嵌套页面。操作iframe内的元素需要先定位到iframe框架本身。# 通过选择器定位iframe元素 iframe_element page.frame_locator(iframe[namemy-frame]) # 现在可以在iframe范围内定位元素了 iframe_element.get_by_role(button, name提交).click() # 或者通过URL或name属性获取Frame对象 frame page.frame(namemy-frame) if frame: frame.fill(#input-inside-iframe, data)4.2 模拟设备、地理位置与权限Playwright可以模拟移动设备如iPhone、Pixel的视口、User-Agent等这对于响应式测试至关重要。from playwright.sync_api import sync_playwright iphone_12 playwright.devices[iPhone 12] with sync_playwright() as p: browser p.chromium.launch(headlessFalse) # 创建上下文时传入设备配置 context browser.new_context(**iphone_12) page context.new_page() page.goto(https://m.example.com) # 此时页面看到的将是iPhone 12的模拟环境你还可以模拟地理位置、语言、时区以及授予或拒绝浏览器权限如摄像头、麦克风、通知。context browser.new_context( geolocation{longitude: 116.397128, latitude: 39.916527}, # 北京坐标 permissions[geolocation], # 授予地理位置权限 localezh-CN, # 设置语言为中文 timezone_idAsia/Shanghai )4.3 拦截与修改网络请求这是Playwright一个极其强大的功能。你可以监听、修改甚至阻断页面的网络请求和响应用于模拟API响应在后端接口未完成时前端开发或测试。性能测试测量请求耗时。屏蔽资源阻止图片、样式表加载以加快测试速度。捕获和分析API调用。# 路由拦截所有请求并可以决定如何处理 def handle_route(route): # 获取请求对象 request route.request # 如果请求的URL包含某个广告脚本则中止请求 if ads.js in request.url: route.abort() # 如果请求的是某个特定的API可以伪造响应 elif /api/userinfo in request.url: route.fulfill( status200, content_typeapplication/json, bodyjson.dumps({name: Mock User, id: 123}) ) else: # 其他请求正常继续 route.continue_() # 在页面上启用路由 page.route(**/*, handle_route) # 现在导航页面所有请求都会经过handle_route函数处理 page.goto(https://example.com)4.4 文件上传与下载文件上传 Playwright处理文件上传非常优雅不需要像Selenium那样找input typefile元素然后send_keys。它使用set_input_files方法。# 定位文件上传输入框 file_input page.locator(input[typefile]) # 上传单个文件 file_input.set_input_files(/path/to/my/file.pdf) # 上传多个文件 file_input.set_input_files([/path/to/file1.jpg, /path/to/file2.jpg]) # 清除已选择的文件 file_input.set_input_files([])文件下载 监听下载事件并等待下载完成获取文件路径。# 开始监听下载事件 with page.expect_download() as download_info: page.get_by_text(导出报告).click() # 点击触发下载的按钮 download download_info.value # 获取Download对象 # 等待下载完成并保存到指定路径 path download.save_as(/local/path/to/save/report.pdf) print(f文件已下载到: {path})5. 调试技巧与常见问题排查即使有了好工具编写和调试自动化脚本也难免遇到问题。掌握以下技巧能极大提升效率。5.1 强大的调试工具Playwright Inspector这是官方提供的图形化调试工具。在运行脚本时添加环境变量PWDEBUG1即可启动。# 在命令行中 PWDEBUG1 python your_script.py # 或者在代码中设置 import os os.environ[PWDEBUG] 1运行后会打开一个浏览器窗口和一个独立的Inspector窗口。在Inspector中你可以逐步执行控制脚本一步一步运行。查看录制自动生成操作对应的代码。检查元素点击页面元素查看Playwright推荐的选择器。查看控制台日志。慢动作模式Slow Mo在启动浏览器时设置slow_mo参数让每个操作都以慢速执行方便观察。browser p.chromium.launch(headlessFalse, slow_mo500) # 每个操作延迟500毫秒录制代码Playwright CLI自带代码录制功能非常适合快速生成脚本草稿。playwright codegen https://example.com这会打开一个浏览器和一个录制窗口。你在浏览器中的操作会被实时转换成代码支持多种语言显示在录制窗口中。5.2 常见问题与解决方案实录以下是我在实际项目中踩过的一些坑和解决方法问题1元素定位不到报错TimeoutError: Timeout 30000ms exceeded。可能原因及排查页面未加载完在操作元素前确保页面或特定元素已经加载。使用page.wait_for_selector()或page.wait_for_load_state()。元素在iframe或Shadow DOM内确认元素是否在嵌套的iframe里需要使用frame_locator。对于Shadow DOMPlaywright的定位器可以穿透一层Shadow Root但复杂的嵌套可能需要CSS选择器配合或/deep/已废弃但Playwright有处理。选择器写错了/不唯一使用浏览器的开发者工具仔细检查元素属性。优先尝试get_by_role,get_by_text等。使用page.locator(“selector”).count()检查匹配的元素数量。元素是动态生成的等待元素出现后再操作。有时需要等待更具体的条件比如元素包含特定文本page.wait_for_selector(“div.item:has-text(‘特定文本’)”)。页面有动画或过渡效果在点击前可以尝试先hover()一下或者用page.wait_for_function()等待元素的某个样式属性稳定。问题2点击或输入操作没有效果。可能原因及排查元素被遮挡其他元素如弹窗、遮罩层盖在了目标元素上面。Playwright的点击默认会检查元素是否可操作包括不被遮挡。你可以通过page.pause()进入调试模式观察或者尝试用page.locator(...).click(forceTrue)强制点击不推荐为首选。需要滚动到视图中如果元素不在当前可视区域内Playwright会自动滚动到该元素。但如果页面有自定义滚动容器可能需要手动处理。可以尝试先page.locator(...).scroll_into_view_if_needed()。需要触发其他事件有些页面逻辑可能监听的是focus、input或change事件而不仅仅是click。尝试组合操作如先focus()再fill()或者用type()代替fill()来触发输入事件。问题3脚本在CI持续集成环境如Docker中运行失败。可能原因及排查缺少系统依赖Playwright的浏览器需要一些系统库。在CI镜像中运行playwright install-depsLinux或使用官方Docker镜像mcr.microsoft.com/playwright/python它预装了所有依赖。无头模式headless问题在CI中通常以headlessTrue运行。有些网站会检测无头浏览器并屏蔽。可以尝试添加args参数来伪装browser.launch(headlessTrue, args[‘--disable-blink-featuresAutomationControlled’])。更高级的伪装可以使用browser.new_context时传入user_agent等。资源限制CI环境可能内存或CPU不足。尝试关闭不必要的浏览器上下文和页面及时close()。也可以只安装和使用一个浏览器如Chromium。问题4如何处理验证码这是一个经典难题。完全自动化解验证码通常违反服务条款。在自动化测试的上下文中正确的做法是测试环境禁用验证码这是最推荐的方式。与开发团队协作在测试环境中为测试账号或特定IP段关闭验证码功能。使用测试专用的万能验证码例如无论输入什么只要包含特定字符串如“TEST”就算通过。人工干预点在脚本运行到验证码步骤时暂停弹出提示让人工输入然后脚本继续。Playwright可以配合page.pause()或监听特定输入来实现。第三方服务谨慎对于必须处理生产环境验证码的RPA场景可以考虑付费的验证码识别服务API但这会引入额外成本、依赖和延迟。5.3 编写健壮脚本的最佳实践使用Page Object Model (POM) 设计模式将每个页面的元素定位和操作封装成一个类。这极大提高了代码的可维护性和复用性。配置合理的超时时间全局超时可以通过context.set_default_timeout(60000)设置。对于特定操作可以使用locator.click(timeout10000)覆盖。做好清理工作在try...except...finally块中确保浏览器和上下文被正确关闭避免资源泄漏。录制是起点不是终点codegen生成的代码很有用但往往包含冗长的CSS选择器。要手动优化改用更稳定的面向用户的定位器。截图和录像是救命稻草在关键步骤或断言失败时自动截图或录制视频这是排查CI环境下失败原因的最直接证据。# 示例失败时截图 try: page.click(“button.submit”) assert page.inner_text(“.status”) “成功” except AssertionError: page.screenshot(path“failure.png”) # 截图保存 raise # 重新抛出异常走到这里你已经掌握了Playwright从环境搭建、核心操作到高级特性和问题排查的完整知识链。真正的熟练来自于实践。我建议你从手头的一个小任务开始比如自动登录某个系统并查询一条数据把上面提到的知识点都用一遍。过程中遇到问题再回头来查阅对应的章节。自动化不是一蹴而就的它是一个不断迭代和优化的过程。当你第一次用脚本完成一个原本需要重复手动操作半小时的任务时那种成就感就是最好的回报。

相关新闻