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.