Im' working on an open source django web app, and I'm looking to use Factory Boy to help me setting up models for some tests, but after a few hours reading the docs and looking at examples, I think I need to accept defeat and ask here.
I have a Customer model which looks a bit like this:
class Customer(models.Model):
def save(self, *args, **kwargs):
if not self.full_name:
raise ValidationError('The full_name field is required')
super(Customer, self).save(*args, **kwargs)
user = models.OneToOneField(
settings.AUTH_USER_MODEL,
on_delete=models.SET_NULL,
related_name='customer',
null=True
)
created = models.DateTimeField()
created_in_billing_week = models.CharField(max_length=9)
full_name = models.CharField(max_length=255)
nickname = models.CharField(max_length=30)
mobile = models.CharField(max_length=30, default='', blank=True)
gocardless_current_mandate = models.OneToOneField(
BillingGoCardlessMandate,
on_delete=models.SET_NULL,
related_name='in_use_for_customer',
null=True,
)
I am also using the standard Django User Model, from django.contrib.auth.
Here's my factory code:
class UserFactory(DjangoModelFactory):
class Meta:
model = get_user_model()
class CustomerFactory(DjangoModelFactory):
class Meta:
model = models.Customer
full_name = fake.name()
nickname = factory.LazyAttribute(lambda obj: obj.full_name.split(' ')[0])
created = factory.LazyFunction(timezone.now)
created_in_billing_week = factory.LazyAttribute(lambda obj: str(get_billing_week(obj.created)))
mobile = fake.phone_number()
user = factory.SubFactory(UserFactory, username=nickname,
email="{}@example.com".format(nickname))
In my case, I want to be able to generate a customer like so
CustomerFactory(fullname="Joe Bloggs")
And have the corresponding user generated, with the correct username, and email address.
Right now, I'm getting this error:
AttributeError: The parameter full_name is unknown. Evaluated attributes are {'email': '<factory.declarations.LazyAttribute object at 0x111d999e8>@example.com'}, definitions are {'email': '<factory.declarations.LazyAttribute object at 0x111d999e8>@example.com', 'username': <DeclarationWrapper for <factory.declarations.LazyAttribute object at 0x111d999e8>>}.
I think this is because I'm relying on a lazy attribute here in the customer, which isn't called before the user factory is created.
How should I be doing this if I want to be able to use a factory to create the Customer model instance, with a corresponding user as described above?
For what it's worth the full model is visible here on the github repo
In that case, the best way is to pick values from the customer declarations:
class CustomerFactory(DjangoModelFactory):
class Meta:
model = models.Customer
full_name = factory.Faker('name')
nickname = factory.LazyAttribute(lambda obj: obj.full_name.split(' ')[0])
created = factory.LazyFunction(timezone.now)
created_in_billing_week = factory.LazyAttribute(lambda obj: str(get_billing_week(obj.created)))
mobile = factory.Faker('phone_number')
user = factory.SubFactory(
UserFactory,
username=factory.SelfAttribute('..nickname'),
email=factory.LazyAttribute(lambda u: "{}@example.com".format(u.username)))
)
Also, use factory.Faker('field_name')
to get random values for each instance: the line fullname = fake.name()
in the class declaration is equivalent to:
DEFAULT_FULL_NAME = fake.name()
class CustomerFactory(DjangoModelFactory):
full_name = DEFAULT_FULL_NAME
class Meta:
model = models.customer
Whereas factory.Faker('name')
is equivalent to:
class CustomerFactory(DjangoModelFactory):
full_name = factory.LazyFunction(fake.name)
class Meta:
model = models.customer
i.e fake.name()
will provide a different name to each model built with this factory.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With