Factory Boy centraliza a criação de objetos válidos para testes. Em vez de repetir construtores longos, cada teste sobrescreve apenas o campo relacionado ao comportamento que pretende verificar.

Criar uma fábrica determinística

from dataclasses import dataclass
import factory


@dataclass
class Usuario:
    nome: str
    email: str
    ativo: bool = True


class UsuarioFactory(factory.Factory):
    class Meta:
        model = Usuario

    nome = factory.Sequence(lambda n: f"Usuario {n}")
    email = factory.LazyAttribute(
        lambda obj: obj.nome.lower().replace(" ", ".") + "@example.com"
    )

No teste, UsuarioFactory(ativo=False) deixa evidente qual variação importa. Use SubFactory para relações e traits para estados recorrentes, mas não esconda uma árvore enorme de objetos atrás de uma chamada aparentemente simples.

Fábricas devem produzir o menor objeto válido. Se toda criação grava no banco, testes unitários ficam lentos e difíceis de isolar. Use a estratégia apropriada para construir ou persistir e limpe transações entre casos.

Evite aleatoriedade sem semente. Um teste que falha apenas com um nome gerado raramente ajuda a investigar a causa. Combine fábricas com fixtures do pytest para gerenciar dependências que têm ciclo de vida, reservando a fábrica para dados.

A documentação oficial do Factory Boy, consultada em 22 de julho de 2026, detalha declarações, associações, traits, estratégias e integrações com ORMs.

Instalar o pacote e escolher a base

Instale Factory Boy como dependência de desenvolvimento:

python -m pip install factory-boy

Use factory.Factory para classes comuns e dataclasses. Integrações como DjangoModelFactory e SQLAlchemyModelFactory conhecem convenções de persistência, mas também facilitam acessos involuntários ao banco. Escolha a base conforme a necessidade do teste, não apenas porque o projeto usa um ORM.

Uma fábrica útil oferece valores válidos e previsíveis. Ela não precisa representar todos os estados possíveis. O teste informa o valor que explica seu cenário:

def test_usuario_inativo_nao_autentica() -> None:
    usuario = UsuarioFactory(ativo=False)
    assert autenticar(usuario) is False

A sobrescrita comunica a intenção de imediato. Um construtor manual com dez campos sem relação com a regra esconderia a diferença importante.

Entender declarações e avaliação

Sequence cria valores únicos e determinísticos dentro do processo. LazyAttribute calcula um campo a partir de outros campos do objeto. LazyFunction chama uma função sem receber o objeto. Iterator percorre uma sequência controlada.

from datetime import UTC, datetime


class UsuarioFactory(factory.Factory):
    class Meta:
        model = Usuario

    nome = factory.Sequence(lambda n: f"usuario-{n}")
    email = factory.LazyAttribute(lambda obj: f"{obj.nome}@example.test")
    criado_em = factory.LazyFunction(lambda: datetime.now(UTC))
    papel = factory.Iterator(["leitor", "editor"])

Datas atuais ainda introduzem variação. Quando o comportamento depende do horário, sobrescreva com um valor fixo ou congele o relógio. Use domínios reservados como example.test para evitar que endereços gerados apontem para destinatários reais.

Modelar relações com SubFactory

SubFactory expressa uma relação obrigatória. RelatedFactory cria outro objeto depois do principal quando a direção inversa é mais útil.

@dataclass
class Pedido:
    cliente: Usuario
    total_centavos: int


class PedidoFactory(factory.Factory):
    class Meta:
        model = Pedido

    cliente = factory.SubFactory(UsuarioFactory)
    total_centavos = 2500

PedidoFactory(cliente__ativo=False) sobrescreve um campo da subfábrica. A sintaxe com dois sublinhados é poderosa, mas cadeias profundas são um alerta. Se criar um pedido também cria organização, permissões, assinaturas e mensagens, os testes ficam caros e opacos. Mantenha o grafo padrão pequeno e crie relações opcionais explicitamente.

Representar estados recorrentes com traits

Traits agrupam campos que, juntos, descrevem um estado:

class AssinaturaFactory(factory.Factory):
    class Meta:
        model = Assinatura

    ativa = True
    cancelada_em = None

    class Params:
        cancelada = factory.Trait(
            ativa=False,
            cancelada_em=datetime(2026, 1, 15, tzinfo=UTC),
        )

AssinaturaFactory(cancelada=True) é mais claro que repetir duas alterações coordenadas. Dê nomes do domínio aos traits e evite termos vagos como especial. Mantenha cada trait focado e valide estados impossíveis no modelo.

