factory-boy

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

factory_boy

factory_boy

Deep Knowledge: Use
mcp__documentation__fetch_docs
with technology:
factory-boy
for comprehensive documentation on all declarations, ORM integrations, and patterns.
深度知识:使用
mcp__documentation__fetch_docs
并指定技术:
factory-boy
,获取所有声明、ORM集成及模式的完整文档。

Installation

安装

bash
pip install factory_boy faker
bash
pip install factory_boy faker

Basic Factory

基础工厂

python
import factory
from myapp.models import User

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()
    )
    username   = factory.Sequence(lambda n: f"user{n}")
    is_active  = True

user  = UserFactory()            # creates (or builds, see Meta.strategy)
user  = UserFactory.build()      # in-memory, no DB
users = UserFactory.create_batch(5)
stub  = UserFactory.stub()       # StubObject, no model.__init__()
python
import factory
from myapp.models import User

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()
    )
    username   = factory.Sequence(lambda n: f"user{n}")
    is_active  = True

user  = UserFactory()            # 创建(或构建,参考Meta.strategy)
user  = UserFactory.build()      # 仅在内存中创建,不写入数据库
users = UserFactory.create_batch(5) # 批量创建5个实例
stub  = UserFactory.stub()       # 创建StubObject,不调用model.__init__()

DjangoModelFactory

DjangoModelFactory

python
from factory.django import DjangoModelFactory

class UserFactory(DjangoModelFactory):
    class Meta:
        model = "auth.User"                       # 'app.Model' string OK
        django_get_or_create = ("username",)       # get-or-create on username

    username = factory.Sequence(lambda n: f"user{n}")
    email    = factory.LazyAttribute(lambda o: f"{o.username}@example.com")
    password = factory.django.Password("testpass123")  # hashed via make_password

    class Params:
        staff = factory.Trait(is_staff=True)
        admin = factory.Trait(is_staff=True, is_superuser=True)
python
from factory.django import DjangoModelFactory

class UserFactory(DjangoModelFactory):
    class Meta:
        model = "auth.User"                       # 支持'app.Model'字符串格式
        django_get_or_create = ("username",)       # 根据username执行get-or-create操作

    username = factory.Sequence(lambda n: f"user{n}")
    email    = factory.LazyAttribute(lambda o: f"{o.username}@example.com")
    password = factory.django.Password("testpass123")  # 通过make_password进行哈希处理

    class Params:
        staff = factory.Trait(is_staff=True)
        admin = factory.Trait(is_staff=True, is_superuser=True)

SQLAlchemyModelFactory

SQLAlchemyModelFactory

python
from sqlalchemy.orm import scoped_session, sessionmaker
from factory.alchemy import SQLAlchemyModelFactory

Session = scoped_session(sessionmaker())

class UserFactory(SQLAlchemyModelFactory):
    class Meta:
        model = User
        sqlalchemy_session = Session
        sqlalchemy_session_persistence = "commit"  # None | "flush" | "commit"

    username = factory.Sequence(lambda n: f"user{n}")
    email    = factory.LazyAttribute(lambda o: f"{o.username}@example.com")
python
from sqlalchemy.orm import scoped_session, sessionmaker
from factory.alchemy import SQLAlchemyModelFactory

Session = scoped_session(sessionmaker())

class UserFactory(SQLAlchemyModelFactory):
    class Meta:
        model = User
        sqlalchemy_session = Session
        sqlalchemy_session_persistence = "commit"  # 可选值:None | "flush" | "commit"

    username = factory.Sequence(lambda n: f"user{n}")
    email    = factory.LazyAttribute(lambda o: f"{o.username}@example.com")

In pytest: inject session

在pytest中:注入session

@pytest.fixture def user(db_session): UserFactory._meta.sqlalchemy_session = db_session return UserFactory()
undefined
@pytest.fixture def user(db_session): UserFactory._meta.sqlalchemy_session = db_session return UserFactory()
undefined

All Key Declarations

所有核心字段声明

python
class ArticleFactory(factory.Factory):
    class Meta:
        model = Article

    # Static value
    status = "draft"

    # Faker provider
    title      = factory.Faker("sentence", nb_words=6)
    body       = factory.Faker("paragraph")
    uuid       = factory.Faker("uuid4")

    # Sequence (unique per factory call)
    slug       = factory.Sequence(lambda n: f"article-{n}")

    # LazyAttribute (computed from other fields)
    summary    = factory.LazyAttribute(lambda o: o.body[:100])

    # LazyFunction (zero-arg callable)
    created_at = factory.LazyFunction(datetime.utcnow)

    # SubFactory (FK)
    author     = factory.SubFactory(UserFactory)

    # SelfAttribute (access parent or sibling fields)
    author_email = factory.SelfAttribute("author.email")

    # Iterator (cycles through values)
    category   = factory.Iterator(["tech", "culture", "science"])

    # Dict field
    metadata   = factory.Dict({"source": "web", "version": factory.Sequence(lambda n: n)})

    # Maybe (conditional)
    published_at = factory.Maybe(
        "is_published",
        yes_declaration=factory.LazyFunction(datetime.utcnow),
        no_declaration=None,
    )
    is_published = True
