[TIL] Using Fixtures with pytest `parametrize`

[TIL] Using Fixtures with pytest `parametrize`

For longer than I care to admit, I was running my end to end tests in only Firefox. I thought it would be trivial to add a fixture to have my tests run using multiple browsers. Unfortunately, there isn't an easy way in pytest to use fixtures as parametrized values*. Here is the solution I landed on:

@pytest.fixture(scope="session")
def firefox():
    return webdriver.Firefox(options=options)

@pytest.fixture(scope="session")
def chrome():
    return webdriver.Chrome()

@pytest.mark.parametrize("browser", ["firefox", "chrome"])
def test_e2e_report(
    browser, request
):
    browser = request.getfixturevalue( # required to use firefox and chrome fixtures
        browser
    )

We are passing string values (["firefox", "chrome"]) into the test with parametrize, then we retrieve the fixture (object) with that name using request.getfixturevalue(browser) built into pytest (link that helped me find this). It's hacky and I don't like it because an IDE won't be able to trace it, but it works!

Related: This doubled the number of tests I was running which started giving me failures because too many connections were open. Running my local server with python manage.py runserver --nothreading stopped those errors from popping up.

*H/T to Jeff Triplett for suggesting pytest-lazy-fixture as an alernative. If I end up doing this in more places in the future, I'd consider using this library. Update: Jeff posted an example of this recently.

Josh Thomas has another way of doing this. Similar to the above, this solution uses strings for the parametrize arguments, but instead of using getfixturevalue, it imports the fixtures as you normally would in a function then uses conditionals (I suppose you could also use a switch statement if you have more parameters) to match the string and the fixture.