You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何实现Pytest+Selenium登录测试重试?解决浏览器状态异常问题

问题解决:Selenium重试时浏览器状态异常修复方案

测试用例

  • 运行Selenium登录测试
    • 打开网站
    • 填写用户名和密码字段
    • 点击登录
    • 等待主页加载完成
    • 断言主页已打开
  • 若登录测试失败则重试(最大尝试次数=5)
  • 收集重试记录
  • 若存在失败重试记录则发送异常状态,否则发送正常状态

问题描述

当断言失败时,WebDriver未正确重置浏览器状态,导致重试执行测试时,浏览器停留在主页而非登录页,进而因等待用户名密码输入框元素而测试失败。

现有代码

重试装饰器

def retry_function(exception, max_attempt: int = 5, step: int = 2):
    def decorator(func):
        @wraps(func)
        def result(*args, **kwargs):
            retry_count = 0
            while retry_count < max_attempt:
                try:
                    return func(*args, **kwargs)
                except exception as err:
                    log.info(f"Retry test [{retry_count}/{max_attempt}]: \"{func.__name__}\" --> {err}. "
                             f"Sleep {step} sec before next retry")
                    time.sleep(step)
                    retry_count += 1

            return func(*args, **kwargs)

        return result

    return decorator

conftest.py

@pytest.hookimpl(tryfirst=True, hookwrapper=True)
def pytest_runtest_makereport(item, call):
    output = yield
    report = output.get_result()

    if report.failed:
        node_id = report.nodeid
        pytest.failed_nodes.append(node_id)
 
@pytest.fixture(scope="function", autouse=True)
def driver(request):
    firefox_options = Options()

    driver = webdriver.Firefox(
        service=Service(GeckoDriverManager().install()),
        options=firefox_options
    )

    failed_before = request.session.testsfailed

    yield driver

    if request.session.testsfailed != failed_before:
        now = datetime.now().strftime('%Y-%m-%d_%H-%M-%S')
        test_name = f'{request.node.name}_{now}'
        take_screenshot(driver, test_name)

    driver.close()

test_login.py

@retry_function(exception=AssertionError, max_attempt=1, step=1)
def test_login(self, driver):
   login_page.login()
   assert is_login_page_opened()

状态发送fixture

@pytest.fixture(scope='session', autouse=True)
def finalizer(request):

    def fin():
        if pytest.failed_nodes:
            resp = api.send_msg(f'Failed tests {pytest.failed_nodes}')
        else:
            resp = api.send_msg('OK')

    request.addfinalizer(fin)

解决方案

核心思路是每次重试前强制重置浏览器状态,确保测试重试时浏览器回到初始干净的登录页环境。以下是三种可行方案:

方案1:修改重试装饰器,重试时重置WebDriver

在捕获异常后,销毁当前浏览器会话并重新初始化,彻底清除之前的状态:

def retry_function(exception, max_attempt: int = 5, step: int = 2):
    def decorator(func):
        @wraps(func)
        def result(*args, **kwargs):
            retry_count = 0
            # 从参数中提取driver对象
            driver = kwargs.get('driver') or (args[1] if len(args) > 1 else None)
            while retry_count < max_attempt:
                try:
                    return func(*args, **kwargs)
                except exception as err:
                    log.info(f"Retry test [{retry_count}/{max_attempt}]: \"{func.__name__}\" --> {err}. "
                             f"Sleep {step} sec before next retry")
                    # 重置浏览器:销毁旧会话,创建新会话
                    if driver:
                        driver.quit()
                        firefox_options = Options()
                        driver = webdriver.Firefox(
                            service=Service(GeckoDriverManager().install()),
                            options=firefox_options
                        )
                        kwargs['driver'] = driver
                    time.sleep(step)
                    retry_count += 1
            # 最后一次尝试后仍失败,执行并抛出异常
            return func(*args, **kwargs)
        return result
    return decorator

方案2:在测试函数内部添加前置状态重置

如果不想修改装饰器,可在测试函数开头添加状态重置逻辑,确保每次执行都从登录页开始:

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

@retry_function(exception=AssertionError, max_attempt=5, step=1)
def test_login(self, driver):
    # 重置状态:清除缓存+导航到登录页
    driver.delete_all_cookies()
    driver.get("https://你的登录页地址.com")
    # 等待登录页元素加载完成
    WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.ID, "用户名输入框ID")))
    
    login_page.login()
    # 修正断言:登录成功后应验证主页而非登录页
    assert is_home_page_opened(), "登录后未跳转到主页"

方案3:优化Fixture配合重试逻辑

将driver的关闭操作改为quit()彻底销毁会话,并在重试时重新获取fixture:

  1. 修改conftest.py中的driver fixture:
@pytest.fixture(scope="function", autouse=True)
def driver(request):
    firefox_options = Options()
    driver = webdriver.Firefox(
        service=Service(GeckoDriverManager().install()),
        options=firefox_options
    )
    failed_before = request.session.testsfailed
    yield driver
    if request.session.testsfailed != failed_before:
        now = datetime.now().strftime('%Y-%m-%d_%H-%M-%S')
        test_name = f'{request.node.name}_{now}'
        take_screenshot(driver, test_name)
    driver.quit()  # 用quit代替close,彻底销毁浏览器会话
  1. 修改重试装饰器,每次重试前重新获取driver fixture:
def retry_function(exception, max_attempt: int = 5, step: int = 2):
    def decorator(func):
        @wraps(func)
        def result(*args, **kwargs):
            retry_count = 0
            request = kwargs.get('request')
            while retry_count < max_attempt:
                # 每次重试重新获取干净的driver
                if request:
                    driver = request.getfixturevalue('driver')
                    kwargs['driver'] = driver
                try:
                    return func(*args, **kwargs)
                except exception as err:
                    log.info(f"Retry test [{retry_count}/{max_attempt}]: \"{func.__name__}\" --> {err}. "
                             f"Sleep {step} sec before next retry")
                    time.sleep(step)
                    retry_count += 1
            return func(*args, **kwargs)
        return result
    return decorator

内容的提问来源于stack exchange,提问作者majesty

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.26 11:25:00