Call the same fixture multiple time in py.test

Question:

I’m using Pytest to write some tests in Python.

I’m using the following fixture in order to create a user and user it in my test, then delete it after the test (finalizer):

@pytest.fixture(scope='function')
def test_user(request):
    def finalizer():
        delete_the_user()

    request.addfinalizer(finalizer)
    return user()

with delete_the_user() and user() two functions not detailed here.

The problem is: I’d like to use 2 users for some of my tests.

I’ve tried to call the fixture like that:

def test_function(test_user_1 = test_user, test_user_2 = test_user):
    # Test here

without success.

How can I use the same fixture more than once in a test?

Asked By: superzouzou

||

Answers:

The only ways to do this I can think of are:

  • Having a second fixture
  • Making the fixture return a function instead (but then you’d have to refactor other functions using the fixture too).
Answered By: The Compiler
@pytest.fixture(scope='function')
def test_user(request):
    user_list = []
    def create_user():
        <create_user>
        user_list.append(user)
        return user

    def finalizer(user_list):
        for user in user_list:
            delete_the_user(user)

    request.addfinalizer(finalizer(user_list))
    return create_user
Answered By: SilentGuy

The accepted answer for this question is correct but does not come with an example. The only other example, as I write this, does not work (though it is close).

Basically, you can happily reuse a fixture if that fixture returns a function. If the object created by that fixture needs tearing down, then you can add a "finalizer". Read more about them here

Taking the code from OP and adapting, your fixture ends up looking like this:

@pytest.fixture(scope='function')
def test_user(request):
    user_list = []

    def create_user(*args, **kwargs):
        # CODE TO CREATE USER HERE
        user_list.append(user)
        return user

    def finalizer():
        for user in user_list:
            delete_the_user(user)

    request.addfinalizer(finalizer)
    return create_user

Then, for your test, instead of declaring each user as an input, you can just use the function returned by test_user

def test_function(test_user):
    test_user_1 = test_user()
    test_user_2 = test_user()
    # Test here

Each time you call test_user(), the user gets added to the same instance of user_list. Once the test is finished (or fails), it calls the finalizer, which has access to user_list.

Answered By: freethebees

One simple way to do this is to define function inside your fixture function:

Inside conftests.py

@pytest.fixture
def many_names():
    def name_generator():
        letters = string.ascii_lowercase
        random_string = "".join(random.choice(letters) for i in range(10))
        return random_string
    return name_generator

Then inside your test file:

def test_post_many_names(
        self, many_names
    ):        
        a = many_names()
        # now in variable a you will have first random name. For exmpale "hdfkjdhhfk"
        b = many_names()
        # when you call second time your fixture in variable b you will 
        # have second random name. For example "fjhsdfjkshksdh" 
       
        # here you put rest of your test
        ...
        assert something != something
Answered By: Ilija
Categories: questions Tags: , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.