Testing Padronizado - C-Suite
Visão Geral
Este documento descreve o framework de testes padronizado para o ecossistema C-Suite, incluindo fixtures, helpers e boas práticas.
Módulo de Testing
Localização
O módulo principal está em: common_testing.py (raiz do projeto)
Características
- Fixtures pytest: Fixtures padronizadas para testes
- TestClient: Helper para testes de API FastAPI
- Mock de cache: Fixture para mock de Redis
- Assert helpers: Funções de assert padronizadas
- Skip helpers: Helpers para pular testes quando dependências não estão disponíveis
Uso Básico
1. Testes de API
import pytest
from fastapi import FastAPI
from common_testing import test_client, assert_json_response
app = FastAPI()
@app.get("/api/test")
async def test_endpoint():
return {"status": "ok"}
def test_endpoint_success(test_client):
response = test_client(app).get("/api/test")
data = assert_json_response(response, expected_status=200)
assert data["status"] == "ok"
2. Testes com Banco de Dados
import pytest
from common_testing import test_db_session
def test_create_organization(test_db_session):
# Cria organização
org = Organization(name="Test Org")
test_db_session.add(org)
test_db_session.commit()
# Verifica
result = test_db_session.query(Organization).filter_by(name="Test Org").first()
assert result is not None
assert result.name == "Test Org"
3. Testes com Cache
import pytest
from common_testing import mock_cache
from common_cache import get_cache
def test_cache_operations(mock_cache):
cache = get_cache()
cache.set("key", "value", ttl=300)
value = cache.get("key")
assert value == "value"
4. Assert Helpers
from common_testing import assert_json_response, assert_error_response
def test_success_response(client):
response = client.get("/api/success")
data = assert_json_response(response, expected_status=200)
assert "data" in data
def test_error_response(client):
response = client.get("/api/not-found")
assert_error_response(response, expected_status=404, error_code="ERR_5000")
Estrutura de Testes
Organização Recomendada
app/
├── app/
│ └── main.py
└── tests/
├── conftest.py # Configuração pytest
├── test_api.py # Testes de API
├── test_models.py # Testes de modelos
├── test_services.py # Testes de serviços
└── integration/
└── test_e2e.py # Testes E2E
conftest.py Padrão
import pytest
import sys
from pathlib import Path
# Adiciona raiz do projeto ao path
sys.path.insert(0, str(Path(__file__).resolve().parents[3]))
# Importa fixtures comuns
from common_testing import (
test_db_session,
test_client,
mock_cache,
temp_dir
)
# Fixtures específicas do app
@pytest.fixture
def app():
from app.main import app
return app
Tipos de Testes
1. Unit Tests
Testam unidades isoladas de código:
def test_calculate_score():
from app.services.scoring import calculate_score
score = calculate_score(intent=0.8, margin=0.15)
assert score == 0.95
2. Integration Tests
Testam integração entre componentes:
def test_create_policy_integration(test_db_session):
from app.services.policies import create_policy
policy = create_policy(
org_id=1,
rule_key="min_margin",
value=0.15,
db=test_db_session
)
assert policy.rule_key == "min_margin"
assert policy.rule_value == 0.15
3. API Tests
Testam endpoints da API:
def test_list_policies(test_client, app):
client = test_client(app)
response = client.get("/api/policies/1")
data = assert_json_response(response, expected_status=200)
assert isinstance(data, list)
4. E2E Tests
Testam fluxos completos:
def test_decision_flow_e2e(test_client, app):
client = test_client(app)
# 1. Criar política
response = client.post("/api/policies/1", json={
"rule_key": "min_margin",
"rule_value": 0.15
})
assert_json_response(response, expected_status=201)
# 2. Fazer decisão (deve respeitar política)
response = client.post("/api/decide", json={
"customer_id": 123,
"org_id": 1,
"candidates": [...]
})
data = assert_json_response(response, expected_status=200)
assert data["decision"]["margin"] >= 0.15
Cobertura de Testes
Meta: > 80% de Cobertura
# Instalar pytest-cov
pip install pytest-cov
# Executar testes com cobertura
pytest --cov=app --cov-report=html --cov-report=term
# Ver relatório HTML
open htmlcov/index.html
Cobertura Mínima por Tipo
- Lógica de negócio: > 90%
- APIs: > 80%
- Serviços: > 80%
- Utils: > 70%
Mock e Stubs
Mock de Serviços Externos
from unittest.mock import patch, MagicMock
def test_external_service(mock_cache):
with patch('httpx.get') as mock_get:
mock_get.return_value.json.return_value = {"status": "ok"}
# Testa código que usa httpx
result = call_external_service()
assert result["status"] == "ok"
Mock de Banco de Dados
def test_with_mock_db():
with patch('app.db.get_db') as mock_db:
mock_session = MagicMock()
mock_db.return_value = mock_session
# Testa código que usa banco
result = get_data()
mock_session.query.assert_called_once()
Testes de Performance
import time
def test_endpoint_performance(test_client, app):
client = test_client(app)
start = time.time()
response = client.get("/api/endpoint")
elapsed = time.time() - start
assert response.status_code == 200
assert elapsed < 0.1 # Deve responder em < 100ms
Testes de Segurança
def test_sql_injection_protection(test_client, app):
client = test_client(app)
# Tenta SQL injection
response = client.get("/api/data?sort=; DROP TABLE users;--")
# Deve retornar erro de validação, não executar SQL
assert_error_response(response, expected_status=400)
def test_authentication_required(test_client, app):
client = test_client(app)
# Tenta acessar endpoint protegido sem auth
response = client.get("/api/protected")
assert_error_response(response, expected_status=401)
CI/CD Integration
GitHub Actions
name: Tests
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-python@v2
with:
python-version: '3.9'
- run: pip install -r requirements.txt
- run: pip install pytest pytest-cov
- run: pytest --cov=app --cov-report=xml
- uses: codecov/codecov-action@v2
GitLab CI
test:
script:
- pip install -r requirements.txt
- pip install pytest pytest-cov
- pytest --cov=app --cov-report=xml
coverage: '/TOTAL.*\s+(\d+%)$/'
Boas Práticas
- Teste primeiro (TDD): Escreva testes antes do código quando possível
- Testes isolados: Cada teste deve ser independente
- Nomes descritivos:
test_create_policy_with_valid_data - Arrange-Act-Assert: Organize testes em 3 partes
- Mock externos: Mock serviços externos e APIs
- Teste casos de erro: Teste não apenas sucesso, mas também erros
- Mantenha testes atualizados: Atualize testes quando código muda
Próximos Passos
- ✅ Módulo de testing criado
- ⏳ Criar testes para módulos comuns (common_errors, common_auth, etc.)
- ⏳ Adicionar testes em todos os apps
- ⏳ Configurar CI/CD para executar testes
- ⏳ Aumentar cobertura para > 80%
- ⏳ Adicionar testes de performance
- ⏳ Adicionar testes de segurança