UI自动化测试框架:Playwright 官方教程(二)—— 异步与动态定位
在上一篇中,我们已经安装好 Playwright 的运行环境,并对其进行了简单的介绍,本文将进一步介绍playwright 的高级特性。
一、同步与异步
我们以一个最简单的打开网页并截图为例来初步的演示 playwright 中的同步与异步功能。
1.1 同步
# -*- coding:utf-8 -*-
from playwright.sync_api import sync_playwright
def run(_playwright):
browser = _playwright.chromium.launch()
page = browser.new_page()
page.goto("https://www.baidu.com")
page.wait_for_load_state("networkidle")
page.screenshot(path="baidu.png")
browser.close()
with sync_playwright() as playwright:
run(playwright)
代码解析:
sync_playwright():启动 Playwright,支持 with 语法,确保程序结束后资源释放。
_playwright.chromium.launch():启动 Chrome(你也可以用
_playwright.firefox.launch())。
browser.new_page():在新页签打开。
page.goto():访问网址。
page.wait_for_load_state():等待页面加载完成。
page.screenshot():截图。
browser.close():关闭浏览器。
执行完代码,你的项目目录下就会多出一张 baidu.png
1.2 异步
# -*- coding:utf-8 -*-
import asyncio
from playwright.async_api import async_playwright
async def open_baidu(url, png_name):
async with async_playwright() as playwright:
browser = await playwright.chromium.launch(headless=False)
page = await browser.new_page()
await page.goto(url)
await page.wait_for_load_state("networkidle")
await page.screenshot(path=png_name)
await browser.close()
print('open baidu.')
asyncio.run(open_baidu("https://www.baidu.com/", 'baidu.png'))
print('open playwright docs.')
asyncio.run(open_baidu("https://playwright.dev/docs/intro", 'playwright.png'))
代码解析:
from playwright.async_api import async_playwright:与同步方式不同,异步导入的模块是
playwright.async_api
async def open_baidu(url, png_name): 定义异步方法
async with async_playwright() as playwright:异步的上下文管理器
browser = await playwright.chromium.launch(headless=False):以有头模式异步的打开一个浏览器实例
asyncio.run(open_baidu("https://playwright.dev/docs/intro", 'playwright.png')):异步运行
运行完成后会生成如下图片:
二、元素基础定位
Playwright 定位元素的核心思想:
Playwright 的定位器(Locators)是其自动等待和重试能力的核心,通过智能等待元素加载、重试机制避免因网络延迟或动态渲染导致的定位失败。推荐优先使用语义化定位器(如角色、文本),而非传统 CSS/XPath,以提高代码可读性和稳定性。
2.1. CSS 选择器
通过 HTML 标签、类名、ID 等 CSS 语法定位:
# 定位 ID 为 "username" 的输入框
page.locator("#TANGRAM__PSP_11__userName").fill("admin")
# 定位类名为 "submit-btn" 的按钮
page.locator("button.submit-btn").click()
适用场景:元素有固定 ID/类名,或需层级嵌套选择(如 div > input)。
2.2. XPath 表达式
通过 XML 路径语法定位复杂结构:
# 定位 name 属性为 "email" 的输入框
page.locator("//input[@name='email']").fill("test@example.com")
# 定位包含特定文本的按钮
page.locator("//button[contains(text(), '提交')]").click()
适用场景:元素属性动态变化、需复杂条件筛选时。
2.3. 按角色定位(Role)
基于 ARIA 角色和可访问性属性,最接近用户感知:
# 定位名称为 "登录" 的按钮
page.get_by_role("button", name="登录").click()
# 定位角色为输入框且名称为 "用户名"
page.get_by_role("textbox", name="用户名").fill("user123")
支持角色:button、link、checkbox、heading 等。
2.4. 按文本内容定位
# 精确匹配文本
page.get_by_text("欢迎回来").click()
# 正则表达式模糊匹配
page.get_by_text(re.compile(r"订单编号\d+")).hover()
适用场景:元素无固定属性,但文本内容稳定。
2.5. 按标签关联定位
# 通过关联的 <label> 文本定位输入框
page.get_by_label("密码:").fill("secret")
# 通过占位符定位
page.get_by_placeholder("请输入手机号").type("13800138000")
优势:与表单控件天然绑定,避免层级依赖。
2.6. 按测试 ID 定位
专为测试设计的属性,需页面添加 data-testid:
# 定位 data-testid="submit-button" 的元素
page.get_by_test_id("submit-button").click()
三、动态元素定位
3.1 显式等待元素加载
# 等待元素可见后再操作
page.locator(".loading").wait_for(state="visible")
# 等待元素消失
page.wait_for_selector(".spinner", state="hidden")
3.2 处理 iframe 嵌套
# 定位到 iframe 内的元素
iframe = page.frame(name="payment-iframe")
iframe.get_by_text("确认支付").click()
3.3 Shadow DOM 穿透
# 通过 >> 符号穿透 Shadow DOM 层级:
page.locator("div#shadow-host >> input.custom-input").fill("data")
四、复杂场景定位
4.1 多重条件筛选
# 定位类名为 "item" 且包含文本 "特价" 的元素
page.locator(".item", has_text="特价").click()
# 组合角色和文本过滤
page.get_by_role("listitem").filter(has_text="待付款").nth(0).click()
4.2 相对定位
# 父子关系定位
parent = page.locator("div.parent")
child = parent.locator("span.child")
# 兄弟元素定位
second_item = page.locator("ul > li").nth(1)
4.3 动态列表处理
# 遍历商品列表并点击第三个商品
items = page.locator(".product-list > li")
await items.nth(2).click()
# 根据文本动态定位最新添加的条目
new_item = page.locator("tr:has-text('2024-03-18')").last
五、注意事项
选择器优先级:尽量使用ID或类名选择器,避免使用复杂的XPath。
异步操作的调试:使用playwright debug工具排查异步问题。
动态内容的处理:确保等待元素加载完成,避免操作失败。