Parâmetros também podem controlar declarações sem virar campos do modelo. Funcionam bem para poucas variantes suportadas. Se dezenas de traits interagem, fábricas separadas ou funções construtoras podem comunicar melhor o domínio.

Escolher entre build e create

Factory Boy oferece estratégias de construção e criação. Em Factory, chamar a classe constrói o objeto. Nas integrações com ORM, create() normalmente persiste e build() mantém o objeto em memória.

Prefira objetos em memória em testes unitários que não exigem banco. Em testes de integração, persistir faz sentido, mas rollback e limpeza pertencem ao framework de testes. A fábrica não substitui isolamento.

build_batch(3) e create_batch(3) criam lotes. Peça apenas a quantidade necessária, pois lotes grandes podem esconder custo. Para testar dez mil linhas, um carregador de dados específico pode ser melhor que um enorme grafo de factories.

Usar hooks posteriores com cuidado

PostGeneration e post_generation tratam valores aplicados após a construção, como relações muitos para muitos. São úteis para exigências do ciclo de vida do framework, mas efeitos implícitos precisam permanecer visíveis.

class EquipeFactory(factory.Factory):
    class Meta:
        model = Equipe

    nome = factory.Sequence(lambda n: f"Equipe {n}")

    @factory.post_generation
    def membros(self, create, extracted, **kwargs):
        if extracted:
            for membro in extracted:
                self.adicionar_membro(membro)

O teste pode chamar EquipeFactory(membros=[ana, sam]). Documente se o hook exige persistência e o que ocorre com build(). Não faça requisições de rede, envio de email ou jobs em hooks. Substitua esses efeitos ou crie o objeto por uma interface de nível inferior.

Integrar fábricas e pytest

A factory cria dados; uma fixture controla ciclo de vida. Ela pode expor a fábrica ou retornar uma função com padrões específicos:

import pytest


@pytest.fixture
def usuario_factory():
    criados: list[Usuario] = []

    def criar_usuario(**alteracoes) -> Usuario:
        usuario = UsuarioFactory(**alteracoes)
        criados.append(usuario)
        return usuario

    yield criar_usuario
    criados.clear()

Para banco, confie nas fixtures de transação do framework. Não adicione exclusão manual que conflite com rollback. O guia de parametrização com pytest mostra como executar o mesmo comportamento com poucas variações relevantes.

Manter aleatoriedade reproduzível

Factory Boy integra Faker e declarações fuzzy. Variedade realista pode revelar suposições sobre Unicode, limites e campos opcionais, mas aleatoriedade sem controle gera falhas difíceis de repetir. Prefira padrões determinísticos.

Quando gerar dados aleatórios tiver objetivo, fixe e registre a semente. Reduza o valor que falhou antes de adicioná-lo como regressão permanente. Testes baseados em propriedades podem ser mais adequados quando o propósito é geração sistemática e redução automática.

Acompanhar a evolução dos modelos

Mantenha factories perto da suíte e revise-as quando campos obrigatórios ou validações mudarem. Um padrão que contorna uma regra nova permite estados que produção rejeitaria. Por outro lado, incluir todo campo opcional em todas as fábricas cria manutenção sem benefício.

Evite importar serviços da aplicação no módulo de factories. A fábrica descreve construção, não orquestra casos de uso. Se um agregado válido só nasce por um serviço de domínio, chame esse serviço em testes de integração e reserve factories para suas entradas.

Checklist de revisão

Confirme que a fábrica produz o menor objeto válido, os padrões são determinísticos, endereços usam domínios seguros e uma chamada não cria um grafo surpreendente. Verifique se a estratégia grava no banco, quem limpa as linhas e se hooks disparam efeitos externos.

Factories melhoram testes quando tornam a diferença relevante óbvia. Se PedidoFactory(total_centavos=0) revela imediatamente o limite testado, a abstração ajuda. Se é preciso rastrear vários traits e hooks, simplifique.

Quando não usar uma factory

Para um objeto com dois campos, um construtor direto pode ser mais claro. Dados tabulares estáticos também podem caber melhor em pytest.mark.parametrize. Use Factory Boy quando padrões válidos, relações ou integrações com ORM realmente eliminarem repetição relevante.

Não compartilhe instâncias criadas entre testes para economizar tempo. O ganho aparente cobra isolamento e dificulta execução paralela. Se a criação persistida ficou cara, investigue consultas, reduza o grafo padrão e use fixtures com escopo maior apenas para dados comprovadamente imutáveis.

Uma boa factory é infraestrutura de teste pequena e previsível. Ela deve facilitar a leitura do cenário, não imitar toda a aplicação.