1

I'm trying to make a fixture for cross-browser tests with PyTest. From @pytest.mark.parametrize fixture gets "browser_name" and sets up correct webdriver in internal method. In the end of tests I tried to add a teardowm method to close browser, but I noticed that it just doesn't work. Can anyone help?

In my conftest.py file I have:

import pytest
from selenium.webdriver.chrome.service import Service as ChromeService
from selenium.webdriver.firefox.service import Service as FirefoxService
from selenium.webdriver.edge.service import Service as EdgeService
from webdriver_manager.chrome import ChromeDriverManager
from webdriver_manager.firefox import GeckoDriverManager
from webdriver_manager.microsoft import EdgeChromiumDriverManager
from selenium import webdriver

@pytest.fixture()
def WebDriverFixture(request):
    def driverSetup(browser_name):
        print("\nStarting browser...\n")
        
        match browser_name:
            case "chrome":
                options = webdriver.ChromeOptions()
                options.add_argument('excludeSwitches')
                options.add_argument('--ignore-ssl-errors=yes')
                options.add_argument('--ignore-certificate-errors')
                options.add_experimental_option("excludeSwitches", ["enable-logging"])
                driver = webdriver.Chrome(service=ChromeService(ChromeDriverManager().install()), options=options)
                driver.implicitly_wait(10)
                return driver
                
            case "firefox":
                options = webdriver.FirefoxOptions()
                options.add_argument('excludeSwitches')
                options.add_argument('--ignore-ssl-errors=yes')
                options.add_argument('--ignore-certificate-errors')
                options.set_preference("excludeSwitches", "enable-logging")
                options.set_preference("dom.disable_beforeunload", True)
                driver = webdriver.Firefox(service=FirefoxService(GeckoDriverManager().install()), options=options)
                driver.implicitly_wait(10)
                return driver
                
            case "edge":
                options = webdriver.EdgeOptions()
                options.add_argument('excludeSwitches')
                options.add_argument('--ignore-ssl-errors=yes')
                options.add_argument('--ignore-certificate-errors')
                options.accept_insecure_certs = True
                driver = webdriver.Edge(service=EdgeService(EdgeChromiumDriverManager().install()), options=options)
                driver.implicitly_wait(10)
                return driver

        def teardown(driverSetup):
            driver = driverSetup(browser_name)
            print("\nQuitting browser...")
            driver.quit()
        request.add_finalizer(teardown(driverSetup))  
        
    return driverSetup

In test file I have:

import pytest
from pages.login_page import LoginPage
from pages.base_page import BasePage
import time

@pytest.mark.parametrize('browser_name', ['chrome'])
def test_negative_cases(WebDriverFixture, browser_name):
    driver = WebDriverFixture(browser_name)
    #tests
    ...

1 Answers1

1

Using yield is a better option than using return for your scenario, because you want to go back and perform another action at the end (closing the browser).

I've modified the code (see below) to show a minimally working example that handles the pytest parameterization and closes browsers at the end.

from selenium import webdriver
import pytest
import time

@pytest.fixture
def WebDriverFixture(request):
    driver = None
    browser_name = request.getfixturevalue("browser_name")
    if browser_name.lower() == "chrome":
        driver = webdriver.Chrome()
    elif browser_name.lower() == "firefox":
        driver = webdriver.Firefox()
    elif browser_name.lower() == "edge":
        driver = webdriver.Edge()
    else:
        raise Exception("Unknown browser")
    yield driver
    try:
        driver.quit()
    except Exception:
        pass

@pytest.mark.parametrize('browser_name', ['chrome', 'firefox', 'edge'])
def test_negative_cases(WebDriverFixture, browser_name):
   driver = WebDriverFixture
   driver.get("https://example.com")
   time.sleep(1)

The latest version of selenium already includes webdriver_manager now, so you no longer need to add that separately.

Michael Mintz
  • 9,007
  • 6
  • 31
  • 48
  • Thank you very much! I still have a few questions: 1. In your last state you meant that webdriver.Chrome() will automatically download up to date webdriver version? 2. Can your solution be edited with @pytest.fixture(params=["chrome", "firefox", "edge"]) to mention browser list only once and not to do it above every test method? – Danila Sukhoparov Jun 29 '23 at 22:22
  • 1
    1. `webdriver.Chrome()` will automatically download a driver that matches your browser version. 2. As a pytest fixture, that line will need to be placed on each test that needs to be parameterized. There are other ways if not using pytest fixtures... a solution for a different question. – Michael Mintz Jun 29 '23 at 22:33
  • But can't we just use @pytest.fixture(params=...) instead of parameterization of single test?. Because params will call fuxture as many times as many params we put in it for every test method. – Danila Sukhoparov Jun 29 '23 at 23:40
  • Perhaps. Check the documentation: https://docs.pytest.org/en/latest/reference/reference.html#fixtures – Michael Mintz Jun 29 '23 at 23:55