Reputation: 20300
I am using Pytest with Django and came to this weird behaviour. I have two user fixtures, one being a superset of the other. Everything works as expected until I use both fixtures in the same test case.
Fixtures:
@pytest.fixture
def user_without_password():
return User.objects.create_user(username=fake.name(), email=fake.email())
@pytest.fixture
def user_with_password(user_without_password):
user = user_without_password
user.set_password('topsecret')
user.save()
return user
Tests
@pytest.mark.django_db()
def test_without_pass(user_without_password):
assert not user_without_password.has_usable_password()
@pytest.mark.django_db()
def test_with_pass(user_with_password):
assert user_with_password.has_usable_password()
# THIS FAILS!!
@pytest.mark.django_db()
def test_both(user_with_password, user_without_password):
assert not user_without_password.has_usable_password()
assert user_with_password.has_usable_password()
The last test doesn't work since apparently user_with_password
and user_without_password
end up being the same object. Is there a way to ensure that they are new objects each time? This behavior feels counter-intuitive.
Upvotes: 2
Views: 1305
Reputation: 8159
pytest fixtures are designed to be efficient – i.e. if a fixture is requested multiple times it is only created once. That means you can always request a fixture from another fixture and be sure you're using the same object as the test.
Further, if you read your user_with_password
fixture like this:
Then it makes sense that the fixture which returns the user it created without a password continues to return that user, but now it's had a password added.
If you want to get around this, then create a fixture that creates new objects rather than just a single object, something like:
@pytest.fixture
def user_without_password_factory():
def create_user_without_password():
return User.objects.create_user(username=fake.name(), email=fake.email())
return create_user_without_password
@pytest.fixture
def user_with_password_factory():
def create_user_with_password():
user = User.objects.create_user(username=fake.name(), email=fake.email())
user.set_password('topsecret')
user.save()
return user
return create_user_with_password
@pytest.mark.django_db()
def test_without_pass(user_without_password_factory):
assert not user_without_password_factory().has_usable_password()
@pytest.mark.django_db()
def test_with_pass(user_with_password_factory):
assert user_with_password_factory().has_usable_password()
# Succeeds!!
@pytest.mark.django_db()
def test_both(user_with_password_factory, user_without_password_factory):
assert not user_without_password_factory().has_usable_password()
assert user_with_password_factory().has_usable_password()
Upvotes: 3