factory-boy
factory-boy is a versatile test fixtures replacement for Python, based on thoughtbot's factory_bot for Ruby. It helps generate fake data efficiently and declaratively for various ORMs like Django, SQLAlchemy, and MongoEngine, supporting multiple build strategies and factory inheritance.
Warnings
- breaking Major breaking changes occurred in the transition from factory-boy 2.x to 3.x, including dropping support for Python 2, Python 3.4, Django 2.0, and Django 2.1.
- breaking For plain `factory.Factory` subclasses, `create()` no longer calls `objects.create()` for ORM models. For Django or SQLAlchemy models, you must now use `DjangoModelFactory` or `SQLAlchemyModelFactory` respectively to ensure persistence to the database when calling `create()`.
- deprecated Old class-level attributes like `FACTORY_FOR`, `ABSTRACT_FACTORY`, `FACTORY_STRATEGY` are deprecated. They should now be defined within a nested `Meta` class using `model`, `abstract`, and `strategy` respectively.
- deprecated The default behavior of `_after_postgeneration` for `DjangoModelFactory` changed. It will no longer automatically save the instance after post-generation hooks in future releases. This often results in `DeprecationWarning` if not handled.
- gotcha Defining mutable fields (e.g., lists, dictionaries) directly at the factory class level can lead to all generated instances sharing the same mutable object. Changes in one instance will propagate to others.
- gotcha Overriding attributes directly at the class level (e.g., `MyFactory.name = 'Fixed'`) is an anti-pattern and can lead to unpredictable behavior, especially in parallel test runs, as it modifies the default for all future objects.
Install
-
pip install factory-boy
Imports
- Factory
import factory class MyFactory(factory.Factory): ...
- Faker
import factory class MyFactory(factory.Factory): name = factory.Faker('name') - Sequence
import factory class MyFactory(factory.Factory): id = factory.Sequence(lambda n: n) - LazyAttribute
import factory class MyFactory(factory.Factory): full_name = factory.LazyAttribute(lambda o: f'{o.first_name} {o.last_name}') - SubFactory
import factory class UserFactory(factory.Factory): ... class ProfileFactory(factory.Factory): user = factory.SubFactory(UserFactory) - DjangoModelFactory
from factory.django import DjangoModelFactory class UserFactory(DjangoModelFactory): class Meta: model = User - SQLAlchemyModelFactory
from factory.alchemy import SQLAlchemyModelFactory class UserFactory(SQLAlchemyModelFactory): class Meta: model = User sqlalchemy_session = db_session
Quickstart
import factory
class User:
def __init__(self, first_name, last_name, email, is_admin=False):
self.first_name = first_name
self.last_name = last_name
self.email = email
self.is_admin = is_admin
def __str__(self):
return f'{self.first_name} {self.last_name} ({self.email})'
class UserFactory(factory.Factory):
class Meta:
model = User
first_name = factory.Faker('first_name')
last_name = factory.Faker('last_name')
email = factory.LazyAttribute(lambda o: f'{o.first_name}.{o.last_name}@example.com'.lower())
is_admin = False
# Create a basic user
user = UserFactory()
print(f"Created user: {user}")
# Create an admin user
admin_user = UserFactory(is_admin=True)
print(f"Created admin user: {admin_user}")
# Create a user with specific name
specific_user = UserFactory(first_name='John', last_name='Doe')
print(f"Created specific user: {specific_user}")