Note
Most recipes below take on Django model examples, but can also be used on their own.
When one attribute is actually a complex field (e.g a :class:`~django.db.models.ForeignKey` to another :class:`~django.db.models.Model`), use the :class:`~factory.SubFactory` declaration:
# models.py
class User(models.Model):
first_name = models.CharField()
group = models.ForeignKey(Group)
# factories.py
import factory
from . import models
class UserFactory(factory.django.DjangoModelFactory):
class Meta:
model = models.User
first_name = factory.Sequence(lambda n: "Agent %03d" % n)
group = factory.SubFactory(GroupFactory)
If the target of the :class:`~django.db.models.ForeignKey` should be chosen from a pre-populated table (e.g :class:`django.contrib.contenttypes.models.ContentType`), simply use a :class:`factory.Iterator` on the chosen queryset:
import factory, factory.django
from . import models
class UserFactory(factory.django.DjangoModelFactory):
class Meta:
model = models.User
language = factory.Iterator(models.Language.objects.all())
Here, models.Language.objects.all()
is a
:class:`~django.db.models.query.QuerySet` and will only hit the database when
factory_boy
starts iterating on it, i.e on the first call to
UserFactory
; thus avoiding DB queries at import time.
When a related object should be created upon object creation (e.g a reverse :class:`~django.db.models.ForeignKey` from another :class:`~django.db.models.Model`), use a :class:`~factory.RelatedFactory` declaration:
# models.py
class User(models.Model):
pass
class UserLog(models.Model):
user = models.ForeignKey(User)
action = models.CharField()
# factories.py
class UserFactory(factory.django.DjangoModelFactory):
class Meta:
model = models.User
log = factory.RelatedFactory(
UserLogFactory,
factory_related_name='user',
action=models.UserLog.ACTION_CREATE,
)
When a :class:`UserFactory` is instantiated, factory_boy will call
UserLogFactory(user=that_user, action=...)
just before returning the created User
.
Django (<1.5) provided a mechanism to attach a Profile
to a User
instance,
using a :class:`~django.db.models.OneToOneField` from the Profile
to the User
.
A typical way to create those profiles was to hook a post-save signal to the User
model.
Prior to version 2.9, the solution to this was to override the :meth:`~factory.Factory._generate` method on the factory.
Since version 2.9, the :meth:`~factory.django.mute_signals` decorator should be used:
from django.db.models.signals import post_save
@factory.django.mute_signals(post_save)
class ProfileFactory(factory.django.DjangoModelFactory):
class Meta:
model = my_models.Profile
title = 'Dr'
# We pass in profile=None to prevent UserFactory from creating another profile
# (this disables the RelatedFactory)
user = factory.SubFactory('app.factories.UserFactory', profile=None)
@factory.django.mute_signals(post_save)
class UserFactory(factory.django.DjangoModelFactory):
class Meta:
model = auth_models.User
username = factory.Sequence(lambda n: "user_%d" % n)
# We pass in 'user' to link the generated Profile to our just-generated User
# This will call ProfileFactory(user=our_new_user), thus skipping the SubFactory.
profile = factory.RelatedFactory(ProfileFactory, factory_related_name='user')
>>> u = UserFactory(profile__title="Lord")
>>> u.get_profile().title
"Lord"
Such behavior can be extended to other situations where a signal interferes with factory_boy related factories.
Any factories that call these classes with :class:`~factory.SubFactory` will also need to be decorated in the same manner.
Note
When any :class:`~factory.RelatedFactory` or :class:`~factory.post_generation`
attribute is defined on the :class:`~factory.django.DjangoModelFactory` subclass,
a second save()
is performed after the call to _create()
.
Code working with signals should thus use the :meth:`~factory.django.mute_signals` decorator
Building the adequate link between two models depends heavily on the use case; factory_boy doesn't provide a "all in one tools" as for :class:`~factory.SubFactory` or :class:`~factory.RelatedFactory`, users will have to craft their own depending on the model.
The base building block for this feature is the :class:`~factory.post_generation` hook:
# models.py
class Group(models.Model):
name = models.CharField()
class User(models.Model):
name = models.CharField()
groups = models.ManyToManyField(Group)
# factories.py
class GroupFactory(factory.django.DjangoModelFactory):
class Meta:
model = models.Group
name = factory.Sequence(lambda n: "Group #%s" % n)
class UserFactory(factory.django.DjangoModelFactory):
class Meta:
model = models.User
name = "John Doe"
@factory.post_generation
def groups(self, create, extracted, **kwargs):
if not create or not extracted:
# Simple build, or nothing to add, do nothing.
return
# Add the iterable of groups using bulk addition
self.groups.add(*extracted)
When calling UserFactory()
or UserFactory.build()
, no group binding
will be created.
But when UserFactory.create(groups=(group1, group2, group3))
is called,
the groups
declaration will add passed in groups to the set of groups for the
user.
For SQLAlchemy, change self.groups.add(group)
in the above example to
self.groups.append(group)
.
If only one link is required, this can be simply performed with a :class:`RelatedFactory`. If more links are needed, simply add more :class:`RelatedFactory` declarations:
# models.py
class User(models.Model):
name = models.CharField()
class Group(models.Model):
name = models.CharField()
members = models.ManyToManyField(User, through='GroupLevel')
class GroupLevel(models.Model):
user = models.ForeignKey(User)
group = models.ForeignKey(Group)
rank = models.IntegerField()
# factories.py
class UserFactory(factory.django.DjangoModelFactory):
class Meta:
model = models.User
name = "John Doe"
class GroupFactory(factory.django.DjangoModelFactory):
class Meta:
model = models.Group
name = "Admins"
class GroupLevelFactory(factory.django.DjangoModelFactory):
class Meta:
model = models.GroupLevel
user = factory.SubFactory(UserFactory)
group = factory.SubFactory(GroupFactory)
rank = 1
class UserWithGroupFactory(UserFactory):
membership = factory.RelatedFactory(
GroupLevelFactory,
factory_related_name='user',
)
class UserWith2GroupsFactory(UserFactory):
membership1 = factory.RelatedFactory(
GroupLevelFactory,
factory_related_name='user',
group__name='Group1',
)
membership2 = factory.RelatedFactory(
GroupLevelFactory,
factory_related_name='user',
group__name='Group2',
)
Whenever the UserWithGroupFactory
is called, it will, as a post-generation hook,
call the GroupLevelFactory
, passing the generated user as a user
field:
UserWithGroupFactory()
generates aUser
instance,obj
- It calls
GroupLevelFactory(user=obj)
- It returns
obj
When using the UserWith2GroupsFactory
, that behavior becomes:
UserWith2GroupsFactory()
generates aUser
instance,obj
- It calls
GroupLevelFactory(user=obj, group__name='Group1')
- It calls
GroupLevelFactory(user=obj, group__name='Group2')
- It returns
obj
When a field of a related class should match one of the container:
# models.py
class Country(models.Model):
name = models.CharField()
lang = models.CharField()
class User(models.Model):
name = models.CharField()
lang = models.CharField()
country = models.ForeignKey(Country)
class Company(models.Model):
name = models.CharField()
owner = models.ForeignKey(User)
country = models.ForeignKey(Country)
Here, we want:
- The
User
to have thelang
of its country (factory.SelfAttribute('country.lang')
) - The
Company
owner to live in the country of the company (factory.SelfAttribute('..country')
)
# factories.py
class CountryFactory(factory.django.DjangoModelFactory):
class Meta:
model = models.Country
name = factory.Iterator(["France", "Italy", "Spain"])
lang = factory.Iterator(['fr', 'it', 'es'])
class UserFactory(factory.django.DjangoModelFactory):
class Meta:
model = models.User
name = "John"
lang = factory.SelfAttribute('country.lang')
country = factory.SubFactory(CountryFactory)
class CompanyFactory(factory.django.DjangoModelFactory):
class Meta:
model = models.Company
name = "ACME, Inc."
country = factory.SubFactory(CountryFactory)
owner = factory.SubFactory(UserFactory, country=factory.SelfAttribute('..country'))
If the value of a field on the child factory is indirectly derived from a field on the parent factory, you will need to use LazyAttribute and poke the "factory_parent" attribute.
This time, we want the company owner to live in a country neighboring the country of the company:
class CompanyFactory(factory.django.DjangoModelFactory):
class Meta:
model = models.Company
name = "ACME, Inc."
country = factory.SubFactory(CountryFactory)
owner = factory.SubFactory(UserFactory,
country=factory.LazyAttribute(lambda o: get_random_neighbor(o.factory_parent.country)))
Sometimes you need a factory to call a specific manager method other than the default :meth:`Model.objects.create() <django.db.models.query.QuerySet.create>` method:
class UserFactory(factory.django.DjangoModelFactory):
class Meta:
model = UserenaSignup
username = "l7d8s"
email = "my_name@example.com"
password = "my_password"
@classmethod
def _create(cls, model_class, *args, **kwargs):
"""Override the default ``_create`` with our custom call."""
manager = cls._get_manager(model_class)
# The default would use ``manager.create(*args, **kwargs)``
return manager.create_user(*args, **kwargs)
A common pattern with factory_boy is to use a :class:`factory.Sequence` declaration to provide varying values to attributes declared as unique.
However, it is sometimes useful to force a given value to the counter, for instance to ensure that tests are properly reproducible.
factory_boy provides a few hooks for this:
- Forcing the value on a per-call basis
In order to force the counter for a specific :class:`~factory.Factory` instantiation, just pass the value in the
__sequence=42
parameter:class AccountFactory(factory.Factory): class Meta: model = Account uid = factory.Sequence(lambda n: n) name = "Test"
>>> obj1 = AccountFactory(name="John Doe", __sequence=10) >>> obj1.uid # Taken from the __sequence counter 10 >>> obj2 = AccountFactory(name="Jane Doe") >>> obj2.uid # The base sequence counter hasn't changed 1
- Resetting the counter globally
If all calls for a factory must start from a deterministic number, use :meth:`factory.Factory.reset_sequence`; this will reset the counter to its initial value (as defined by :meth:`factory.Factory._setup_next_sequence`).
>>> AccountFactory().uid 1 >>> AccountFactory().uid 2 >>> AccountFactory.reset_sequence() >>> AccountFactory().uid # Reset to the initial value 1 >>> AccountFactory().uid 2
It is also possible to reset the counter to a specific value:
>>> AccountFactory.reset_sequence(10) >>> AccountFactory().uid 10 >>> AccountFactory().uid 11
This recipe is most useful in a :class:`~unittest.TestCase`'s :meth:`~unittest.TestCase.setUp` method.
- Forcing the initial value for all projects
The sequence counter of a :class:`~factory.Factory` can also be set automatically upon the first call through the :meth:`~factory.Factory._setup_next_sequence` method; this helps when the objects' attributes mustn't conflict with preexisting data.
A typical example is to ensure that running a Python script twice will create non-conflicting objects, by setting up the counter to "max used value plus one":
class AccountFactory(factory.django.DjangoModelFactory): class Meta: model = models.Account @classmethod def _setup_next_sequence(cls): try: return models.Accounts.objects.latest('uid').uid + 1 except models.Account.DoesNotExist: return 1
>>> Account.objects.create(uid=42, name="Blah") >>> AccountFactory.create() # Sets up the account number based on the latest uid <Account uid=43, name=Test>
Although using random values is great, it can provoke test flakiness. factory_boy provides a few helpers for this.
Note
Those methods will seed the random engine used in both :class:`factory.Faker` and :mod:`factory.fuzzy` objects.
- Seeding the random engine
The simplest way to manage randomness is to push a selected seed when starting tests:
import factory.random # Pass in any value factory.random.reseed_random('my awesome project')
- Reproducing unseeded tests
A project might choose not to use an explicit random seed (for better fuzzing), but still wishes to have reproducible tests.
For such cases, use a combination of :meth:`factory.random.get_random_state()` and :meth:`factory.random.set_random_state()`.
Since the random state structure is implementation-specific, we recommend passing it around as a base64-encoded pickle dump.
class MyTestRunner: def setup_test_environment(self): state = os.environ.get('TEST_RANDOM_STATE') if state: try: decoded_state = pickle.loads(base64.b64decode(state.encode('ascii'))) except ValueError: decoded_state = None if decoded_state: factory.random.set_random_state(decoded_state) else: encoded_state = base64.b64encode(pickle.dumps(factory.random.get_random_state())) print("Current random state: %s" % encoded_state.decode('ascii')) super().setup_test_environment()
In order to inject some data to, say, a REST API, it can be useful to fetch the factory's data as a dict.
Internally, a factory will:
- Merge declarations and overrides from all sources (class definition, call parameters, ...)
- Resolve them into a dict
- Pass that dict as keyword arguments to the model's
build
/create
function
In order to get a dict, we'll just have to swap the model; the easiest way is to use :meth:`factory.build`:
class UserFactory(factory.django.DjangoModelFactory):
class Meta:
model = models.User
first_name = factory.Sequence(lambda n: "Agent %03d" % n) # Agent 000, Agent 001, Agent 002
username = factory.Faker('user_name')
>>> factory.build(dict, FACTORY_CLASS=UserFactory)
{'first_name': "Agent 000", 'username': 'john_doe'}
When defining a :class:`~factory.fuzzy.FuzzyChoice` you can reuse the same choice list from the model field descriptor.
Use the getter
kwarg to select the first element from each choice tuple.
class UserFactory(factory.Factory):
class Meta:
model = User
# CATEGORY_CHOICES is a list of (key, title) tuples
category = factory.fuzzy.FuzzyChoice(User.CATEGORY_CHOICES, getter=lambda c: c[0])
For model which uses GenericForeignKey
.. literalinclude:: ../examples/django_demo/generic_foreignkey/models.py
We can create factories like this:
.. literalinclude:: ../examples/django_demo/generic_foreignkey/factories.py