O fluxo de sempre.
Abre o editor, escreve o código, executa. Se sobrar tempo, escreve algum teste.
A prática tradicional
Código primeiro. Teste depois, no fim do dia.
A ordem clássica
Escreve a função
Executa o código
Teste, se sobrar tempo
A ordem se inverte.
No TDD, pensa o comportamento, escreve o teste que falha, depois o código mínimo.
A inversão TDD
O teste vem antes do código, não depois.
A troca que reorganiza tudo
Comportamento primeiro
Teste que falha
Código que passa
Arquitetura sustentada
Red, Green, Refactor.
O teste vem antes do código. O ciclo da IA mantém o ritmo.
Três fases, um ciclo
A IA executa, o humano governa.
A nova divisão de trabalho
Humano define intenção
A spec é a fonte do comportamento
Agente executa o ciclo
Escreve teste, codifica, refatora
Teste arbitra
Passou está aceito, falhou volta
Exemplo de quinze segundos: a função soma.
Primeiro o teste falha (Red). Depois o código mínimo passa (Green).
# o teste vem antes do código from calculadora import soma def test_soma(): assert soma(2, 3) == 5
# implementação mínima para passar def soma(a, b): return a + b
O ciclo se fecha em três passos
1. pytest falha
calculadora.py ainda não existe
2. pytest passa
implementação mínima vira a luz verde
3. refatora
limpar com a rede de segurança ativa
A chave vem antes da fechadura.
A spec é a promessa. O teste é o auditor que destranca o código.
Spec, teste e código se encaixam
A chave girou? O código está correto.
A regra que protege o contrato
Spec descreve
Linguagem que humanos leem
Teste verifica
Asserção que a máquina executa
Não altere o teste
Mexer no contrato é falsificar a auditoria
Passo 1: a spec em texto.
Uma frase de regra vira o contrato que vai guiar o teste e o código.
# Regra de desconto
Compras com valor maior ou igual a 100 reais
recebem 10% de desconto sobre o valor total.
Compras abaixo desse limite não recebem desconto.
A spec define a intenção antes de qualquer linha de código
Passo 2: o teste que falha.
A spec vira asserts. O teste falha porque o código ainda não existe.
from compras import calcular_valor_final def test_acima_de_100(): assert calcular_valor_final(100) == 90 assert calcular_valor_final(200) == 180 def test_abaixo_de_100(): assert calcular_valor_final(99) == 99 assert calcular_valor_final(50) == 50
O teste é o contrato verificável da spec
Passo 3: o código que passa.
O agente implementa o mínimo necessário para virar os testes em verde.
# o agente implementa respeitando o teste def calcular_valor_final(valor): if valor >= 100: return valor * 0.9 return valor
O contrato fechou: spec, teste e código apertam as mãos
Cinco camadas. Uma pirâmide.
Muitos testes baratos na base, poucos testes caros no topo.
Cada camada responde uma pergunta
Custo cresce, quantidade encolhe.
Stack mínima em Python
pytest
framework principal
pytest-mock
substitui dependências
coverage.py
mede cobertura
hypothesis
property-based
Unitário: a função sob o microscópio.
Uma função pura, dezenas de entradas, uma certeza por linha.
A menor unidade do código
Função isolada, sem rede, sem disco, sem surpresa.
A unidade mínima que o teste protege
Função pura
sem efeito colateral
Mocks de externos
DB, rede, tempo
Milissegundos
milhares por segundo
pytest, unittest
o ferramental clássico
Como você escreve um teste unitário.
Arrange, Act, Assert. Uma função, uma asserção por caso.
import pytest from precificacao import calcular_desconto # Arrange + Act + Assert num único bloco parametrizado @pytest.mark.parametrize("valor, esperado", [ (100, 90), # exatamente no limite (200, 180), # acima do limite (99.99, 99.99), # um centavo abaixo (0, 0), # caso degenerado ]) def test_calcular_desconto(valor, esperado): assert calcular_desconto(valor) == esperado
Artefatos e código envolvidos
A função sob teste
precificacao.calcular_desconto, sem I/O
O arquivo de teste
tests/unit/test_calcular_desconto.py
O comando
pytest tests/unit -q
Uma skill faz o teste unitário pra você.
Você não copia prompt. Você invoca uma skill que já encapsula o protocolo inteiro.
O que a skill encapsula
Prompt + protocolo
vivem dentro da skill, não no chat
Artefato gerado
tests/unit/test_*.py
Humano decide
revisar e aprovar o merge
Integração: módulos conversando.
Dois módulos. Um contrato. Uma conversa que precisa terminar.
Onde dois módulos se encontram
O teste fica no fio, não em cada ponta.
O teste que liga as peças
Múltiplos módulos
api, repo, fila
DB real ou fake
SQLite, testcontainers
Fixtures de setup
dado conhecido, estado limpo
Segundos
dezenas por suíte
Como você escreve um teste de integração.
Sobe um ambiente mínimo, executa o caso, verifica o estado final.
import pytest from app.repo import PedidoRepo from app.servicos import criar_pedido @pytest.fixture def db(tmp_path): # SQLite isolado, descartado ao fim do teste return PedidoRepo(tmp_path / "test.db") def test_pedido_persiste_no_repo(db): # Arrange: estado limpo, dado conhecido pedido = criar_pedido(db, cliente="ana", valor=120) # Act: a operação atravessa serviço e repo encontrado = db.buscar(pedido.id) # Assert: estado final é exatamente o esperado assert encontrado.cliente == "ana" assert encontrado.valor == 120
Artefatos e código envolvidos
Fixtures de ambiente
DB temporário, fila em memória
Pastas
tests/integration/
testcontainers
Postgres, Redis, RabbitMQ reais
Uma skill mapeia a integração e escreve o teste.
Você aponta o módulo. A skill descobre dependências e propõe as fixtures mínimas.
O que a skill encapsula
Mapa de dependências
descoberta automática do que sobe junto
Artefato gerado
tests/integration/test_*.py
Humano decide
o que é real, o que é fake
Contrato: o formato que não pode quebrar.
Schema de entrada, schema de saída. O resto é detalhe.
A casca da interface
O que entra, o que sai, em que forma.
A casca da interface
OpenAPI
a casca da API HTTP
JSON Schema
valida payloads e respostas
Pact
consumidor versus provedor
Snapshot
forma estável da saída
Como você escreve um teste de contrato.
Define o schema, gera o payload, valida nas duas pontas.
from pydantic import BaseModel, ValidationError from app.api import criar_pedido_endpoint class PedidoOut(BaseModel): id: int cliente: str valor: float status: str def test_resposta_segue_schema(): # O endpoint pode mudar internamente, # mas a forma da resposta é um contrato. resp = criar_pedido_endpoint({"cliente": "ana", "valor": 120}) # Se a casca quebrar, este teste explode. PedidoOut.model_validate(resp)
Artefatos e código envolvidos
O schema versionado
contracts/PedidoOut.json
Pastas
tests/contract/
Validadores
pydantic, jsonschema, pact
Uma skill lê o schema e mantém o contrato.
Schema novo, teste novo. Schema mudou, alerta vermelho.
O que a skill encapsula
Parser + gerador
schema vira casos válidos e contraexemplos
Artefato gerado
tests/contract/test_*.py
Humano decide
evolução e versão do contrato
End-to-end: o usuário do começo ao fim.
Login, carrinho, pagamento, sucesso. Caminho real, dados reais.
O sistema todo, sem atalho
O caminho que o cliente percorre no produto.
A jornada inteira sob teste
Browser real
Chromium, Firefox, Webkit
Dados de teste
usuário fixture, cartão fake
Golden paths
os caminhos que pagam o aluguel
Minutos
poucos, mas caros
Como você escreve um teste end-to-end.
Um roteiro de cliques que termina em um estado observável.
import { test, expect } from "@playwright/test"; test("checkout feliz termina em pedido confirmado", async ({ page }) => { await page.goto("/login"); await page.fill("#email", "ana@exemplo.com"); await page.fill("#senha", "senha-teste"); await page.click("text=Entrar"); await page.click("text=Adicionar ao carrinho"); await page.click("text=Finalizar compra"); await page.click("text=Pagar"); await expect(page.locator("text=Pedido confirmado")).toBeVisible(); });
Artefatos e código envolvidos
Cenários reais
login, compra, cancelamento
Pastas
tests/e2e/
Stack
Playwright, Cypress, Selenium
Uma skill converte user story em jornada navegável.
Você descreve o caminho do cliente. A skill devolve o teste navegando o app.
O que a skill encapsula
Playwright + seletores
resistentes a refactor de UI
Artefato gerado
tests/e2e/*.spec.ts
Humano decide
quais paths bloqueiam o deploy
Regressão: o bug que não pode voltar.
Cada bug encontrado vira um teste. O cinturão cresce a cada commit.
A memória do que já doeu
Bug que entrou no teste não sai mais.
A memória do que já quebrou
Suíte fixa
cresce, nunca encolhe
Roda no CI
a cada PR, sem exceção
Snapshot
o comportamento de hoje
Histórico
um teste por bug capturado
Como você monta o cinturão de regressão.
Bug encontrado vira teste com o número da issue. Nunca sai mais.
import pytest from precificacao import calcular_total # ISSUE #482: total negativo aparecia quando # cupom de desconto era maior que a soma. def test_total_nao_pode_ser_negativo_issue_482(): itens = [{"valor": 10}, {"valor": 15}] cupom = 50 # maior que a soma dos itens assert calcular_total(itens, cupom=cupom) == 0
Artefatos e código envolvidos
Teste por bug
nome carrega o número da issue
Pastas
tests/regression/
CI bloqueia
PR não sobe com regressão vermelha
Uma skill prende o bug no cinturão.
A skill recebe o número da issue, reproduz o bug e adiciona ao cinturão de regressão.
O que a skill encapsula
Reprodutor + cinturão
caso mínimo + integração com CI
Artefato gerado
tests/regression/test_ISSUE_*.py
Humano decide
quando o bug está realmente curado
A pirâmide vira estrutura de pastas.
Olhar para tests/ já revela como o projeto pensa sobre validação.
# roda toda a pirâmide em ordem de custo $ pytest tests/unit/ $ pytest tests/integration/ $ pytest tests/contract/ $ pytest tests/e2e/ $ pytest tests/regression/ # mede cobertura $ pytest --cov=src tests/unit/ # property-based testing $ pytest tests/unit/test_invariantes.py # todo bug corrigido vira teste # na pasta de regressão, sem exceção
A regra de ouro dos testes
CI roda em ordem
Falha cedo, economiza minutos
Bug vira teste
A regressão guarda a memória
Lê em 30 segundos
Programador novo entende sozinho
O humano no comando.
A IA digita o código. Cinco responsabilidades continuam humanas.
A bússola do projeto está nas mãos do humano
Cinco pontos cardeais que o agente não absorve.
A síntese do capítulo
SDD escreve a partitura
A spec descreve a intenção
TDD afina a orquestra
O teste arbitra o resultado
Humano rege
A IA toca os instrumentos