大数跨境
0
0

Playwright教程:如何稳定处理下拉框(Select & 自定义组件)

Playwright教程:如何稳定处理下拉框(Select & 自定义组件) Playwright实战教程
2025-11-28
5
导读:下拉框(Dropdown / Select)是前端 UI 中非常常见的交互组件,可惜在自动化测试里,它也属于“容易踩坑”的类型。

 

下拉框(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=…)
  • • 支持 valuelabelindex

✔ 示例:选择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. 1. 点击下拉框
  2. 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
点击 + select_option
⭐⭐⭐⭐
自定义下拉(div/ul/li)
click → wait_for → click
⭐⭐⭐⭐

并提供了:

  • • ✔ 可运行完整代码
  • • ✔ SauceDemo 实战验证
  • • ✔ 通用自定义下拉选择函数
  • • ✔ POM 封装示例

如果觉得本教程和本公众号对你有帮助,还请 点个赞,关个注,下次更新不迷路!

 


【声明】内容源于网络
0
0
Playwright实战教程
内容 224
粉丝 0
Playwright实战教程
总阅读27
粉丝0
内容224