pythonpython-3.xselenium-webdriverpytestpytest-fixtures

Teardown method from add_finalizer of PyTest fixture doesn't work


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

Solution

  • 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.