You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

移动端Web自动化测试:汉堡菜单打开断言失败求助

移动端Web自动化测试:汉堡菜单打开断言失败求助

大家好,我现在在做Android移动端Web的自动化测试,用Appium + Pytest框架测试Chrome浏览器里的汉堡菜单功能。执行测试时,模拟器能正常打开Chrome并加载目标URL,但尝试打开汉堡菜单后,断言一直失败,提示AssertionError: Burger is not open. class='burger',想请各位帮忙排查下问题~


问题复现步骤

  • 启动Android模拟器(Pixel 8,Android 16.0)
  • 启动Appium服务
  • 运行Pytest测试用例
  • 模拟器打开Chrome加载目标URL后,执行汉堡菜单打开操作,最终断言失败

环境与执行命令

启动命令

  • 启动模拟器:
    emulator -avd Pixel
    
  • 启动Appium:
    appium --allow-insecure="*:chromedriver_autodownload"
    
  • 运行测试:
    pytest tests/test_burger_menu.py::test_open_burger_menu --no-header --no-summary -q
    

相关代码片段

conftest.py(配置文件)

import pytest
from appium import webdriver
from appium.options.android import UiAutomator2Options


def pytest_addoption(parser):
    parser.addoption("--appium-url", action="store", default="http://127.0.0.1:4723")
    parser.addoption("--udid", action="store", default="emulator-5554")
    parser.addoption(
        "--base-url",
        action="store",
        default="https://www.example.com",
    )


@pytest.fixture(scope="session")
def base_url(request) -> str:
    return request.config.getoption("--base-url")


@pytest.fixture
def driver(request):
    appium_url = request.config.getoption("--appium-url")
    udid = request.config.getoption("--udid")

    options = UiAutomator2Options()
    options.platform_name = "Android"
    options.automation_name = "UiAutomator2"
    options.udid = udid
    options.browser_name = "Chrome"
    options.new_command_timeout = 120

    options.set_capability("appium:uiautomator2ServerLaunchTimeout", 120000)
    options.set_capability("appium:uiautomator2ServerInstallTimeout", 120000)
    options.set_capability("appium:uiautomator2ServerStartupTimeout", 120000)
    options.set_capability("appium:skipLogcatCapture", True)
    options.set_capability("appium:clearSystemFiles", True)
    options.set_capability("appium:ignoreHiddenApiPolicyError", True)

    # Important for mobile Chrome: don't wait for full page load (reduces hangs/crashes)
    options.set_capability("pageLoadStrategy", "none")

    # Disable first-run/Welcome screen in Chrome
    options.set_capability("appium:chromeOptions", {"args": ["--disable-fre", "--no-first-run"]})

    drv = webdriver.Remote(appium_url, options=options)
    try:
        yield drv
    finally:
        drv.quit()

pages/home_page.py(注:当前代码存在粘贴错误,重复了conftest配置,实际应为页面对象类)

import pytest
from appium import webdriver
from appium.options.android import UiAutomator2Options


def pytest_addoption(parser):
    parser.addoption("--appium-url", action="store", default="http://127.0.0.1:4723")
    parser.addoption("--udid", action="store", default="emulator-5554")
    parser.addoption(
        "--base-url",
        action="store",
        default="https://www.particleformen.com/?store_switch=en",
    )


@pytest.fixture(scope="session")
def base_url(request) -> str:
    return request.config.getoption("--base-url")


@pytest.fixture
def driver(request):
    appium_url = request.config.getoption("--appium-url")
    udid = request.config.getoption("--udid")

    options = UiAutomator2Options()
    options.platform_name = "Android"
    options.automation_name = "UiAutomator2"
    options.udid = udid
    options.browser_name = "Chrome"
    options.new_command_timeout = 120

    options.set_capability("appium:uiautomator2ServerLaunchTimeout", 120000)
    options.set_capability("appium:uiautomator2ServerInstallTimeout", 120000)
    options.set_capability("appium:uiautomator2ServerStartupTimeout", 120000)
    options.set_capability("appium:skipLogcatCapture", True)
    options.set_capability("appium:clearSystemFiles", True)
    options.set_capability("appium:ignoreHiddenApiPolicyError", True)

    # Important for mobile Chrome: don't wait for full page load (reduces hangs/crashes)
    options.set_capability("pageLoadStrategy", "none")

    # Disable first-run/Welcome screen in Chrome
    options.set_capability("appium:chromeOptions", {"args": ["--disable-fre", "--no-first-run"]})

    drv = webdriver.Remote(appium_url, options=options)
    try:
        yield drv
    finally:
        drv.quit()

