MÓDULO 02 · 2 SEMANAS · TESTES & TDD

Testes, TDD & Qualidade

Como times maduros tratam testes — não como obrigação, mas como ferramenta de design e confiança.

Duração ~2 semanas
Conceitos 8 fundamentais
Projeto Suíte multi-camada
Pré-requisito Módulos 00 e 01
Qualidade Confiabilidade Operação

Oito ideias que separam quem escreve testes "porque tem que ter" de quem usa testes como ferramenta de pensamento e confiança em produção.

01

TDD em profundidade — red, green, refactor

O ciclo de Beck destrinchado. Disciplina específica de cada passo, padrões de aprendizado (Calculator kata, FizzBuzz, Bowling), e os erros típicos de quem está aprendendo.

estudar →
02

Pirâmide, Troféu & Honeycomb — estratégias de teste

Cohn, Kent C. Dodds, Spotify. Quando cada estratégia faz sentido, e como decidir o mix certo para o seu sistema.

estudar →
03

Test doubles — vocabulário preciso

Dummy, stub, spy, mock, fake. Quando usar cada um, por que mocks demais é antipattern, e a heurística de Fowler em "Mocks Aren't Stubs".

estudar →
04

BDD & Given/When/Then — testes como especificação

Por que Dan North inventou BDD, como Gherkin estrutura conversa entre devs e stakeholders, e quando BDD ajuda vs estorva.

estudar →
05

Property-based testing — quando exemplos não bastam

Hypothesis, FsCheck, gopter. Em vez de testar "soma(2,3) == 5", testar "soma é comutativa". Encontrando bugs que ninguém pensaria em escrever como exemplo.

estudar →
06

Mutation testing — verificando a qualidade dos testes

Cobertura mente. Mutation testing introduz bugs propositais e verifica se os testes os pegam. Stryker, mutmut, go-mutesting.

estudar →
07

Contract testing — quando integração não escala

Pact, OpenAPI validation. Como testar a fronteira entre serviços sem subir tudo em E2E. Consumer-driven contracts.

estudar →
08

Snapshot, fixtures & testes flaky

Snapshot tests, builders de dados de teste, e o problema dos testes flaky — como diagnosticar, isolar e exterminar.

estudar →
princípio orientador

Teste não é cerimônia que vem depois do código. É a ferramenta que revela onde o design está mal-posto, antes mesmo do código ir para produção. Quando esse hábito vira reflexo, qualidade deixa de ser inspeção e vira propriedade estrutural.

Decisões de qualidade que separam o teste como cerimônia do teste como ferramenta.

TDD sempre, ou só onde faz sentido?

TDD funciona melhor quando o problema é razoavelmente bem-definido e o ciclo é curto (lógica de domínio, parsers, transformações). Funciona mal em exploração (UI nova, integração com API desconhecida, bibliotecas de terceiros sem clareza). Em projetos exploratórios, escreva código spike primeiro, jogue fora, e implemente com TDD a versão final. "TDD obrigatório em tudo" é dogma; "TDD onde fortalece" é prática.

Cobertura como métrica — útil ou enganosa?

Cobertura mede se o código foi executado, não se foi verificado. Você pode ter 100% de cobertura sem nenhuma assertion útil. Use cobertura como sinal de alerta (caiu de 80% para 60%? algo mudou) e não como meta. Para verificar qualidade real dos testes, use mutation testing — ele mede se os testes pegam bugs introduzidos propositalmente.

Mocks ou fakes para dependências externas?

Fakes (implementações alternativas funcionais) quase sempre vencem. Um InMemoryUserRepository que implementa a mesma interface que PostgresUserRepository permite testar lógica de domínio rapidamente, com comportamento real. Mocks excessivos verificam como o código foi escrito (chamadas, ordem) em vez de o que ele faz. Mocks são apropriados para fronteiras de protocolo e efeitos colaterais sem outra alternativa.

E2E quanto e onde?

E2E é caro, lento e frágil — mas pega bugs que nenhum teste isolado pegaria. Use poucos, em caminhos críticos do usuário (login, checkout, ações que se erradas custam reputação ou receita). Não tente cobrir todas as variantes — isso é trabalho dos testes de integração e unidade. E2E foca em garantir que o sistema está vivo e funciona ponta a ponta.

