Dynamically parametrizing a Pytest fixture based on options/configuration

Viewed 122

I have a basic Pytest test suite working, and I need to dynamically parametrize a fixture at runtime based on configuration/arguments.

The "heart" of my test suite is a session-scoped fixture that does some very expensive initialization based on some custom CLI arguments, and all of the tests use this fixture. To oversimplify a bit, think of the fixture as providing a remote connection to a server to run the tests on, the initialization involves configuring the OS/dependencies on that server (hence why it's expensive and session-scoped), and the CLI arguments specify aspects of the OS/environment configuration.

This works great for individual test runs, but now I want to take this test suite and run the entire thing over a list of different test configurations. The list of configurations needs to be determined at runtime based on a configuration file and some additional CLI arguments.

As I understand it, the "obvious" way of doing this kind of thing in Pytest is by parametrizing my initialization fixture. But the problem is my need to do it dynamically based on other arguments; it looks like you can call a function to populate the params list in @pytest.fixture, but that function wouldn't have access to the Pytest CLI args, since arg parsing hasn't happened yet.

I've managed to figure out how to do this by implementing pytest_generate_tests to dynamically parametrize each test using indirect parametrization, and taking advantage of fixture caching to ensure that each unique fixture configuration is only actually initialized once, even though the fixture is actually being parametrized for each test. This is working, although it's questionable whether Pytest officially supports this. Is there a better way of achieving what I'm trying to do here?


The "Right" way

def generate_parameters():
  # This is just a plain function, and arg parsing hasn't happened yet,
  # so I can't use Pytest options to generate my parameters
  pass

@pytest.fixture(scope="session", params=generate_parameters())
def expensive_init_fixture(pytestconfig, request):
  # Do initialization based on CLI args and request.param
  pass

The way that works

@pytest.fixture(scope="session")
def expensive_initialization_fixture(pytestconfig, request):
  # Do initialization based on CLI args and request.param
  pass

def pytest_generate_tests(metafunc):
  if 'expensive_init_fixture' in metafunc.fixturenames:
    parameters = [] # Generate parameters based on config in metafunc.config
    metafunc.parametrize('expensive_init_fixture', parameters, scope="session", indirect=True)
1 Answers

I'm not sure if what im seggusting is what your looking for, but using pytest_testconfig sounds a good solution. testconfig lets you use a global config variables in all tests, you can set theyre value however you like, meaning you can get them from the CLI.

This means you can have the default value of config when you start a run, and if you want to change it in the next run, you take the param from the CLI.

I'm not sure what them motive beheind this, but you should see if you really need a global_config.py file here, changing tests values between runs shouldn't be something configurable from the CLI, tests are meant to be persistant, and if something needs changing then a commit is a better solution since you can see it's history.

Related