Aula 04 · 55–70 minutos
Teste a regra com um fake
Quarta aula: teste uma regra Go com um fake.
Resultado e mapa da aula
Ao concluir, você saberá escrever um teste unitário que produz evidência direta da regra, distinguir esse teste de um teste de integração e escolher um fake sem modelar acidentalmente a API do banco.
- Delimite o comportamento sob prova.
- Monte fake, serviço e entrada controlada.
- Observe o efeito ou um erro controlado.
- Separe as verificações de infraestrutura.
O teste acompanha o fluxo da regra
O teste cria o fake, injeta-o no serviço, executa um caso e observa o estado que o fake registrou. O fake não reproduz PostgreSQL: ele implementa somente o contrato de que o teste precisa.
teste → fakeOrderSaver → checkout.Service → Save — observa estado → regra → contratoDepender do banco no teste introduz causas irrelevantes para falhar: rede, migração e dados compartilhados. O fake mantém a evidência perto da decisão que você quer verificar.
Quando executa
- Antes do caso: o teste constrói o fake vazio.
- Durante o caso:
ServicechamaSavepelo contrato. - Depois: o teste verifica se o pedido esperado foi registrado.
É um teste unitário quando o comportamento sob prova está na regra e as bordas foram substituídas por valores controlados.
Fake pequeno, evidência direta
type fakeOrderSaver struct { saved checkout.Order }
func (f *fakeOrderSaver) Save(_ context.Context, order checkout.Order) error { f.saved = order; return nil }
func TestServiceSavesOrder(t *testing.T) {
fake := &fakeOrderSaver{}
service := checkout.NewService(fake)
want := checkout.Order{ID: "o-1"}
_ = service.Place(context.Background(), want)
if fake.saved != want { t.Fatal("pedido não foi salvo") }
}
Se o contrato cresce para acomodar detalhes do fake, pare: provavelmente ele deixou de representar a necessidade do consumidor.
Recupere da memória
Qual adaptação deixa o teste unitário independente do banco?
Fonte primária recomendada
Package testing. Use a documentação oficial para explorar subtestes, helpers e mensagens de falha úteis.
Próxima: refatorar uma dependência concreta.
Evidência do comportamento, não da infraestrutura
Problema em trabalho real: Usar um PostgreSQL local para provar que uma regra pediu Save mistura a decisão de negócio com rede, schema, dados compartilhados e disponibilidade do processo externo. Uma falha se torna menos diagnóstica e o ciclo fica lento.
Fluxo e momento de execução: O teste monta um fake vazio, injeta-o no serviço, executa Place e inspeciona o estado que o fake registrou. Para o caminho de erro, o fake devolve um erro controlado e o teste observa a reação pública da regra. SQL não participa desse ciclo.
Abordagem insuficiente: Não trate um banco real como mock da regra. Ele é apropriado em teste de integração do adaptador, onde a pergunta é se a consulta, os mapeamentos e a configuração funcionam juntos.
Decisão recomendada: Faça o fake implementar somente OrderSaver e guardar os valores recebidos. Prefira afirmar o efeito observável — pedido salvo ou erro propagado/traduzido — a afirmar detalhes internos da implementação.
Como verificar na prática: Execute go test ./... repetidamente: o resultado deve ser determinístico e não exigir serviço externo. Mantenha uma suíte de integração distinta, com ambiente descartável, para validar PostgreSQL; não substitua uma pela outra.
Limite de segurança: Fakes não devem conter credenciais, dados pessoais reais ou URLs privadas. Use valores sintéticos e confirme que mensagens de erro expostas ao cliente não revelem SQL, host ou detalhes de autenticação.
Fonte primária: How to Write Go Code — Testing.
Guia de solução do exercício
Fluxo de execução: O teste cria o fake, injeta-o, executa a regra e observa o estado gravado. Banco e rede não participam dessa evidência.
Abordagem inadequada: Usar banco local para comprovar uma chamada de Save transforma uma decisão unitária em integração.
db := openTestPostgres()
service := checkout.NewService(postgres.NewOrderRepository(db))
Por que falha e como corrigir: Falhas de conexão, schema e dados compartilhados mascaram a causa real. O ciclo fica lento e a asserção deixa de mostrar diretamente o efeito da regra.
Abordagem correta: Implemente um fake mínimo que registra o pedido recebido.
type fakeSaver struct { saved Order }
func (f *fakeSaver) Save(o Order) error { f.saved = o; return nil }
// depois: compare f.saved com o pedido esperado
Raciocínio, trade-offs e limites: Use fake quando o teste precisa observar estado ou comportamento simples. Para erro de persistência, configure o fake para devolver um erro controlado e confirme a reação da regra. Ainda mantenha testes de integração separados para validar SQL e o adaptador real.
Abra o diagrama relacionado e compare o fluxo.
Pergunta de mercado e entrevista
1. Quando usar fake em vez de banco no teste?
Como responder: Use-o ao provar a decisão da regra de forma rápida e determinística. Diferencie de teste de integração, ainda necessário para SQL, driver e configuração real.
2. Fake, stub e mock são iguais?
Como responder: Não. O fake tem implementação funcional simples; um stub devolve respostas pré-definidas; um mock normalmente verifica interações. Escolha o menor double que produz a evidência necessária.
3. O que um teste unitário deve afirmar aqui?
Como responder: O efeito da regra: por exemplo, o pedido entregue ao OrderSaver ou a reação a um erro controlado. Não afirme detalhes do SQL, pois eles pertencem ao teste do adaptador.
Pratique mais 9 questões de recuperação
Responda sem consultar a aula. O feedback é imediato; volte ao trecho relevante antes de tentar de novo.
DI entrega uma dependência; DIP define:
Em Go, uma interface é satisfeita quando:
Quem deve possuir uma interface pequena?
Onde a implementação concreta é escolhida?
Para testar uma regra sem banco, injete:
Uma interface deve conter:
O detalhe PostgreSQL deve depender de:
Qual sinal pede uma interface?
Após a refatoração, a regra não deve importar: