下拉框(Dropdown / Select)是前端 UI 中非常常见的交互组件,可惜在自动化测试里,它也属于“容易踩坑”的类型。
本篇我们以 SauceDemo 电商站点(https://www.saucedemo.com/)
为例,讲解:
-
• ✔ 标准 select下拉的稳定处理方式 -
• ✔ 自定义下拉组件的选择技巧 -
• ✔ 如何避免常见报错(元素不可见、选项未渲染、点击无响应) -
• ✔ 可复用的封装代码(企业级项目常用)
非常适合刚开始写 Playwright 自动化的同学习之用。
1. 示例网站与目标
SauceDemo 商品列表页有一个排序下拉框:
HTML 如下:
<select class="product_sort_container">
<option value="az">Name (A to Z)</option>
<option value="za">Name (Z to A)</option>
<option value="lohi">Price (low to high)</option>
<option value="hilo">Price (high to low)</option>
</select>
这是标准 <select>。
2. 开始前置(登录 fixture)
我们依旧使用标准登录账户:
-
• 用户名: standard_user -
• 密码: secret_sauce
创建 conftest.py:
import pytest
from playwright.async_api import async_playwright
@pytest.fixture()
async def page_context():
async with async_playwright() as pw:
browser = await pw.chromium.launch(headless=False)
context = await browser.new_context()
page = await context.new_page()
yield page
await context.close()
await browser.close()
@pytest.fixture()
async def login(page_context):
page = page_context
await page.goto("https://www.saucedemo.com/")
await page.fill("#user-name", "standard_user")
await page.fill("#password", "secret_sauce")
await page.click("#login-button")
return page
这样每个测试都会自动完成登录。
3. 场景一:标准 <select> 下拉处理(最稳定方式)
Playwright 对 <select> 提供 官方稳定 API:
-
• page.select_option(selector, value=…) -
• 支持 value、label、index
✔ 示例:选择Price low to high
async def test_select_sort_by_price(login):
page = login
await page.goto("https://www.saucedemo.com/inventory.html")
# 选择 option value="lohi"
await page.select_option(".product_sort_container", value="lohi")
# 验证排序是否生效(第一件商品价格为 $7.99)
price_text = await page.locator(".inventory_item_price").first.text_content()
assert price_text == "$7.99"
为什么 select_option 更稳定?
因为它不依赖 UI 动画、不需要点击、不受遮挡影响,直接作用于 DOM。
4. 场景二:点击方式选择下拉
某些前端项目会禁止页面直接 select_option,需要模拟用户点击。
Playwright 的流程为:
-
1. 点击下拉框 -
2. 点击某一个 option
案例:
async def test_click_dropdown_option(login):
page = login
await page.goto("https://www.saucedemo.com/inventory.html")
dropdown = page.locator(".product_sort_container")
await dropdown.click()
# 点击 label
await dropdown.select_option(label="Name (Z to A)")
此方式适用于:
-
• value不稳定 -
• 多语言系统 -
• option的label才能唯一标识
5. 场景三:自定义下拉组件(非 <select>)
许多企业项目会使用:
-
• Ant Design Select -
• ElementUI Select -
• Bootstrap Dropdown -
• 自定义 div+ul伪下拉框
这些组件不再是 <select> 标签
👉 必须用点击方式处理。
下面给你一个通用的“自定义下拉处理函数”。
✔ “万能自定义下拉选择器”封装(可直接用在企业项目)
async def select_custom_dropdown(page, dropdown_selector, option_text):
# 点击下拉框展开
await page.locator(dropdown_selector).click()
# 动态等待选项出现
option = page.locator(f"text={option_text}")
await option.wait_for(state="visible")
await option.click()
使用:
await select_custom_dropdown(
page,
dropdown_selector=".product_sort_container",
option_text="Price (low to high)"
)
虽然 SauceDemo 用的是 <select>,但这个方法适用于:
-
• 前端组件库 (AntD / ElementUI) -
• 自定义 div 菜单 -
• 弹窗类型选择框
⚠ 常见问题与解决方案
❌ 1. “Element is not attached to the DOM”
原因:
-
• 下拉展开需要动画 -
• 渲染未完成就 click
解决:
await option.wait_for(state="visible")
❌ 2. “element not visible”
解决:
await page.locator(dropdown).scroll_into_view_if_needed()
❌ 3. 下拉中出现重复 option,点击错了
使用:
locator("text='Price (low to high)'").nth(0)
6. POM 封装示例(让你的项目更专业)
pages/inventory_page.py:
from playwright.async_api import Page
class InventoryPage:
def __init__(self, page: Page):
self.page = page
self.sort_select = page.locator(".product_sort_container")
self.price_label = page.locator(".inventory_item_price")
async def sort_by(self, value: str):
await self.page.select_option(".product_sort_container", value=value)
async def get_first_price(self):
return await self.price_label.first.text_content()
配合测试:
async def test_sort_by_price_pom(login):
page = login
inv = InventoryPage(page)
await page.goto("https://www.saucedemo.com/inventory.html")
await inv.sort_by("lohi")
price = await inv.get_first_price()
assert price == "$7.99"
7. 总结
本篇我们学习了:
|
|
|
|
|---|---|---|
select
|
select_option() |
|
|
|
select_option
|
|
|
|
|
|
并提供了:
-
• ✔ 可运行完整代码 -
• ✔ SauceDemo实战验证 -
• ✔ 通用自定义下拉选择函数 -
• ✔ POM封装示例
如果觉得本教程和本公众号对你有帮助,还请 点个赞,关个注,下次更新不迷路!

