ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Playwright自动化测试实战:从环境搭建到网络拦截与调试

Playwright自动化测试实战:从环境搭建到网络拦截与调试 简介Playwright自动化测试实战文档围绕微软出品的Playwright测试框架展开面向测试工程师、Python开发者以及自动化测试入门者系统讲解基于Python的Playwright应用。内容先从环境搭建入手覆盖Windows、macOS、Linux三大系统下PyCharm安装、pip升级、pytest-playwright安装以及Chromium、Firefox、WebKit内置浏览器下载可帮助读者快速扫清跨浏览器自动化测试的环境障碍。随后通过百度首页实战案例逐步演示输入内容、点击“百度一下”按钮、Web断言、命令行参数配置、录制用例与base_url使用引导完成第一条自动化脚本。进阶部分还整理了click、hover、下拉框、导航等常用方法实践并给出调试技巧、性能优化与最佳实践建议同时点出与传统测试框架的差异。资源以单个docx格式文件打包大小约17.16MB目录结构清晰、章节衔接连贯既能通读学习也可作为日常查询手册。目前已有375人学习下载适合希望系统掌握Playwright并落地到实际项目的读者。1. 为什么说 Playwright 已经不只是“另一个 Selenium”很多团队还在用 Selenium 维护一套端到端测试每天下午你就开始盯着 rerun 的用例点开截图看是不是又因为“元素不可交互”挂掉了。这种状态下Playwright 的出现不是换一个 API 那么简单。它把等待机制内置进每个操作把网络请求拦截、多标签页、iframe 这些过去要额外写一堆 helper 的能力全部做成了原生 API代码量能砍掉三分之一到一半。下面这套方案就是围绕实际落地讲的怎么搭最小工程、怎么控制启动参数、怎么处理动态页面和接口依赖最后怎么保证失败之后能快速定位。适合做自动化测试的工程师、爬虫方向的人以及准备面试时想用 Playwright 讲清楚原理的测试开发。2. Playwright 自动化测试的最小环境与启动参数Playwright 支持 Python、Node.js、Java、.NET 等多种语言但自动化测试生态里Python 和 pytest 绑定最紧所以下面的示例都用 Python。环境安装很简单但很多东西藏在参数里只有理解了浏览器实例和上下文之间的关系才能写出隔离性好、不互相影响的用例。2.1 安装 Playwright 与浏览器内核安装分两步两条命令缺一不可pip install playwright playwright install chromium第一条安装控制库第二条把浏览器内核下载到本机缓存。pip install playwright不会附带浏览器二进制所以如果你只装了这个就启动浏览器会遇到“Executable doesn’t exist”的错误。需要同时下载内核而且官方推荐用chromium作为默认内核日常回归只需要跑它一个就够了不必把 firefox、webkit 都装一遍。安装完成后验证一下版本playwright --version如果输出了版本号说明控制库可用。CI 环境里下载慢的话可以把浏览器缓存目录提前打到镜像里或者设置PLAYWRIGHT_BROWSERS_PATH指向固定目录避免每次构建都重新下载。2.2 用 sync_playwright 启动浏览器的最小代码一个能跑通的最小脚本长这样from playwright.sync_api import sync_playwright with sync_playwright() as p: browser p.chromium.launch( headlessFalse, slow_mo300, ) page browser.new_page() page.goto(https://example.com) print(page.title()) browser.close()sync_playwright是一个上下文管理器负责启动驱动进程和清理资源。chromium.launch负责拉起浏览器实例headlessFalse表示有头模式方便观察slow_mo300让每个操作之间延迟 300ms模拟真人操作的速度适合调试阶段。browser.new_page()会创建新的标签页goto会等页面触发load事件但注意它不保证所有接口都返回完毕所以后面还需要显式等待。2.3 启动参数与上下文配置launch管的是浏览器进程而context才是隔离的会话环境。常见做法是在new_context里传入浏览器配置同一个浏览器实例可以开出多个互不干扰的上下文它们的 cookie、localStorage 是完全独立的。参数类型默认值作用headlessboolTrue是否无头运行CI 必须为Trueslow_moint无每次操作后的延迟毫秒数调试用viewportdict1280x720窗口尺寸影响响应式布局user_agentstr无自定义 UA部分页面会做 UA 校验localestren-US浏览器语言影响 i18n 文案timezone_idstr无指定时区比如Asia/Shanghaipermissionslist无授权定位、摄像头等权限ignore_https_errorsboolFalse忽略证书错误测试环境常用ignore_https_errors在测试环境几乎必设因为自签名证书会让页面直接加载失败。permissions可以用来授予地理位置权限不用真的去点浏览器弹窗。locale和timezone_id适合做国际化断言比如切换到中文环境后判断“登录成功”这个文案是否显示。这些参数放在new_context里比放在launch里更合理因为一个浏览器进程可以开出多个不同配置的上下文并行用例之间不会被 cookie 串号。3. 页面操作与元素定位从 codegen 到选择器优先级页面操作的核心是“定位元素 → 执行动作 → 断言结果”。Playwright 的定位 API 比 Selenium 更贴近用户行为而且内置自动等待。很多人一上来就点录屏确实能快速跑通但真正到了写用例和排查时还是得理解选择器的优先级和等待机制。3.1 用 codegen 快速生成脚本录制工具是第一步。命令行playwright codegen --channelchrome https://example.com--channelchrome会调用本机安装的 Chrome而不是 Playwright 自带的 Chromium。这种做法有两个好处一是本机 Chrome 更接近真实用户登录态可以复用二是有些内网页面只对 Chrome 版本有依赖。如果不指定默认用 Playwright 的 Chromium效果也一致。弹出的浏览器窗口里右侧会自动生成操作代码语言可以切成 Python 或 JavaScript。codegen 生成的脚本只能当草稿不能直接作为测试用例。原因有几点生成的选择器往往是#app div div .btn这种长 CSS 路径页面结构一变就挂录制的坐标点击没有语义没有断言无法判断页面是否真的进入了预期状态。我的做法是拿录制结果当元素线索然后手动改成稳定的 role 或 text 选择器。3.2 选择器优先级role、text、CSS、XPathPlaywright 支持多种选择器建议按这个顺序选选择器示例使用场景rolepage.get_by_role(button, name提交)按可访问性语义定位textpage.get_by_text(登录成功)按文本内容定位CSSpage.locator([data-testiduser-name])有稳定测试属性时使用XPathpage.locator(//input[idxxx])保底方案不优先get_by_role是首选因为按钮、链接、输入框这些语义是用户和屏幕阅读器都依赖的样式改变不会影响测试。get_by_text适合定位提示文案或列表项但要注意文本可能被拆成多个子节点这时可以用has_text做局部匹配。CSS 里优先选择>page.get_by_role(textbox, name用户名).fill(admin) page.get_by_role(button, name登录).click() page.get_by_text(欢迎回来).wait_for()fill会先找到可见文本框清空原来的值再输入。click会自动等待元素稳定、可点击后才真正点击不需要手动加time.sleep。wait_for是显式等待默认超时 5 秒可以传timeout10000覆盖。3.3 用 pytest 组织用例与断言如果在测试项目里用 pytest官方推荐安装pytest-playwright插件。装完之后可以直接用pagefixturedef test_login(page): page.goto(https://example.com/login) page.get_by_label(用户名).fill(admin) page.get_by_label(密码).fill(password) page.get_by_role(button, name登录).click() expect(page.get_by_text(欢迎回来)).to_be_visible()这里的expect来自playwright.sync_api断言会不断重试直到超时。to_be_visible()默认在 5 秒内每 100ms 检查一次元素可见性所以页面加载慢也不会秒失败。断言失败时输出里会带上元素的快照方便定位问题。注意get_by_label和get_by_role的name参数匹配的是可访问名称不是value属性所以表单里的label标签一定要写的规范否则这两个 API 会找不到元素。4. 监听页面请求与动态 iframe高级拦截与执行上下文很多测试用例想验证的不只是界面上的文字还有某个操作后前端有没有发出正确的接口请求或者后端返回特定数据时页面怎么渲染。这类场景用 Selenium 做起来很绕因为要额外引入代理工具。Playwright 直接内置了网络事件监听和路由拦截几行代码就能 mock 接口。4.1 监听页面请求与响应监听请求和响应用page.on事件def test_track_api(page): requests [] responses [] page.on(request, lambda req: requests.append(req.url)) page.on(response, lambda res: responses.append((res.url, res.status))) page.goto(https://example.com/login) page.get_by_role(button, name登录).click() assert any(api/login in url for url in requests)page.on(request)会在每次请求发出前触发page.on(response)在响应到达后触发。这里用同步 API 的 lambda 做记录足够但要注意 handler 里不要写耗时操作因为事件回调是串行执行的太慢会拖慢整个测试。更精确的写法是page.expect_responsewith page.expect_response(**/api/login) as resp_info: page.get_by_role(button, name登录).click() resp resp_info.value assert resp.status 200这个块会把“点击”和“等待响应”绑定起来不会漏掉点击之后的网络请求比 sleep 后再去查列表靠谱得多。4.2 拦截并修改接口返回如果后端接口在测试环境不可用可以直接 mock 掉。比如登录接口返回的 token 是动态的你想固定用一个测试 tokendef handle_route(route): route.fulfill(json{token: test-token, username: admin}, status200) page.route(**/api/login, handle_route) page.goto(https://example.com/login) page.get_by_role(button, name登录).click()page.route的 URL 匹配规则支持 glob 模式**匹配任意路径。route.fulfill会直接伪造一个响应不再走到真实网络。这个能力在做异常场景测试时也很好用比如模拟接口 500page.route(**/api/stock, lambda route: route.abort(failed))route.abort会终止请求前端拿到的就是网络失败错误适合测试错误提示分支。需要注意的是后注册的 route 会覆盖前面匹配到的规则。如果你希望先处理一部分请求、剩下的放行需要在 handler 里调用route.continue_()。4.3 动态 iframe 里的元素定位iframe 一直是 Selenium 的痛点切换 frame 要写switch_to.frame嵌套一多很容易乱。Playwright 用frame_locator处理不需要全局切换frame page.frame_locator(#dynamic-frame) frame.get_by_role(button, name提交).click()frame_locator返回一个独立的作用域所有定位都在目的 iframe 内查找。对于动态加载的 iframe需要先保证 iframe 出现在 DOM 里。常见做法是locator page.locator(#dynamic-frame) locator.wait_for()frame_locator本身不会自动等 iframe 出现但如果你在它下面定位某个元素它会等到元素被找到为止。所以更直接的方式是page.frame_locator(#dynamic-frame).get_by_text(加载完成).wait_for()这段代码会在 iframe 里等 “加载完成” 这个文本出现。如果 iframe 内容来自另一个域名也不用考虑跨域问题Playwright 在系统层面驱动浏览器不受页面 JS 的同源策略限制。这在爬虫场景搭配 scrapy-playwright 时也一样iframe 的加载时序最终会反映到元素可见性上等待策略要写在交互附近不要写在开场后很久的位置。5. 回归排错提升trace viewer 与滚动、下载的固定套路自动化测试用例一多失败之后的定位就显得格外重要。Playwright 的 trace viewer 能记录每一步操作的截图、DOM 快照和网络请求失败时直接回放比看日志猜原因高效得多。browser p.chromium.launch() context browser.new_context() context.tracing.start(screenshotsTrue, snapshotsTrue) page context.new_page() page.goto(https://example.com) # 测试到这里如果失败 context.tracing.stop(pathtrace.zip) browser.close()跑完测试后在命令行执行playwright show-trace trace.zip这里注意screenshotsTrue会为每个步骤保存截图snapshotsTrue会保存 DOM 快照。trace 文件随着步骤增多会变大建议只在失败用例里动态开启 tracing而不是所有用例都开否则 CI 上的产物会膨胀。另一个经常遇到的问题就是滚动和文件下载。Playwright 的click默认会把元素滚动到可视区域所以大多数情况下不用手动滚动。但有些页面是懒加载的比如无限列表需要滚到底部才能触发下一页加载这时用page.mouse.wheel(0, 1000)或者更直接地用page.evaluate(window.scrollBy(0, 1000))wheel会模拟鼠标滚轮事件更接近用户操作适合触发滚动监听器evaluate直接调用 JS适合快速跳转。下载文件则用expect_downloadwith page.expect_download() as dl_info: page.get_by_role(link, name下载报表).click() download dl_info.value download.save_as(/tmp/report.xlsx)expect_download会捕获浏览器的下载事件文件会先保存到临时目录你再把它移动到指定路径。注意不要用goto触发下载这会阻塞页面导航。在 CI 里跑的时候我习惯把PLAYWRIGHT_BROWSERS_PATH/tmp/playwright加在流水线环境变量里这样并行任务之间不会抢同一个浏览器缓存目录能省掉不少偶发的 “Executable doesn’t exist” 报错。本文还有配套的精品资源点击获取
返回列表