-
-
Notifications
You must be signed in to change notification settings - Fork 2.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Multiple use of fixture in a single test #2703
Comments
that would require fixtures to match argument names in a new way, |
If the argument names matching needs to be reworked anyway, would this call be easier? @pytest.fixture
def rand_int():
yield random.randint(0, 100)
def test_rand_ints(r0=rand_int, r1=rand_int, r3=rand_int):
# do something with 3 random integers |
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 |
well - from my pov - until we rework the fixture mechanism into a externally usable library its going to be a huge disservice for any user |
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. |
we tried to introduce such scopes, but it was demonstrate that this currently breaks the world - internal refactorings are needed |
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 |
Yeah, the |
Hmm how would that work? Could you provide an usage example? |
Oh I must have forgotten about it, sorry about that. |
FactoryEssentially, 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 Idea 1@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:
Idea 2Require @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 |
imho we need a mechanism that handles a mapping of parameter names and fixtures/parameter sets all proposals i have seen here so far cant even hope to fit the bill |
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. |
@BrenBarn nothing happened because even the underpinnings for this need quite some work - which currently isn't available in terms of able manpower |
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 |
Thanks, that is useful. Are there any examples of that in the docs? |
Duplicate of #456 I believe, if not feel free to reopen (let's consolidate discussion on this there) |
this one is about something slightly different |
if you only need cleanup, why not something like this? from contextlib import contextmanager, ExitStack
import pytest
@contextmanager
def user_getter(name):
print(f"setup {name}")
try:
yield name.upper()
finally:
print(f"teardown {name}")
@pytest.fixture
def user():
with ExitStack() as stack:
def callable(name):
return stack.enter_context(user_getter(name))
yield callable
def test_one_user(user):
assert "JOHN" == user("john")
def test_two_users(user):
assert "JOHN" == user("john")
assert "JANE" == user("jane") edit: i'm realizing now that this has already been suggested |
I have an idea for an API design that I think would be nice. Pytest's fixtures work quite similarly to FastAPI's concept of dependencies, and it seems like a similar API would solve for the issues brought up on this ticket. I also believe that it should be possible to implement it while maintaining the current behavior of implicitly referencing fixtures by name, but expand on that to also allow arbitrarily named parameters by providing a method of explicitly mapping them to fixtures. import pytest
@pytest.fixture
def user() -> User:
...
def test_implicit_user(user):
assert isinstance(user, User)
def test_explicit_user(explicit=pytest.Depends(user)):
assert isinstance(explicit, User)
def test_many_users(
first=pytest.Depends(user),
second=pytest.Depends(user),
):
assert isinstance(first, User)
assert isinstance(second, User)
assert first != second The implementation would have to be slightly different towards FastAPI's in that multiple invocations for a single test function should lead to one call to the fixture function per parameter. FastAPI caches and shares resolved objects through the graph of dependencies, which might make sense in some cases here too. |
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.
I can get around this by creating a fixture that generates values.
I realize these examples could just be utility functions, but consider the following examples that require cleanup.
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.,
@fixture(scope='module')
).The text was updated successfully, but these errors were encountered: