Estratégia de TDD para a R2
Por que este documento existe
Este documento define como o Squad 07 aplicará Test-Driven Development (TDD) durante a Release 2 do CrivoAI. A R2 marca a transição da documentação e prototipagem (R1) para a implementação funcional ponta a ponta, incluindo análise real de texto legislativo, cálculo de score e integração com agente de IA.
Sem uma estratégia clara de TDD, cada desenvolvedor pode adotar interpretações diferentes de quando e como testar, gerando inconsistências, baixa cobertura e dificuldade para revisão de código.
Como se relaciona com o projeto
Este documento complementa o fluxo SDD/TDD descrito em docs/sdd/index.md. Enquanto aquele documento descreve o processo de especificação antes da implementação, este define especificamente como os testes devem ser escritos e revisados durante o desenvolvimento da R2.
A estratégia é derivada do estudo realizado na Sprint 10 (Issue #113) e foi formalizada para orientar o time na Issue #117.
O Ciclo Red-Green-Refactor
Todo desenvolvimento de nova funcionalidade na R2 deve seguir o ciclo TDD:
RED — Escrever o teste antes
O desenvolvedor escreve um teste que descreve o comportamento esperado de uma funcionalidade que ainda não existe. O teste deve falhar porque o código de produção ainda não foi implementado. Essa falha é esperada e correta: ela confirma que o teste está testando algo real.
Se o teste passar sem código implementado, o teste está errado ou testando a coisa errada.
GREEN — Implementar o mínimo para passar
O desenvolvedor escreve apenas o código necessário para o teste passar. A solução pode ser simples ou até ingênua nesse momento. O objetivo exclusivo é fazer o teste ficar verde. Soluções hardcoded são aceitáveis enquanto há mais testes a serem escritos.
REFACTOR — Limpar sem quebrar
Com os testes passando, o desenvolvedor refatora: melhora legibilidade, elimina duplicação, aplica padrões arquiteturais. Os testes garantem que nenhum comportamento foi alterado durante a limpeza. Se algum teste quebrar durante a refatoração, a mudança introduziu uma regressão.
Quando aplicar TDD obrigatoriamente
TDD é obrigatório nas seguintes situações na R2:
Novas features: qualquer funcionalidade nova, endpoint de API, componente com lógica de negócio ou integração com o agente de IA.
Correção de bugs: o fluxo correto é escrever um teste que reproduza o bug antes de corrigir. O teste falha com o bug presente e passa após a correção. Isso garante que o bug não retorne.
Modificação de código existente: quando a alteração muda o comportamento de uma função ou componente, o teste deve ser atualizado ou escrito antes da mudança.
TDD é recomendado (mas não obrigatório) para refatorações que não alteram comportamento externo, quando já existe cobertura de testes adequada.
Padrões de escrita de testes
Arrange-Act-Assert (AAA)
Estrutura padrão para testes unitários e de integração. Cada seção tem responsabilidade clara.
def test_calcular_score_retorna_valor_entre_zero_e_cem():
# Arrange — configurar o estado inicial e as dependências
texto = "Art. 1º. Esta lei dispõe sobre o Programa Nacional de Qualidade Legislativa."
# Act — executar a ação que está sendo testada
resultado = calcular_score(texto)
# Assert — verificar o resultado esperado
assert isinstance(resultado, float)
assert 0.0 <= resultado <= 100.0
it("exibe mensagem de erro quando submissão falha", async () => {
// Arrange
mockApi.postLei.mockRejectedValue(new Error("Erro de conexão"));
// Act
render(<FormularioSubmissao />);
await userEvent.click(screen.getByRole("button", { name: /enviar/i }));
// Assert
expect(await screen.findByText(/erro de conexão/i)).toBeInTheDocument();
});
Given-When-Then
Variante mais próxima da linguagem de negócio, útil para descrever cenários de integração ou E2E:
Dado: usuário autenticado com token válido
Quando: submete texto legislativo com título, número e conteúdo
Então: recebe confirmação de persistência com status 201
Estrutura de testes no CrivoAI
Backend (Python + pytest)
Os testes do backend ficam em backend/tests/. A estrutura espelha a de backend/app/:
backend/
├── app/
│ ├── api/
│ │ ├── auth.py
│ │ └── laws.py
│ └── services/
│ └── score_service.py
└── tests/
├── unit/
│ └── test_score_service.py
└── integration/
├── test_auth_routes.py
└── test_laws_routes.py
Execução:
cd backend
pytest
pytest --cov=app --cov-report=term-missing
Frontend (TypeScript + Jest + React Testing Library)
Os testes ficam próximos aos componentes ou em __tests__/:
frontend/src/
├── components/
│ ├── ScoreBadge/
│ │ ├── ScoreBadge.tsx
│ │ └── ScoreBadge.test.tsx
│ └── FormularioSubmissao/
│ ├── FormularioSubmissao.tsx
│ └── FormularioSubmissao.test.tsx
└── lib/
├── api.ts
└── api.test.ts
Execução:
cd frontend
npm run test
npm run test -- --coverage
Metas de cobertura para R2
| Escopo | Cobertura mínima |
|---|---|
| Código novo | 90% |
| Código modificado | 90% |
| Backend (geral) | 90% |
| Frontend (geral) | 90% |
A meta de 90% é definida pelo plano de ensino da disciplina e é obrigatória para todos os PRs da R2.
Cobertura é um indicador, não um objetivo final. Um teste que percorre uma linha de código sem verificar nada não conta como cobertura significativa. O reviewer deve avaliar se os assertions fazem sentido, não apenas se a porcentagem foi atingida.
Ferramentas de cobertura
Backend — pytest-cov:
pip install pytest-cov
pytest --cov=app --cov-report=term-missing --cov-fail-under=90
A flag --cov-fail-under=90 garante que o CI falhe automaticamente se a cobertura cair abaixo da meta. Para relatório HTML:
pytest --cov=app --cov-report=html
Frontend — Jest coverage:
npm run test -- --coverage
O threshold pode ser fixado no jest.config.js:
coverageThreshold: {
global: { lines: 90, functions: 90, branches: 90 },
}
SonarQube — Qualidade de código no CI/CD
Nota: SonarQube é uma ferramenta de CI/CD, não um framework de teste. Sua configuração pertence ao pipeline (
.github/workflows/). Em relação a cobertura, ele apenas lê e exibe os relatórios gerados pelo pytest-cov e Jest — não executa testes.
O que o SonarQube analisa:
- Code smells: código funcional mas difícil de manter (métodos longos, complexidade alta)
- Bugs: padrões com alta probabilidade de falha em produção
- Vulnerabilidades: segredos expostos, injeção SQL, imports inseguros
- Duplicações: blocos repetidos que devem ser extraídos
- Cobertura: lida a partir do
coverage.xml(pytest-cov) oulcov.info(Jest)
Fluxo de integração:
pytest --cov=app --cov-report=xml → coverage.xml
Jest --coverage → lcov.info
↓
CI envia para SonarQube
↓
SonarQube exibe painel + Quality Gate
O Quality Gate do SonarQube pode bloquear merges se a cobertura cair abaixo da meta ou se forem introduzidos bugs e vulnerabilidades críticas. A configuração de Quality Gate e a integração com GitHub Actions deve ser documentada e implementada dentro do escopo de CI/CD do projeto, não aqui.
Pirâmide de testes do CrivoAI
/\
/E2E\ Playwright — fluxos completos (R2)
/------\
/ Integra-\ pytest + TestClient / MSW — rotas, banco
/ ção \
/--------------\
/ Unitários \ pytest / Jest — funções, componentes
/------------------\
A proporção esperada: ~70% unitários, ~20% integração, ~10% E2E.
Checklist de TDD para Pull Requests
Todo PR que inclui implementação na R2 deve atender os itens abaixo antes de ser revisado:
Sobre os testes:
- Testes foram escritos antes ou junto ao código de produção
- Testes são determinísticos (mesmo resultado em qualquer ambiente)
- Happy path está coberto
- Pelo menos um edge case ou cenário de erro está coberto
- Nenhum teste depende de estado externo não controlado (banco real, hora do sistema, variável de ambiente ausente)
- Mocks e stubs estão usados apenas onde necessário
Sobre a cobertura:
- Coverage mínima de 90% foi atingida para o código alterado
-
pytestpassa sem erros e warnings inesperados -
npm run testpassa sem erros
Sobre a revisão:
- O reviewer avaliou a qualidade dos testes, não apenas a do código
- Testes novos não foram adicionados apenas para aumentar métricas de coverage
O que não fazer
Não escrever testes depois da implementação e chamar de TDD. Testes escritos depois tendem a cobrir apenas o que o código já faz, não o que deveria fazer. TDD real exige a sequência correta: teste → código → refatoração.
Não usar mocks em excesso. Mockar tudo além do necessário cria testes que passam mas não detectam problemas reais de integração.
Não ignorar testes que falham intermitentemente (flaky tests). Um teste que falha às vezes é tão prejudicial quanto nenhum teste. Corrija ou remova antes de mergear.
Não considerar coverage como garantia de qualidade. 100% de coverage com assertions vazias ou triviais é pior que 70% com assertions significativas.
Relação com CI/CD
Na R2, o pipeline de CI deve executar automaticamente a suite de testes a cada push e bloquear merge se:
- Algum teste falhar
- A cobertura cair abaixo da meta definida
npm run lintounpm run buildfalharem
A configuração de CI está em .github/workflows/. Alterações no pipeline devem seguir as regras do AGENTS.md (análise prévia, sem CI/CD impulsivo).
Referências
estudos/sprint10/estudo_TDD_e_frameworks_de_teste.md— Estudo aprofundado realizado na Sprint 10 (Issue #113)docs/sdd/index.md— Visão geral do processo SDD/TDD do Squad 07specs/AGENTS.md— Fluxo obrigatório de specs antes da implementação- Issue #113 — TDD e Frameworks de Teste (estudo base)
- Issue #117 — Documentar Estratégia de TDD para R2
- CONTRIBUTING.md — Guia de contribuição do projeto