tests/test_burger_menu.py(测试用例)

import pytest
import allure
from pages.home_page import HomePage


@allure.epic("Mobile Web Tests")
@allure.feature("Home Page")
@pytest.mark.smoke
@allure.story("Burger menu functionality")
def test_open_burger_menu(driver, base_url):
    with allure.step("Open home page"):
        HomePage(driver).open(base_url).assert_loaded()

    with allure.step("Open burger menu"):
        HomePage(driver).open_menu()

    with allure.step("Verify menu is opened"):
        HomePage(driver).assert_menu_opened()

我的疑惑与当前排查点

  1. 发现pages/home_page.py的代码错误重复了conftest的配置,实际应该是封装元素操作的页面对象类,这大概率是问题根源之一
  2. 页面加载策略设置为none,会不会导致元素未渲染完成就执行了操作?
  3. 断言汉堡菜单打开的逻辑是否准确?比如判断的class元素是否在菜单打开后才会出现/状态变化?

排查建议与解决方案(经验分享)

1. 首先修正页面对象类的错误

pages/home_page.py必须是封装元素定位和操作的类,示例如下:

from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.by import By

class HomePage:
    def __init__(self, driver):
        self.driver = driver
        self.wait = WebDriverWait(driver, 20)
        # 汉堡按钮定位(请根据实际页面调整)
        self.burger_trigger = (By.CLASS_NAME, "burger-trigger")
        # 打开后的汉堡菜单容器定位(请根据实际页面调整)
        self.opened_burger_menu = (By.CLASS_NAME, "burger.open")

    def open(self, url):
        self.driver.get(url)
        return self

    def assert_loaded(self):
        # 显性等待核心元素(汉堡按钮)加载完成,确保页面就绪
        self.wait.until(EC.presence_of_element_located(self.burger_trigger))
        return self

    def open_menu(self):
        # 等待汉堡按钮可点击后再执行点击
        self.wait.until(EC.element_to_be_clickable(self.burger_trigger)).click()
        return self

    def assert_menu_opened(self):
        try:
            # 等待菜单可见,或判断元素属性变化(比如class是否包含open)
            self.wait.until(EC.visibility_of_element_located(self.opened_burger_menu))
        except Exception as e:
            # 可以添加截图辅助调试
            self.driver.save_screenshot("menu_assert_failed.png")
            raise AssertionError(f"Burger is not open. class='burger'") from e

2. 验证元素定位的准确性

  • 用Chrome DevTools(开启模拟器Chrome远程调试)或Appium Inspector查看移动端页面的元素结构,确认:
    • 汉堡按钮的实际定位符(class/id/xpath等)
    • 菜单打开后,容器元素的class是否有状态变化(比如新增open类)
  • 注意响应式页面的元素差异:移动端和桌面端的汉堡菜单定位可能不同,确保定位的是移动端专属元素

3. 优化等待策略

  • 放弃隐式等待,全部改用显性等待,针对每个操作和断言设置足够的超时时间
  • 由于页面加载策略是noneassert_loaded()必须等待核心元素出现后再执行后续操作,避免元素未渲染就触发点击

4. 强制Chrome使用移动端视图

在Appium的ChromeOptions中添加移动端仿真配置,避免模拟器Chrome默认使用桌面视图:

options.set_capability("appium:chromeOptions", {
    "args": ["--disable-fre", "--no-first-run"],
    "mobileEmulation": {"deviceName": "Pixel 8"}
})

5. 添加调试辅助

在关键步骤添加日志或截图,帮助定位问题:

def open_menu(self):
    button = self.wait.until(EC.element_to_be_clickable(self.burger_trigger))
    print(f"点击前汉堡按钮的class: {button.get_attribute('class')}")
    button.click()
    self.driver.save_screenshot("after_click_burger.png")
    return self

先把页面对象类的错误修正,再从元素定位、等待策略这两个核心点排查,基本就能解决断言失败的问题。如果还有疑问,可以贴出实际页面的元素结构或HomePage类的正确代码,我们再进一步分析~

火山引擎 最新活动