Join GitHub today
GitHub is home to over 31 million developers working together to host and review code, manage projects, and build software together.Sign up
Multiple use of fixture in a single test #2703
Feature request to add the ability to use a fixture more than once in the test function arguments. Off the top of my head, appending a number to the fixture name could be a decent API. Obviously don't override existing fixtures and perhaps require the appended numbers to be ascending.
@pytest.fixture def rand_int(): yield random.randint(0, 100) def test_rand_ints(rand_int0, rand_int1, rand_int2): # do something with 3 random integers
I can get around this by creating a fixture that generates values.
@python.fixture def randints(): def gen(count): for x in range(count): yield random.randint(0, 100) yield gen def test_randint_gen(randints): r1, r2, r3 = randints(3) # do something with 3 random integers
I realize these examples could just be utility functions, but consider the following examples that require cleanup.
@python.fixture def user(): u = models.User() u.commit() yield u u.delete() @python.fixture def usergen(request): def gen(count): for x in range(count): u = models.User() u.commit() request.addfinalizer(lambda u=u: u.delete()) yield u yield gen def test_user(user0, user1, user2): # do something with 3 users def test_usergen(usergen): user1, user2, user3 = usergen(3) # do something with 3 users
I think this deserves debate as my suggestion requires less code and I believe to be more intuitive.
It should be noted that adding scope to either solution will be confusing and probably not recommended (e.g.,
How would we handle fixture inter-dependencies with this feature?
For example, from the original example, what happens if another fixture also depends on the
@pytest.fixture def subscribed_user(user): # subscribe user to newsletter return Subscription(user) def test_user(user0, user1, user2, subscribed_user): ...
Which of the
Also, one of the criticisms of
This is partly what sparked this proposal. Honestly, I don't like that the same fixture value is used for every instance. I realize it's a function scope, but most likely, I can access
@pytest.fixture def test_user(user, subscribed_user): assert user != subscribed_user.user # I want this to succeed
Perhaps another scope such as
I very much like @massich's keyword argument suggestion.
I see @defrank, thanks. I've seen people on occasion with the same impression, but I think the current system is more powerful because reusing fixture instances within the same context is what allows you to actually create more complex systems.
For example, consider the
By the way, a simple way to obtain the functionality you need is to make your fixture a factory instead; that gives you full control when and where to create the instances.
def test_user(user_factory): user0, user1 = user_factory.create('calvin', 'hobbes')
For convenience, you might also have a
@pytest.fixture def user(user_factory): return user_factory.create('jonh')
Pytest itself follows the same approach with the
Essentially, mark factory fixtures.
@pytest.factory def user_factory(request): def factory(*names): for name in names: user = models.User(name=name) user.commit() request.addfinalizer(lambda u=user: u.delete()) yield user yield factory
@pytest.fixture def user(bob=user_factory): yield bob def test_user_factory(user, amy=user_factory, Joe=user_factory): assert user.name == 'bob' assert amy.name == 'amy' assert Joe.name == 'joe'
I don't like this:
@pytest.fixture def user(user_0='bob'): yield user_0 def test_user_factory(user, user_1='joe', user_amy='amy'): assert user.name == 'bob' assert user_1.name == 'joe' assert user_amy.name == 'amy'
Since my factory requires names, I'm not considering the following, although it should be considered:
def test_user_factory(user, user_amy, user_john): assert user != user_amy assert user != user_john assert user_amy != user_john
referenced this issue
Apr 4, 2018
What is the status of this? I find this is pretty common for fixtures that model some kind of database object. For instance, I have a fixture for a user model. That's great if I only need one. But what if I'm writing a test that needs more than user (because it has to test some kind of interaction between them)? If the
. . . because calling
It seems like handling the teardown is done by
It seems like at the least it should be possible to provide a context manager wrapper around fixture functions that allows tests to preserve the teardown when calling the fixture function manually. So a test could do:
This would at least let test functions use a fixture more than once by manually calling it multiple times from within the test function body.
One solution is to make your fixture return a factory instead of the resource directly:
@pytest.fixture(name='make_user') def make_user_(): created =  def make_user(): u = models.User() u.commit() created.append(u) return u yield make_user for u in created: u.delete() def test_two_users(make_user): user1 = make_user() user2 = make_user() # test them # you can even have the normal fixture when you only need a single user @pytest.fixture def user(make_user): return make_user() def test_one_user(user): # test him/her
This is what we have with