The purpose of factory_boy is to provide a default way of getting a new instance, while still being able to override some fields on a per-call basis.
This section will drive you through an overview of factory_boy’s feature. New users are advised to spend a few minutes browsing through this list of useful helpers.
Factories declare a set of attributes used to instantiate an object, whose class is defined in the
factory.Factory(or a more suitable subclass)
- Add a
- Set its
modelattribute to the target class
- Add defaults for keyword args to pass to the associated class’
import factory from . import base class UserFactory(factory.Factory): class Meta: model = base.User firstname = "John" lastname = "Doe"
You may now get
base.User instances trivially:
>>> john = UserFactory() <User: John Doe>
It is also possible to override the defined attributes by passing keyword arguments to the factory:
>>> jack = UserFactory(firstname="Jack") <User: Jack Doe>
A given class may be associated to many
class EnglishUserFactory(factory.Factory): class Meta: model = base.User firstname = "John" lastname = "Doe" lang = 'en' class FrenchUserFactory(factory.Factory): class Meta: model = base.User firstname = "Jean" lastname = "Dupont" lang = 'fr'
>>> EnglishUserFactory() <User: John Doe (en)> >>> FrenchUserFactory() <User: Jean Dupont (fr)>
When a field has a unique key, each object generated by the factory should have a different value for that field.
This is achieved with the
class UserFactory(factory.Factory): class Meta: model = models.User username = factory.Sequence(lambda n: 'user%d' % n)
>>> UserFactory() <User: user0> >>> UserFactory() <User: user1>
For more complex situations, you may also use the
@sequence() decorator (note that
self is not added as first parameter):
class UserFactory(factory.Factory): class Meta: model = models.User @factory.sequence def username(n): return 'user%d' % n
In simple cases, calling a function is enough to compute the value. If that function doesn’t depend on the object
being built, use
LazyFunction to call that function; it should receive a function taking no
argument and returning the value for the field:
class LogFactory(factory.Factory): class Meta: model = models.Log timestamp = factory.LazyFunction(datetime.now)
>>> LogFactory() <Log: log at 2016-02-12 17:02:34> >>> # The LazyFunction can be overriden >>> LogFactory(timestamp=now - timedelta(days=1)) <Log: log at 2016-02-11 17:02:34>
For complex cases when you happen to write a specific function,
@lazy_attribute() decorator should be more appropriate.
Some fields may be deduced from others, for instance the email based on the username.
LazyAttribute handles such cases: it should receive a function
taking the object being built and returning the value for the field:
class UserFactory(factory.Factory): class Meta: model = models.User username = factory.Sequence(lambda n: 'user%d' % n) email = factory.LazyAttribute(lambda obj: '%email@example.com' % obj.username)
@lazy_attribute() decorator is available:
class UserFactory(factory.Factory): class Meta: model = models.User username = factory.Sequence(lambda n: 'user%d' % n) @factory.lazy_attribute def email(self): return '%firstname.lastname@example.org' % self.username
Once a “base” factory has been defined for a given class, alternate versions can be easily defined through subclassing.
Factory will inherit all declarations from its parent,
and update them with its own declarations:
class UserFactory(factory.Factory): class Meta: model = base.User firstname = "John" lastname = "Doe" group = 'users' class AdminFactory(UserFactory): admin = True group = 'admins'
>>> user = UserFactory() >>> user <User: John Doe> >>> user.group 'users' >>> admin = AdminFactory() >>> admin <User: John Doe (admin)> >>> admin.group # The AdminFactory field has overridden the base field 'admins'
Any argument of all factories in the chain can easily be overridden:
>>> super_admin = AdminFactory(group='superadmins', lastname="Lennon") >>> super_admin <User: John Lennon (admin)> >>> super_admin.group # Overridden at call time 'superadmins'
Some classes take a few, non-kwarg arguments first.
This is handled by the
class MyFactory(factory.Factory): class Meta: model = MyClass inline_args = ('x', 'y') x = 1 y = 2 z = 3
>>> MyFactory(y=4) <MyClass(1, 4, z=3)>
Altering a factory’s behaviour: parameters and traits¶
Some classes are better described with a few, simple parameters, that aren’t fields on the actual model.
In that case, use a
class RentalFactory(factory.Factory): class Meta: model = Rental begin = factory.fuzzy.FuzzyDate(start_date=datetime.date(2000, 1, 1)) end = factory.LazyAttribute(lambda o: o.begin + o.duration) class Params: duration = 12
>>> RentalFactory(duration=0) <Rental: 2012-03-03 -> 2012-03-03> >>> RentalFactory(duration=10) <Rental: 2008-12-16 -> 2012-12-26>
When many fields should be updated based on a flag, use
class OrderFactory(factory.Factory): status = 'pending' shipped_by = None shipped_on = None class Meta: model = Order class Params: shipped = factory.Trait( status='shipped', shipped_by=factory.SubFactory(EmployeeFactory), shipped_on=factory.LazyFunction(datetime.date.today), )
A trait is toggled by a single boolean value:
>>> OrderFactory() <Order: pending> >>> OrderFactory(shipped=True) <Order: shipped by John Doe on 2016-04-02>
All factories support two built-in strategies:
buildprovides a local object
createinstantiates a local object, and saves it to the database.
For 1.X versions, the
create will actually call
as for a Django model.
Factory subclass will provide an object through the default strategy:
class MyFactory(factory.Factory): class Meta: model = MyClass
>>> MyFactory.create() <MyFactory: X (saved)> >>> MyFactory.build() <MyFactory: X (unsaved)> >>> MyFactory() # equivalent to MyFactory.create() <MyClass: X (saved)>
The default strategy can be changed by setting the