Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can I parameterize a pytest fixture with other fixtures?

I have a python test that uses a fixture for credentials (a tuple of userid and password)

def test_something(credentials)
   (userid, password) = credentials
   print("Hello {0}, welcome to my test".format(userid))

and I have pytest fixture for credentials:

@pytest.fixture()
def credentials():
   return ("my_userid", "my_password")

It works great

Now I want to extend this for multiple credentials (say staging and production) so that my test will run twice (once each for staging and production).

I thought that parameterization was the answer, but it seems I can't parameterize with fixtures.

I would love to do something like this:

@pytest.fixture(params=[staging_credentials, production_credentials])
def credentials(request):
    return request.param

where staging_credentials and production_credentials are all fixtures:

@pytest.fixture()
def staging_credentials():
   return ("staging_userid", "staging_password")

@pytest.fixture()
def production_credentials():
   return ("prod_userid", "prod_password")

but apparently parameters to the fixture can't be other fixtures.

Any suggestions on how to elegantly handle this? I've looked at https://docs.pytest.org/en/latest/proposals/parametrize_with_fixtures.html but that seems a but too brute force.

Thanks! Steve

like image 351
Steve Heyman Avatar asked Oct 25 '17 20:10

Steve Heyman


1 Answers

Indirect parametrization is the answer. So, the parameters to the fixtures CAN be other fixtures (by their name/code).

import pytest

all_credentials = {
    'staging': ('user1', 'pass1'),
    'prod': ('user2', 'pass2'),
}

@pytest.fixture
def credentials(request):
    return all_credentials[request.param]

@pytest.mark.parametrize('credentials', ['staging', 'prod'], indirect=True)
def test_me(credentials):
    pass

Technically, you can not only get a dict value by its key, but generate the credentials result based on request.param, and this param will be the value passed to the same-name parameter of the test.

If you want other fixtures to be used (probably because of the setup/teardown stages, as this is the only reason to do so):

import pytest

@pytest.fixture
def credentials(request):
    return request.getfuncargvalue(request.param)

@pytest.fixture()
def staging_credentials():
   return ("staging_userid", "staging_password")

@pytest.fixture()
def production_credentials():
   return ("prod_userid", "prod_password")

@pytest.mark.parametrize('credentials', ['staging_credentials', 'production_credentials'], indirect=True)
def test_me(credentials):
    pass

Here, request.getfuncargvalue(...) will return a fixture value by the fixture name, dynamically.

like image 67
Sergey Vasilyev Avatar answered Nov 12 '22 01:11

Sergey Vasilyev