如何实现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:
- 修改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,彻底销毁浏览器会话
- 修改重试装饰器,每次重试前重新获取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
相关产品推荐
相关产品推荐

