Introduction ============ 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. .. note:: 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. Users looking for quick helpers may take a look at :doc:`recipes`, while those needing detailed documentation will be interested in the :doc:`reference` section. Basic usage ----------- Factories declare a set of attributes used to instantiate an object, whose class is defined in the ``class Meta``'s ``model`` attribute: - Subclass ``factory.Factory`` (or a more suitable subclass) - Add a ``class Meta:`` block - Set its ``model`` attribute to the target class - Add defaults for keyword args to pass to the associated class' ``__init__`` method .. code-block:: python 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: .. code-block:: pycon >>> john = UserFactory() It is also possible to override the defined attributes by passing keyword arguments to the factory: .. code-block:: pycon >>> jack = UserFactory(firstname="Jack") A given class may be associated to many :class:`~factory.Factory` subclasses: .. code-block:: python 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' .. code-block:: pycon >>> EnglishUserFactory() >>> FrenchUserFactory() Sequences --------- 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:`~factory.Sequence` declaration: .. code-block:: python class UserFactory(factory.Factory): class Meta: model = models.User username = factory.Sequence(lambda n: 'user%d' % n) .. code-block:: pycon >>> UserFactory() >>> UserFactory() .. note:: For more complex situations, you may also use the :meth:`~factory.@sequence` decorator (note that ``self`` is not added as first parameter): .. code-block:: python class UserFactory(factory.Factory): class Meta: model = models.User @factory.sequence def username(n): return 'user%d' % n LazyFunction ------------ In simple cases, calling a function is enough to compute the value. If that function doesn't depend on the object being built, use :class:`~factory.LazyFunction` to call that function; it should receive a function taking no argument and returning the value for the field: .. code-block:: python class LogFactory(factory.Factory): class Meta: model = models.Log timestamp = factory.LazyFunction(datetime.now) .. code-block:: pycon >>> LogFactory() >>> # The LazyFunction can be overriden >>> LogFactory(timestamp=now - timedelta(days=1)) .. note:: For complex cases when you happen to write a specific function, the :meth:`~factory.@lazy_attribute` decorator should be more appropriate. LazyAttribute ------------- Some fields may be deduced from others, for instance the email based on the username. The :class:`~factory.LazyAttribute` handles such cases: it should receive a function taking the object being built and returning the value for the field: .. code-block:: python class UserFactory(factory.Factory): class Meta: model = models.User username = factory.Sequence(lambda n: 'user%d' % n) email = factory.LazyAttribute(lambda obj: '%s@example.com' % obj.username) .. code-block:: pycon >>> UserFactory() >>> # The LazyAttribute handles overridden fields >>> UserFactory(username='john') >>> # They can be directly overridden as well >>> UserFactory(email='doe@example.com') .. note:: As for :class:`~factory.Sequence`, a :meth:`~factory.@lazy_attribute` decorator is available: .. code-block:: python class UserFactory(factory.Factory): class Meta: model = models.User username = factory.Sequence(lambda n: 'user%d' % n) @factory.lazy_attribute def email(self): return '%s@example.com' % self.username Inheritance ----------- Once a "base" factory has been defined for a given class, alternate versions can be easily defined through subclassing. The subclassed :class:`~factory.Factory` will inherit all declarations from its parent, and update them with its own declarations: .. code-block:: python class UserFactory(factory.Factory): class Meta: model = base.User firstname = "John" lastname = "Doe" group = 'users' class AdminFactory(UserFactory): admin = True group = 'admins' .. code-block:: pycon >>> user = UserFactory() >>> user >>> user.group 'users' >>> admin = AdminFactory() >>> admin >>> admin.group # The AdminFactory field has overridden the base field 'admins' Any argument of all factories in the chain can easily be overridden: .. code-block:: pycon >>> super_admin = AdminFactory(group='superadmins', lastname="Lennon") >>> super_admin >>> super_admin.group # Overridden at call time 'superadmins' Non-kwarg arguments ------------------- Some classes take a few, non-kwarg arguments first. This is handled by the :data:`~factory.FactoryOptions.inline_args` attribute: .. code-block:: python class MyFactory(factory.Factory): class Meta: model = MyClass inline_args = ('x', 'y') x = 1 y = 2 z = 3 .. code-block:: pycon >>> MyFactory(y=4) 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 :attr:`~factory.Factory.Params` declaration: .. code-block:: python 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 .. code-block:: pycon >>> RentalFactory(duration=0) 2012-03-03> >>> RentalFactory(duration=10) 2012-12-26> When many fields should be updated based on a flag, use :class:`Traits ` instead: .. code-block:: python 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: .. code-block:: pycon >>> OrderFactory() >>> OrderFactory(shipped=True) Strategies ---------- All factories support two built-in strategies: * ``build`` provides a local object * ``create`` instantiates a local object, and saves it to the database. .. note:: For 1.X versions, the ``create`` will actually call ``AssociatedClass.objects.create``, as for a Django model. Starting from 2.0, :meth:`factory.Factory.create` simply calls ``AssociatedClass(**kwargs)``. You should use :class:`~factory.django.DjangoModelFactory` for Django models. When a :class:`~factory.Factory` includes related fields (:class:`~factory.SubFactory`, :class:`~factory.RelatedFactory`), the parent's strategy will be pushed onto related factories. Calling a :class:`~factory.Factory` subclass will provide an object through the default strategy: .. code-block:: python class MyFactory(factory.Factory): class Meta: model = MyClass .. code-block:: pycon >>> MyFactory.create() >>> MyFactory.build() >>> MyFactory() # equivalent to MyFactory.create() The default strategy can be changed by setting the ``class Meta`` :attr:`~factory.FactoryOptions.strategy` attribute.