Estratégia de Testes¶
Para garantir a confiabilidade do ProtectKids sem tornar o processo de desenvolvimento lento, adotamos o conceito clássico da Pirâmide de Testes. A ideia central é ter uma base massiva de testes rápidos e isolados, e menos testes no topo (que são mais lentos e complexos).
1. Pirâmide de Testes¶
Unitários (Base)
Focam em validar o comportamento da menor unidade de código isolável no sistema (geralmente uma única função pura), **sem tocar em componentes externos** como bancos de dados ou APIs.
example "No contexto do ProtectKids"
Validar se uma função utilitária de limpeza remove caracteres especiais de uma ementa, ou se o extrator de texto formata strings corretamente.
Integração (Meio)
Validam se dois ou mais componentes internos ou sistemas conversam corretamente entre si, garantindo que a união de partes isoladas funciona como o esperado.
example "No contexto do ProtectKids"
Verificar se uma rota do FastAPI consegue receber um payload HTTP, processar as validações e persistir o modelo do SQLModel com sucesso dentro de um banco de dados de teste.
E2E (Topo)
Simulam a jornada completa do usuário final na aplicação, simulando cliques, fluxos de navegação e interações reais da tela até a persistência final de dados.
example "No contexto do ProtectKids"
Simular um usuário entrando no Dashboard em React, aplicando um filtro por tema legislativo e verificando se os gráficos de barras atualizam dinamicamente com os dados reais vindos do banco através da API.
2. Ecossistema do Backend com pytest e SQLModel¶
O pytest é o framework padrão para testes em Python devido à sua simplicidade e suporte robusto a Fixtures. Fixtures são funções que preparam o ambiente para os testes (ligando recursos) e limpam tudo após a execução.
Simulação do Banco de Dados com SQLite em Memória¶
Para testar operações de banco sem sujar ou depender do PostgreSQL local/produção, criamos uma fixture que gera um banco SQLite efêmero em memória RAM a cada ciclo de teste:
import pytest
from sqlmodel import SQLModel, Session, create_engine
from sqlmodel.pool import StaticPool
from database import get_session
from main import app
@pytest.fixture(name="session")
def session_fixture():
engine_teste = create_engine(
"sqlite://",
connect_args={"check_same_thread": False},
poolclass=StaticPool,
)
SQLModel.metadata.create_all(engine_teste)
with Session(engine_teste, expire_on_commit=False) as session:
yield session
SQLModel.metadata.drop_all(engine_teste)
@pytest.fixture(name="client")
def client_fixture(session: Session):
def get_session_override():
yield session
app.dependency_overrides[get_session] = get_session_override
from fastapi.testclient import TestClient
with TestClient(app) as client:
yield client
app.dependency_overrides.clear()
Por que SQLite em vez de PostgreSQL?
O SQLite em memória garante que os testes rodem em qualquer máquina, sem precisar do Docker ou de um banco instalado. Cada teste começa com um banco limpo e isolado, sem interferência entre execuções.
3. Isolando o Pipeline de ETL com Mocks¶
O pipeline de ETL não pode depender de conexões externas ativas. Se o portal da Câmara dos Deputados estiver fora do ar ou instável, a nossa esteira de CI/CD quebraria. Usamos objetos Mock para simular o comportamento dessas dependências externas.
Estratégias de Mock utilizadas
Mockando a API da Câmara (requests.get)
Interceptamos a chamada de rede e injetamos uma resposta estática contendo apenas o formato JSON esperado para validar se o nosso parser funciona.
Mockando o pdfplumber
Em vez de abrir e ler um arquivo binário pesado de um PDF real, simulamos o objeto do gerenciador de contexto do pdfplumber retornando um texto plano pré-definido.
4. Otimização de Performance no Motor de NLP¶
O carregamento do pipeline de linguagem do spaCy (pt_core_news_sm) exige processamento pesado e tempo de CPU significativos. Se instanciarmos o modelo a cada teste unitário executado, a suíte de testes demorará minutos para rodar.
Estratégia de Otimização: Fixture com escopo de sessão
Configurar uma fixture com scope="session" garante que o modelo de IA seja carregado na memória RAM uma única vez no início da execução da suíte inteira, sendo compartilhado entre todos os testes que avaliam o classificador.
import pytest
import spacy
@pytest.fixture(scope="session")
def nlp_model():
# Carregado apenas 1 vez para todos os testes do backend
return spacy.load("pt_core_news_sm")
5. Ferramentas de Teste para o Frontend (React)¶
Para garantir o comportamento do Dashboard e a renderização correta dos gráficos sem quebras visuais, o ecossistema React utiliza duas ferramentas complementares:
| Ferramenta | Papel |
|---|---|
| Jest | Executor de testes (test runner). Cria o ambiente simulado do navegador (DOM), gerencia as asserções (expect) e calcula a cobertura de código no ambiente JavaScript. |
| React Testing Library (RTL) | Fornece utilitários focados em testar o comportamento dos componentes sob a perspectiva do usuário real — busca o que está visível na tela (botões por texto, papéis semânticos), não estados internos do código. |
Casos de Testes Críticos no Frontend¶
Renderização de Elementos-Chave
Validar se os cards de resumo e gráficos carregam com a paleta visual correta do projeto.
Interações do Painel
Simular o clique em uma tag de tema ou partido e verificar se a função de filtragem dispara a requisição esperada na API.
6. Como Rodar os Testes¶
Pré-requisitos¶
# Windows (PowerShell)
cd backend
.\venv\Scripts\Activate.ps1
# Linux/macOS
cd backend
source venv/bin/activate
Comandos principais¶
# Rodar toda a suíte
pytest tests/ -v
# Rodar com relatório de cobertura no terminal
pytest tests/ --cov=. --cov-report=term-missing -v
# Gerar relatório visual em HTML
pytest tests/ --cov=. --cov-report=html
# Rodar testes de um arquivo específico
pytest tests/test_analytics.py -v
Atenção
A pasta htmlcov/ e o arquivo .coverage são gerados localmente e não devem ser commitados. Ambos já estão listados no .gitignore do projeto.
7. Cobertura Atual¶
Name Stmts Miss Cover
----------------------------------------------------
database.py 10 2 80%
main.py 165 31 81%
models.py 38 0 100%
tests/conftest.py 42 0 100%
tests/test_analytics.py 60 0 100%
tests/test_busca_por_id.py 49 0 100%
tests/test_filtros_origem.py 17 0 100%
tests/test_main.py 49 0 100%
tests/test_normalizacao.py 17 0 100%
tests/test_tramitacoes.py 38 0 100%
----------------------------------------------------
TOTAL 381 33 91%
Cobertura Total: 91% — 43 testes, 0 falhas
O projeto supera a marca recomendada de 80% de cobertura, com models.py e todos os arquivos de teste atingindo 100%.