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.

  1. Delimite o comportamento sob prova.
  2. Monte fake, serviço e entrada controlada.
  3. Observe o efeito ou um erro controlado.
  4. 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.

Fluxo de teste com fake
teste → fakeOrderSaver → checkout.Service → Save — observa estado → regra → contrato

Depender 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

  1. Antes do caso: o teste constrói o fake vazio.
  2. Durante o caso: Service chama Save pelo contrato.
  3. 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.

Use os controles do diagrama para comparar as composições e seguir as relações.

Abrir o ciclo do teste.

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: