Apresentação
Estratégia de Testes — studyAI
Testes unitários, integração, E2E, prompts e RAG. Critérios mínimos antes de deploy.
Índice
- Pirâmide de testes
- Testes unitários
- Testes de integração
- Testes E2E
- Testes de prompts e agentes
- Testes do RAG
- Datasets de avaliação
- Critérios antes de deploy
Pirâmide de testes
┌─────────┐
│ E2E │ Poucos, smoke critical paths
├─────────┤
│ Integr. │ API, DB, RAG
├─────────┤
│ Unit │ Services, utils, chunking
└─────────┘
Testes unitários
Services
- AuthService: hash password, verify, token generation
- Chunking: split_by_headers, chunk size, overlap
- Embedding: mock OpenAI, verificar formato de output
- Router (rules): "avalia" → evaluate, "resume" → summarize
- Evaluator (rule-based): keywords overlap, length ratio → score
- SM-2: next_review_at, ease_factor, interval
Utils
- Normalização de texto (trim, lowercase onde aplicável)
- Parse JSON de LLM (fallback, extração de blocos)
Ferramenta: pytest
Testes de integração
API
- POST /auth/register → 201, user criado
- POST /auth/login → 200, tokens
- GET /projects (auth) → 200, lista
- GET /projects (sem auth) → 401
- POST /projects/:id/files → 201, file em DB
- POST /projects/:id/chat → 200, resposta (mock LLM)
- POST /projects/:id/evaluate → 200, score
DB
- Migrations aplicam sem erro
- Constraints (unique, FK) funcionam
RAG
- Chunk → embed → store → retrieve (roundtrip)
- Filtro project_id em retrieval (isolamento)
Ferramenta: pytest + httpx (TestClient FastAPI), DB em memória ou testcontainers
Testes E2E
Smoke: fluxo completo
- Upload PDF → status processed → query → resposta
- Generate (topic) → job completed → documento + perguntas
- Chat → resposta com citações
- Evaluate → score + feedback
Ferramenta: pytest + Playwright ou Cypress (opcional para MVP)
Smoke simplificado (API only)
- Upload → index → POST /query → resposta não vazia
- POST /generate → GET /jobs/:id → status completed
Testes de prompts e agentes
Golden tests
- Router: Conjunto de mensagens → intent esperado
- "avalia a minha resposta" → evaluate
- "resume o documento" → summarize
- "o que é X?" → qa
- Content Planner: Topic + chunks mock → estrutura com N secções
- Question Evaluator: Resposta correta vs errada → score alto vs baixo
Formato
Ficheiro tests/golden/router.json:
[
{"input": "avalia isto", "expected_intent": "evaluate"},
{"input": "o que é RAG?", "expected_intent": "qa"}
]
Test: carregar JSON, chamar Router (mock LLM ou real com seed), assert intent.
Testes do RAG
Retrieval
- Query → top-k chunks relevantes (ground truth manual)
- Chunks de outro projeto não aparecem (project_id filter)
Groundedness (eval)
- Dataset: 10–20 (query, expected_source_chunks)
- Retrieval recall: % de queries onde expected chunks estão no top-k
- Resposta: LLM-as-judge ou LangSmith eval → groundedness score
Eval set (RAG QA)
Ficheiro data/eval/rag_qa.json:
[
{
"query": "O que é uma colonoscopia?",
"expected_topics": ["colonoscopia", "exame", "endoscopia"],
"min_groundedness": 0.8
}
]
Datasets de avaliação
| Dataset | Uso | Tamanho |
|---|---|---|
| router_golden | Intent classification | 20–50 exemplos |
| rag_qa_eval | Retrieval + groundedness | 10–20 por projeto tipo |
| evaluator_golden | Score consistency | 10 pares (resposta, score esperado) |
Manutenção: Adicionar casos quando bugs forem encontrados (regressão).
Critérios antes de deploy
| Critério | Mínimo |
|---|---|
| Unit tests | Cobertura > 70% em services críticos |
| Integration | Auth, projects, chat, evaluate passam |
| E2E smoke | Upload → index → chat passa |
| RAG eval | retrieval_recall > 0.7 em eval set |
| Golden prompts | Router accuracy > 90% |
| Sem regressões | Golden tests passam |
Estrutura de pastas
api/
├── tests/
│ ├── unit/
│ │ ├── test_auth.py
│ │ ├── test_chunking.py
│ │ ├── test_router_rules.py
│ │ └── test_evaluator_rules.py
│ ├── integration/
│ │ ├── test_api_auth.py
│ │ ├── test_api_projects.py
│ │ └── test_rag.py
│ ├── e2e/
│ │ └── test_smoke.py
│ └── golden/
│ ├── router.json
│ └── evaluator.json
data/
└── eval/
└── rag_qa.json
Zona de prática
Sem perguntas. Clica em Editar para adicionar.