I'm building a number of tests for our django application and I'm using FactoryBoy
The Profile
model has a gender
field which is defined as follows:
class Profile(models.Model):
GENDER_CHOICES = (
(u'm', _(u'Male')),
(u'f', _(u'Female')),
)
gender = models.CharField(
max_length=2, choices=GENDER_CHOICES, verbose_name=_("Gender"),
null=True, blank=True
)
I wanted to randomize the value of this field in factory boy with the following line of code:
class ProfileFactory(factory.Factory):
(...)
gender = factory.LazyFunction(random.choice(['f', 'm']))
However, this throws a TypeError: 'str' object is not callable
error
Using an old blogpost I then tried the following solution, which worked:
gender = factory.LazyAttribute(lambda x: random.choice(['f', 'm']))
This solved the problem, but its unclear to me why it did.
The documentation for factory.LazyFunction
states that:
The LazyFunction is the simplest case where the value of an attribute
does not depend on the object being built.
It takes as argument a method to call (function, lambda…); that method
should not take any argument, though keyword arguments are safe but
unused, and return a value.
It was my understanding that random.choice(['f', 'm'])
constituted a method call and thus should work as I expected it to.
But as it did not my understanding of LazyFunction
is clearly flawed and I was hoping somebody could explain what I am doing wrong here
LazyFunction
requires a callable: something that it can call.
random.choice(['f', 'm'])
returns a string; to make it a callable, the solution is LazyFunction(lambda: random.choice(['f', 'm']))
.
However, the best solution to your issue is to use factory.fuzzy.FuzzyChoice
, designed specifically for your usecase:
gender = factory.fuzzy.FuzzyChoice(['f', 'm'])
This will perform the random calls on its own, and supports proper seeding/randomness management — see https://factoryboy.readthedocs.io/en/latest/reference.html#randomness-management for details.