I am writing tests for a large Django application with multiple apps. As part of this process I am gradually creating factories for all models of the different apps within the Django project.
However, I've run into some confusing behavior with FactoryBoy
Our app uses Profiles
which are linked to the default auth.models.User
model with a OneToOneField
class Profile(models.Model):
user = models.OneToOneField(User)
birth_date = models.DateField(
verbose_name=_("Date of Birth"), null=True, blank=True)
( ... )
I created the following factories for both models:
@factory.django.mute_signals(post_save)
class ProfileFactory(factory.django.DjangoModelFactory):
class Meta:
model = profile_models.Profile
user = factory.SubFactory('yuza.factories.UserFactory')
birth_date = factory.Faker('date_of_birth')
street = factory.Faker('street_name')
house_number = factory.Faker('building_number')
city = factory.Faker('city')
country = factory.Faker('country')
avatar_file = factory.django.ImageField(color='blue')
tenant = factory.SubFactory(TenantFactory)
@factory.django.mute_signals(post_save)
class UserFactory(factory.django.DjangoModelFactory):
class Meta:
model = auth_models.User
username = factory.Faker('user_name')
first_name = factory.Faker('first_name')
last_name = factory.Faker('last_name')
email = factory.Faker('email')
is_staff = False
is_superuser = False
is_active = True
last_login = factory.LazyFunction(timezone.now)
profile = factory.RelatedFactory(ProfileFactory, 'user')
Which I then run the followings tests for:
class TestUser(TestCase):
def test_init(self):
""" Verify that the factory is able to initialize """
user = UserFactory()
self.assertTrue(user)
self.assertTrue(user.profile)
self.assertTrue(user.profile.tenant)
class TestProfile(TestCase):
def test_init(self):
""" Verify that the factory is able to initialize """
profile = ProfileFactory()
self.assertTrue(profile)
All tests in TestUser
pass, but the TestProfile
fails on the factory initialization ( profile = ProfileFactory()
) and raises the following error:
IntegrityError: duplicate key value violates unique constraint "yuza_profile_user_id_key"
DETAIL: Key (user_id)=(1) already exists.
Its not clear to me why a duplicate User would already exist, (there should only be one call to create one right?, especially since any interfering signals have been disabled)
My code was based on the example from the FactoryBoy documentation, which also dealt with Users / Profiles that are connected via a OneToOneKey
Does anyone know what I am doing wrong?
Update
As per the suggestions of both Bruno and ivissani I've changed the user
line in the ProfileFactory
to
user = factory.SubFactory('yuza.factories.UserFactory', profile=None)
Now all the tests described above pass successfully!
However I still run into the following issue - when other factories call the UserFactory
the
IntegrityError: duplicate key value violates unique constraint "yuza_profile_user_id_key"
DETAIL: Key (user_id)=(1) already exists.
still returns.
I've included an example of a factory calling the UserFactory
below, buts its happening to every factory that has a user
field.
class InvoiceFactory(factory.django.DjangoModelFactory):
class Meta:
model = Invoice
user = factory.SubFactory(UserFactory)
invoice_id = None
title = factory.Faker('catch_phrase')
price_paid = factory.LazyFunction(lambda: Decimal(0))
tax_rate = factory.LazyFunction(lambda: Decimal(1.21))
invoice_datetime = factory.LazyFunction(timezone.now)
Changing the user
field on the InvoiceFactory
to
user = factory.SubFactory(UserFactory, profile=None)
Helps it pass some of the tests, but eventually runs into trouble since it no longer has a profile associated with it.
Weirdly the following (declaring the user before the factory) DOES work:
self.user = UserFactory()
invoice_factory = InvoiceFactory(user=self.user)
Its not clear to me why I still keep running into the IntegrityError
here, calling the UserFactory()
now works fine.
profile=None
to theSubFactory
field definition. Not quite sure why you getIntegrityError
but yourProfileFactory
should look likeuser = factory.SubFactory('yuza.factories.UserFactory', profile=None)
– Verbify