Common recipes

Note

Most recipes below take on Django model examples, but can also be used on their own.

Dependent objects (ForeignKey)

When one attribute is actually a complex field (e.g a ForeignKey to another Model), use the 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)

Choosing from a populated table

If the target of the ForeignKey should be chosen from a pre-populated table (e.g django.contrib.contenttypes.models.ContentType), simply use a 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 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.

Reverse dependencies (reverse ForeignKey)

When a related object should be created upon object creation (e.g a reverse ForeignKey from another Model), use a 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 UserFactory is instantiated, factory_boy will call UserLogFactory(user=that_user, action=...) just before returning the created User.

Example: Django’s Profile

Django (<1.5) provided a mechanism to attach a Profile to a User instance, using a 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 _generate() method on the factory.

Since version 2.9, the 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 SubFactory will also need to be decorated in the same manner.

Note

When any RelatedFactory or post_generation attribute is defined on the DjangoModelFactory subclass, a second save() is performed after the call to _create().

Code working with signals should thus use the mute_signals() decorator

Simple Many-to-many relationship

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 SubFactory or RelatedFactory, users will have to craft their own depending on the model.

The base building block for this feature is the 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).

Many-to-many relation with a ‘through’

If only one link is required, this can be simply performed with a RelatedFactory. If more links are needed, simply add more 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:

  1. UserWithGroupFactory() generates a User instance, obj

  2. It calls GroupLevelFactory(user=obj)

  3. It returns obj

When using the UserWith2GroupsFactory, that behavior becomes:

  1. UserWith2GroupsFactory() generates a User instance, obj

  2. It calls GroupLevelFactory(user=obj, group__name='Group1')

  3. It calls GroupLevelFactory(user=obj, group__name='Group2')

  4. It returns obj

Copying fields to a SubFactory

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 the lang 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)))

Custom manager methods

Sometimes you need a factory to call a specific manager method other than the default Model.objects.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)

Forcing the sequence counter

A common pattern with factory_boy is to use a 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 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 factory.Factory.reset_sequence(); this will reset the counter to its initial value (as defined by 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 TestCase’s setUp() method.

Forcing the initial value for all projects

The sequence counter of a Factory can also be set automatically upon the first call through the _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>

Using reproducible randomness

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 factory.Faker and 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 factory.random.get_random_state() and 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()

Converting a factory’s output to a dict

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:

  1. Merge declarations and overrides from all sources (class definition, call parameters, …)

  2. Resolve them into a dict

  3. 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 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'}

Fuzzying Django model field choices

When defining a 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])

Django models with GenericForeignKeys

For model which uses GenericForeignKey

from django.contrib.contenttypes.fields import GenericForeignKey
from django.contrib.contenttypes.models import ContentType
from django.db import models


class TaggedItem(models.Model):
    """Example GenericForeignKey model from django docs"""
    tag = models.SlugField()
    content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE)
    object_id = models.PositiveIntegerField()
    content_object = GenericForeignKey('content_type', 'object_id')

    def __str__(self):
        return self.tag

We can create factories like this:

from django.contrib.auth.models import Group, User
from django.contrib.contenttypes.models import ContentType

import factory.django

from .models import TaggedItem


class UserFactory(factory.django.DjangoModelFactory):
    first_name = 'Adam'

    class Meta:
        model = User


class GroupFactory(factory.django.DjangoModelFactory):
    name = 'group'

    class Meta:
        model = Group


class TaggedItemFactory(factory.django.DjangoModelFactory):
    object_id = factory.SelfAttribute('content_object.id')
    content_type = factory.LazyAttribute(
        lambda o: ContentType.objects.get_for_model(o.content_object))

    class Meta:
        exclude = ['content_object']
        abstract = True


class TaggedUserFactory(TaggedItemFactory):
    content_object = factory.SubFactory(UserFactory)

    class Meta:
        model = TaggedItem


class TaggedGroupFactory(TaggedItemFactory):
    content_object = factory.SubFactory(GroupFactory)

    class Meta:
        model = TaggedItem