python
class ArticleFactory(factory.Factory):
    class Meta:
        model = Article

    # 静态值
    status = "draft"

    # Faker提供器
    title      = factory.Faker("sentence", nb_words=6)
    body       = factory.Faker("paragraph")
    uuid       = factory.Faker("uuid4")

    # 序列(每次工厂调用生成唯一值)
    slug       = factory.Sequence(lambda n: f"article-{n}")

    # LazyAttribute(根据其他字段计算)
    summary    = factory.LazyAttribute(lambda o: o.body[:100])

    # LazyFunction(无参数可调用对象)
    created_at = factory.LazyFunction(datetime.utcnow)

    # SubFactory(外键关联)
    author     = factory.SubFactory(UserFactory)

    # SelfAttribute(访问父级或同级字段)
    author_email = factory.SelfAttribute("author.email")

    # Iterator(循环取值)
    category   = factory.Iterator(["tech", "culture", "science"])

    # Dict字段
    metadata   = factory.Dict({"source": "web", "version": factory.Sequence(lambda n: n)})

    # Maybe(条件声明)
    published_at = factory.Maybe(
        "is_published",
        yes_declaration=factory.LazyFunction(datetime.utcnow),
        no_declaration=None,
    )
    is_published = True

SubFactory with Overrides

带覆盖配置的SubFactory

python
undefined
python
undefined

Override sub-factory fields via __ separator

通过__分隔符覆盖子工厂字段

post = PostFactory(author__username="alice", author__email="alice@test.com")
post = PostFactory(author__username="alice", author__email="alice@test.com")

Override nested sub-factory

覆盖嵌套子工厂

company = CompanyFactory(owner__address__city="Rome")
undefined
company = CompanyFactory(owner__address__city="Rome")
undefined

RelatedFactory (Reverse FK)

RelatedFactory(反向外键)

python
class UserFactory(DjangoModelFactory):
    class Meta:
        model = User

    profile = factory.RelatedFactory(
        ProfileFactory,
        factory_related_name="user",  # Profile.user
        bio="Default bio",
    )
python
class UserFactory(DjangoModelFactory):
    class Meta:
        model = User

    profile = factory.RelatedFactory(
        ProfileFactory,
        factory_related_name="user",  # 对应Profile.user字段
        bio="默认简介",
    )

post_generation (M2M, Side Effects)

post_generation(多对多、副作用处理)

python
class ArticleFactory(DjangoModelFactory):
    class Meta:
        model = Article
        skip_postgeneration_save = True

    @factory.post_generation
    def tags(self, create, extracted, **kwargs):
        if not create or not extracted:
            return
        self.tags.set(extracted)
python
class ArticleFactory(DjangoModelFactory):
    class Meta:
        model = Article
        skip_postgeneration_save = True

    @factory.post_generation
    def tags(self, create, extracted, **kwargs):
        if not create or not extracted:
            return
        self.tags.set(extracted)

Usage

使用示例

article = ArticleFactory(tags=[tag1, tag2])
undefined
article = ArticleFactory(tags=[tag1, tag2])
undefined

Traits

Traits(特性)

python
class OrderFactory(factory.Factory):
    class Meta:
        model = Order

    status = "pending"

    class Params:
        shipped = factory.Trait(
            status="shipped",
            shipped_at=factory.LazyFunction(datetime.utcnow),
        )
        paid = factory.Trait(
            status="paid",
            paid_at=factory.LazyFunction(datetime.utcnow),
        )
python
class OrderFactory(factory.Factory):
    class Meta:
        model = Order

    status = "pending"

    class Params:
        shipped = factory.Trait(
            status="shipped",
            shipped_at=factory.LazyFunction(datetime.utcnow),
        )
        paid = factory.Trait(
            status="paid",
            paid_at=factory.LazyFunction(datetime.utcnow),
        )

Usage

使用示例

pending = OrderFactory() shipped = OrderFactory(shipped=True) paid = OrderFactory(paid=True)
undefined
pending = OrderFactory() shipped = OrderFactory(shipped=True) paid = OrderFactory(paid=True)
undefined

pytest Integration

pytest集成

python
undefined
python
undefined

conftest.py

conftest.py

import pytest from myapp.tests.factories import UserFactory, ArticleFactory
@pytest.fixture def user(db): # for Django return UserFactory()
@pytest.fixture def user(db_session): # for SQLAlchemy UserFactory._meta.sqlalchemy_session = db_session return UserFactory()
@pytest.fixture def articles(db): return ArticleFactory.create_batch(5)
@pytest.fixture(autouse=True) def reset_sequences(): UserFactory.reset_sequence(0) ArticleFactory.reset_sequence(0) yield
undefined
import pytest from myapp.tests.factories import UserFactory, ArticleFactory
@pytest.fixture def user(db): # Django环境下使用 return UserFactory()
@pytest.fixture def user(db_session): # SQLAlchemy环境下使用 UserFactory._meta.sqlalchemy_session = db_session return UserFactory()
@pytest.fixture def articles(db): return ArticleFactory.create_batch(5)
@pytest.fixture(autouse=True) def reset_sequences(): UserFactory.reset_sequence(0) ArticleFactory.reset_sequence(0) yield
undefined

Anti-Patterns

反模式

Anti-PatternSolution
UserFactory(email="test@example.com")
in every test
Override in fixture or use
Sequence
SubFactory without
django_get_or_create
Can cause unique constraint violations
Not resetting sequencesUse
autouse
fixture to reset_sequence(0)
Creating M2M before object existsUse
post_generation
/
skip_postgeneration_save
反模式解决方案
每个测试中都写
UserFactory(email="test@example.com")
在fixture中覆盖配置,或使用
Sequence
SubFactory未设置
django_get_or_create
可能导致唯一约束冲突
未重置序列使用
autouse
fixture调用reset_sequence(0)
对象创建前就处理多对多关系使用
post_generation
/
skip_postgeneration_save