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

使用pytest+Selenium并行测试时pytest_sessionfinish报错

问题:pytest并行测试时Allure环境属性写入失败

问题背景

我在conftest.py中编写了pytest+Selenium的自动化测试代码,执行并行测试命令时,pytest_sessionfinish函数在向Allure报告添加浏览器、版本等环境属性时报错。

原conftest.py代码

import os
from urllib import request

import pytest
from selenium.webdriver.chrome.options import Options
import allure
from selenium import webdriver


@pytest.fixture(autouse=True)
def setup(request):
    global driver
    options = Options()
    options.add_experimental_option("detach", True)
    options.add_argument("--headless")
    # options.add_argument("--no-sandbox")
    options.add_argument("--disable-dev-shm-usage")
    # options = webdriver.ChromeOptions()
    driver = webdriver.Chrome(options=options)
    request.cls.driver = driver
    driver.maximize_window()
    driver.get("https://magento.softwaretestingboard.com/")
    yield
    driver.quit()


def pytest_exception_interact(report):
    if report.failed:
        allure.attach(body=driver.get_screenshot_as_png(), name="screenshot",
                      attachment_type=allure.attachment_type.PNG)


def pytest_sessionfinish() -> None:
    environment_properties = {
     'browser': driver.name,
     'driver_version': driver.capabilities['browserVersion']
    }
    allure_env_path = os.path.join("allure-results", 'environment.properties')
    with open(allure_env_path, 'w') as f:
        data = '\n'.join([f'{variable}={value}' for variable, value in environment_properties.items()])
        f.write(data)

执行的并行测试命令

pytest -n 5 --dist loadgroup --alluredir=allure-results --reruns 2 --reruns-delay 2

报错栈