Quando contract testing vs E2E entre serviços?

Em arquitetura de microsserviços, E2E entre todos os serviços vira inviável: combinatórias explodem, ambiente fica frágil, builds lentos. Contract testing (Pact, OpenAPI) verifica que cada serviço cumpre o contrato que outros consomem, sem precisar subir todos juntos. E2E vira último recurso, focado em fluxos críticos.

Construir uma suíte multi-camada — unidade, integração, contract, E2E — para um sistema real força você a sentir o trade-off de cada nível.

PROJETO PRÁTICO

Sistema de pedidos com suíte multi-camada

Um sistema simples (catálogo + pedido + pagamento + notificação) implementado via TDD desde o primeiro commit, com cobertura por unidade, integração (banco real em container), contract test (Pact entre serviço de pedido e pagamento), e E2E mínimo no caminho feliz. Property-based em parsers e mutation testing rodando em CI.

REQUISITOS
  • Domínio implementado 100% via TDD (commits mostram o ciclo red/green/refactor)
  • Testes unitários sub-segundo, sem dependências externas
  • Testes de integração com Postgres em container (Testcontainers)
  • Contract test entre 2 serviços via Pact
  • 1-2 testes E2E cobrindo fluxo crítico
  • Property-based em validações e cálculos numéricos
  • Mutation testing rodando em CI com threshold mínimo
  • Cobertura instrumentada mas não como meta — só como sinal
Qualidade Confiabilidade
STACK SUGERIDA POR LINGUAGEM
STACK
.NET 10 + xUnit + FluentAssertions + NSubstitute + Testcontainers + FsCheck + Stryker.NET + PactNet
ESTRUTURA / NOTAS
  • Orders.Domain/ (lógica pura, sem dependências)
  • Orders.Domain.Tests/ (xUnit + FluentAssertions, sub-segundo)
  • Orders.Infra/ (EF Core, repositório Postgres)
  • Orders.Infra.Tests/ (Testcontainers + Postgres real)
  • Orders.Contracts.Tests/ (PactNet)
  • Orders.E2E.Tests/ (WebApplicationFactory)
  • Stryker.NET no CI para mutation
STACK
Python 3.13 + pytest + Hypothesis + pytest-mock + Testcontainers + Pact-Python + mutmut
ESTRUTURA / NOTAS
  • orders/domain/ (lógica pura)
  • orders/infra/ (SQLAlchemy, repositórios)
  • tests/unit/ (pytest, sub-segundo)
  • tests/integration/ (Testcontainers)
  • tests/contract/ (Pact)
  • tests/e2e/ (httpx + servidor real)
  • tests/property/ (Hypothesis para invariantes)
  • mutmut em CI
STACK
stdlib testing + testify + gopter + Testcontainers-Go + Pact-Go + go-mutesting
ESTRUTURA / NOTAS
  • internal/domain/ (lógica pura)
  • internal/infra/ (postgres com sqlx ou pgx)
  • internal/domain/*_test.go (table-driven)
  • internal/infra/*_integration_test.go (build tag)
  • tests/contract/ (Pact-Go)
  • tests/e2e/ (httptest + servidor real)
  • internal/domain/*_property_test.go (gopter)
  • go-mutesting em CI
entregável

Repositório com README documentando cada camada de teste, qual problema ela pega, e quanto tempo leva. Mostre via screenshots de CI: unidade em segundos, integração em minutos, mutation em dezenas de minutos. O gradiente de feedback é o ponto.

Perguntas reais sobre filosofia e prática de testes. Diferenciam quem escreve testes de quem entende para que servem.

Q.01

Explique o ciclo TDD. Por que escrever o teste primeiro e não depois? Qual é a parte mais negligenciada?

Q.02

Qual a diferença entre mock, stub e fake? Quando você prefere cada um? Por que "mockar tudo" é antipattern?

Q.03

Cobertura de testes 95% — isso significa que os testes são bons? Como você verifica a qualidade dos testes?

Q.04

Você tem um teste flaky que falha 1 em cada 20 execuções. O que faz?

Q.05

Em microsserviços, como garantir que mudanças em um serviço não quebram outros sem subir tudo em E2E?