Error with pytest_sessionfinish when running pytest in parallel with selenium

40 Views Asked by At

I have this code in the conftest file (running automation with pytest + selenium):

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)

The problem happens when running pytest in parallel (pytest -n 5 --dist loadgroup --alluredir=allure-results --reruns 2 --reruns-delay 2). This is due to pytest_sessionfinish adding environment variables to the allure report (for example the browser and browser version). I get an error when running:

  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
             ^^^^^^^^^^^^^^^^^^^^^^

I also tried to add environment variables to the allure report when pytest ran with multiple processes (or parallel testing).

1

There are 1 best solutions below

0
Michael Mintz On

You can use https://github.com/seleniumbase/SeleniumBase (a full framework for selenium + pytest) to solve your multithreading issues: After pip install seleniumbase you can run the following with pytest:

(allure-pytest is installed separately. To activate: pytest --alluredir=allure_results:

from seleniumbase import BaseCase


class BaseTestCase(BaseCase):
    def setUp(self):
        super().setUp()
        self.driver.get("https://magento.softwaretestingboard.com/")

    def tearDown(self):
        self.save_teardown_screenshot()  # If test fails, or if "--screenshot"
        if self.has_exception():
            pass  # <<< Run custom code if the test failed. >>>
        else:
            pass  # <<< Run custom code if the test passed. >>>
        # <<< Run custom tearDown() code BEFORE the super().tearDown() >>>
        super().tearDown()


class MyTests(BaseTestCase):
    def test_1(self):
        self.click('''span:contains("What's New")''')
        self.sleep(1)

    def test_2(self):
        self.type("#search", "Tees and Tops\n")
        self.sleep(1)

    def test_3(self):
        self.hover_and_js_click('span:contains("Gear")', 'span:contains("Bags")')
        self.sleep(1)

    def test_4(self):
        self.hover_and_js_click('span:contains("Gear")', 'span:contains("Watches")')
        self.sleep(1)

    def test_5(self):
        self.click('''span:contains("Sale")''')
        self.sleep(1)

You can customize setUp() and tearDown(), but be sure that the super() part remains. If a test fails, screenshots are automatically saved to the ./latest_logs/ folder. Add to the tearDown() section if you want screenshots saved with Allure.

Headless Selenium Mode can be activated via pytest: pytest --headless.