Pytest use same fixture twice in one function

Question:

For my web server, I have a login fixture that create a user and returns the headers needed to send requests. For a certain test, I need two users. How can I use the same fixture twice in one function?

from test.fixtures import login


class TestGroups(object):

    def test_get_own_only(self, login, login):
         pass
Asked By: danijar

||

Answers:

I do it with Dummy class which will implement fixture functionality. Then just call it from your test. Provide clarify method name to better understand what is your test doing.

import pytest

@pytest.fixture
def login():
    class Dummy:
        def make_user(self):
            return 'New user name'
    return Dummy()

def test_something(login):
    a = login.make_user()
    b = login.make_user()
    assert a == b
Answered By: Sergei Voronezhskii

An alternative is just to copy the fixture function. This is both simple and correctly handles parameterized fixtures, calling the test function with all combinations of parameters for both fixtures. This example code below raises 9 assertions:

import pytest

@pytest.fixture(params=[0, 1, 2])
def first(request):
    return request.param

second = first

def test_double_fixture(first, second):
    assert False, '{} {}'.format(first, second)
Answered By: danijar

I needed my tests to directly call a fixture to overwrite the current instanced result, so I wrote an abstraction layer that contains references to all of my fixtures:

def call_fixture(fixture, session=''):
    return {
        'fixture_name': fixture_name(session),
    }[fixture]

Called with (get_session is another fixture):

call_fixture('fixture_name', get_session)
Answered By: Will the Thrill

I have done it like so:

limits = [10, 20, 30]

@pytest.fixture(params=limits)
def number(request):
    return random.randint(request.param)

@pytest.fixture(params=limits)
def two_numbers(request):
    return number(request), number(request)

def test_numbers(two_numbers):
    num_1, num_2 = two_numbers
    ...
Answered By: Greg Ennis

The trick is to use mark.parametrize with the “indirect” switch, thus:

@pytest.fixture
def data_repeated(request):
    return [deepcopy({'a': 1, 'b': 2}) for _ in range(request.param)]


@pytest.mark.parametrize('data_repeated', [3], indirect=['data_repeated'])
def test(data_repeated):
    assert data_repeated == [
        {'a': 1, 'b': 2},
        {'a': 1, 'b': 2},
        {'a': 1, 'b': 2}]
Answered By: Dunk

If you’re fixture isn’t parameterized (always returns the same value) and you sometimes need multiple "instances" (assuming it’s an object) of that, you could use the "factory as fixture" pattern described here. It’s similar to what @Sergei Voronezhskii suggested, but you can even pass the needed quantity as an argument and you don’t need a dummy class:

@pytest.fixture
def make_user():
    def _make_user(quantity):
        return [User() for _ in range(quantity)]

    return _make_user # return the function, so don't write '()'

# the parametrize part is optional, just for illustration purposes
@pytest.mark.parametrize("quantity", list(range(5))
def test_make_user(quantity, make_user):
    assert len(make_user(quantity) == quantity

Now if your fixture itself is parameterized (using ‘pytest.fixture(params=…)’) and you want all combinations of two values (the Cartesion product), then this will not work and you’re better off using @danijar’s answer. You could also define another fixture ‘double_X’ that requests ‘first’ and ‘second’ and make your test function request ‘double_X’. Depending on your use case this could be more or less verbose than requesting the two fixtures directly at your test function(s).

Whether you parametrize at your fixture, your test or the function body level (creating for loops…) depends on what parts you’ll reuse I guess. So there isn’t a clear cut answer, at least not one I found after digging through the fixtures docs for some hours.

Answered By: Hamburg is nice