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?
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).
@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
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
.
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
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?
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).
@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
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
.
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