File "<frozen runpy>", line 198, in _run_module_as_main
  File "<frozen runpy>", line 88, in _run_code
  File "C:\Users\benik\PycharmProjects\pythonAutomationStore\.venv\Scripts\pytest.exe\__main__.py", line 7, in <module>
  File "C:\Users\benik\PycharmProjects\pythonAutomationStore\.venv\Lib\site-packages\_pytest\config\__init__.py", line 198, in console_main
    code = main()
           ^^^^^^
  File "C:\Users\benik\PycharmProjects\pythonAutomationStore\.venv\Lib\site-packages\_pytest\config\__init__.py", line 175, in main
    ret: Union[ExitCode, int] = config.hook.pytest_cmdline_main(
                                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\benik\PycharmProjects\pythonAutomationStore\.venv\Lib\site-packages\pluggy\_hooks.py", line 493, in __call__
    return self._hookexec(self.name, self._hookimpls, kwargs, firstresult)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\benik\PycharmProjects\pythonAutomationStore\.venv\Lib\site-packages\pluggy\_manager.py", line 115, in _hookexec
    return self._inner_hookexec(hook_name, methods, kwargs, firstresult)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\benik\PycharmProjects\pythonAutomationStore\.venv\Lib\site-packages\pluggy\_callers.py", line 113, in _multicall
    raise exception.with_traceback(exception.__traceback__)
  File "C:\Users\benik\PycharmProjects\pythonAutomationStore\.venv\Lib\site-packages\pluggy\_callers.py", line 77, in _multicall
    res = hook_impl.function(*args)
          ^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\benik\PycharmProjects\pythonAutomationStore\.venv\Lib\site-packages\_pytest\main.py", line 320, in pytest_cmdline_main
    return wrap_session(config, _main)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\benik\PycharmProjects\pythonAutomationStore\.venv\Lib\site-packages\_pytest\main.py", line 308, in wrap_session
    config.hook.pytest_sessionfinish(
  File "C:\Users\benik\PycharmProjects\pythonAutomationStore\.venv\Lib\site-packages\pluggy\_hooks.py", line 493, in __call__
    return self._hookexec(self.name, self._hookimpls, kwargs, firstresult)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\benik\PycharmProjects\pythonAutomationStore\.venv\Lib\site-packages\pluggy\_manager.py", line 115, in _hookexec
    return self._inner_hookexec(hook_name, methods, kwargs, firstresult)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\benik\PycharmProjects\pythonAutomationStore\.venv\Lib\site-packages\pluggy\_callers.py", line 113, in _multicall
    raise exception.with_traceback(exception.__traceback__)
  File "C:\Users\benik\PycharmProjects\pythonAutomationStore\.venv\Lib\site-packages\pluggy\_callers.py", line 96, in _multicall
    teardown.throw(exception)  # type: ignore[union-attr]
    ^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\benik\PycharmProjects\pythonAutomationStore\.venv\Lib\site-packages\_pytest\logging.py", line 861, in pytest_sessionfinish
    return (yield)
            ^^^^^
  File "C:\Users\benik\PycharmProjects\pythonAutomationStore\.venv\Lib\site-packages\pluggy\_callers.py", line 96, in _multicall
    teardown.throw(exception)  # type: ignore[union-attr]
    ^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\benik\PycharmProjects\pythonAutomationStore\.venv\Lib\site-packages\_pytest\terminal.py", line 854, in pytest_sessionfinish        
    result = yield
             ^^^^^
  File "C:\Users\benik\PycharmProjects\pythonAutomationStore\.venv\Lib\site-packages\pluggy\_callers.py", line 96, in _multicall
    teardown.throw(exception)  # type: ignore[union-attr]
    ^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\benik\PycharmProjects\pythonAutomationStore\.venv\Lib\site-packages\_pytest\warnings.py", line 138, in pytest_sessionfinish        
    return (yield)
            ^^^^^
  File "C:\Users\benik\PycharmProjects\pythonAutomationStore\.venv\Lib\site-packages\pluggy\_callers.py", line 77, in _multicall
    res = hook_impl.function(*args)
          ^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\benik\PycharmProjects\pythonAutomationStore\tests\conftest.py", line 48, in pytest_sessionfinish
    driver = session.config._driver
             ^^^^^^^^^^^^^^^^^^^^^^

问题分析与解决方案

核心问题

  1. 全局变量driver在并行环境下不可靠:pytest-xdist采用多进程模式,每个进程有独立内存空间,全局变量无法跨进程共享,pytest_sessionfinish执行时可能无法获取有效driver实例,甚至driver已被销毁。
  2. 多进程写入文件竞争:多个进程同时写入environment.properties会导致内容覆盖或写入失败。

修复方案

方案1:通过pytest配置传递信息+文件锁解决写入冲突

修改conftest.py,避免全局变量,用pytest的config对象存储浏览器信息,同时通过文件锁处理多进程写入竞争:

import os
import pytest
from selenium.webdriver.chrome.options import Options
import allure
from selenium import webdriver
from filelock import FileLock

@pytest.fixture(autouse=True)
def setup(request):
    options = Options()
    options.add_experimental_option("detach", True)
    options.add_argument("--headless")
    options.add_argument("--disable-dev-shm-usage")
    driver = webdriver.Chrome(options=options)
    request.cls.driver = driver
    # 将浏览器信息存入config自定义属性
    if not hasattr(request.config, 'browser_info'):
        request.config.browser_info = {
            'browser': driver.name,
            'driver_version': driver.capabilities['browserVersion']
        }
    driver.maximize_window()
    driver.get("https://magento.softwaretestingboard.com/")
    yield
    driver.quit()

def pytest_exception_interact(report, request):
    if report.failed:
        # 通过request获取当前用例的driver,而非全局变量
        driver = request.cls.driver
        allure.attach(body=driver.get_screenshot_as_png(), name="screenshot",
                      attachment_type=allure.attachment_type.PNG)

def pytest_sessionfinish(session) -> None:
    if hasattr(session.config, 'browser_info'):
        environment_properties = session.config.browser_info
        allure_env_path = os.path.join("allure-results", 'environment.properties')
        # 文件锁避免多进程写入冲突
        with FileLock(f"{allure_env_path}.lock"):
            # 读取已有内容,避免覆盖
            existing_data = ""
            if os.path.exists(allure_env_path):
                with open(allure_env_path, 'r') as f:
                    existing_data = f.read()
            # 生成新内容并去重
            new_data = '\n'.join([f'{k}={v}' for k, v in environment_properties.items()])
            combined_lines = list(set(existing_data.split('\n') + new_data.split('\n')))
            combined_data = '\n'.join([line for line in combined_lines if line.strip()])
            # 写入文件
            with open(allure_env_path, 'w') as f:
                f.write(combined_data)

方案2:预先生成环境文件

如果所有并行测试使用相同浏览器版本,可提前生成environment.properties文件,测试前复制到allure-results目录:

  1. 项目根目录创建environment.properties:
browser=chrome
driver_version=120.0.6099.109
  1. 执行测试命令:
copy environment.properties allure-results\environment.properties
pytest -n 5 --dist loadgroup --alluredir=allure-results --reruns 2 --reruns-delay 2

额外注意事项

  • 安装filelock依赖:pip install filelock,用于处理多进程文件写入竞争。
  • 并行测试中,pytest_exception_interact需通过request对象获取当前用例的driver,避免全局变量导致的上下文错误。

内容的提问来源于stack exchange,提问作者בני קון

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.29 14:07:34