Search code examples
pythonpytest

How can I pass fixtures to pytest.mark.parameterize?


I am trying to pass three different fixtures to my pytest.mark.parameterize decorator, like this:

@pytest.mark.parametrize("credentials, return_code", [
    (user1, 200),
    #(user2, 200),
    #(user3, 401)
])
def test_login():
    assert True
# Actual test uses response.return_code == return_code

However, I get an error that reads:

NameError: name 'user1' is not defined

This isn't correct though, I have defined it in conftest.py:

user1_info = [('user1', 'password')]
@pytest.fixture(params=user1_info)
def user1(request):
    return request.param

The fixture works if I change my test to be like this:

def test_login(user1):
    assert True
# Actual test uses response.return_code == return_code

The problem is that if I do it this way, I have to write three separate tests, one for each fixture, and for each status_code check. If I could use parameterize, I could use one function and check the user/expected return code.

How can I utilize my fixtures are part of the pytest.mark.parameterize decorator?


Solution

  • To pass fixtures as parametrized arguments, reference them as strings and use the keyword arguments indirect=['arg_name', ...].

    Then use the parameter names credentials and return_code as arguments to your test function.

    @pytest.mark.parametrize(
        "credentials, return_code",
        [
            ("user1", 200),
            ("user2", 200),
            ("user3", 401),
        ],
        # Only the `credentials` parameter is a reference to the
        # fixture. The `return_code` should be passed as is.
        indirect=["credentials"],
    )
    def test_login(credentials, return_code):
        return True
    

    The documentation for pytest's parametrize magic with indirect can be found here: https://docs.pytest.org/en/stable/example/parametrize.html#apply-indirect-on-particular-arguments