O que significa “modelar” casos de teste para APIs
Modelar casos de teste é transformar requisitos (contrato, regras de negócio e expectativas de consumo) em um conjunto de cenários verificáveis, cobrindo: positivos (uso correto), negativos (uso incorreto/hostil) e limites (bordas onde falhas são comuns). O objetivo não é “testar tudo”, e sim criar uma suíte pequena, mas representativa, que maximize a chance de detectar defeitos com o menor número de testes.
Uma forma prática de pensar é separar cada endpoint em três camadas de variação:
- Entrada: body, query/path params, headers relevantes, contexto de autenticação/escopo.
- Processamento: regras (validações, cálculos, transições de estado, consistência entre recursos).
- Saída: status, payload, headers de resposta, efeitos colaterais (criação/alteração de dados).
Técnicas para derivar casos a partir de entradas e saídas
1) Particionamento de equivalência (PE)
Particionamento de equivalência divide o domínio de entrada em classes onde o sistema tende a se comportar de forma semelhante. Em vez de testar todos os valores, você escolhe representantes de cada classe.
Exemplo (campo age em um cadastro):
- Classe válida:
ageentre 18 e 120 (inclusive). - Classe inválida (abaixo):
age< 18. - Classe inválida (acima):
age> 120. - Classe inválida (tipo): string, null, boolean.
Casos mínimos derivados por PE: 1 valor de cada classe (ex.: 25, 17, 121, “vinte”, null).
- Ouça o áudio com a tela desligada
- Ganhe Certificado após a conclusão
- + de 5000 cursos para você explorar!
Baixar o aplicativo
2) Análise de valores limite (VL)
Valores limite focam nas bordas das regras, onde erros de comparação (< vs <=) e arredondamentos são frequentes. Para cada faixa, teste: limite inferior, logo abaixo, logo acima e limite superior.
Exemplo (mesma regra 18..120):
- 17 (abaixo), 18 (limite), 19 (acima)
- 119 (abaixo), 120 (limite), 121 (acima)
Para strings, limites comuns: minLength, maxLength, vazio, e “max+1”. Para listas/paginação: 0, 1, limite máximo, máximo+1.
3) Tabelas de decisão
Tabelas de decisão ajudam quando o resultado depende de combinações de condições. Você lista condições (entradas/contexto) e ações/saídas esperadas, e então escolhe um conjunto de regras representativas.
Exemplo (criação de pedido com cupom):
| Regra | Usuário ativo | Cupom existe | Cupom expirado | Valor mínimo atingido | Resultado |
|---|---|---|---|---|---|
| R1 | Sim | Sim | Não | Sim | Aplicar desconto |
| R2 | Sim | Sim | Sim | Sim | Rejeitar cupom |
| R3 | Sim | Não | - | - | Rejeitar cupom |
| R4 | Não | Sim | Não | Sim | Rejeitar (usuário inválido) |
| R5 | Sim | Sim | Não | Não | Rejeitar (mínimo não atingido) |
Em vez de testar todas as combinações possíveis, você cobre as regras que representam comportamentos distintos.
4) Transições de estado
Transições de estado modelam recursos que mudam de “fase” (ex.: pedido, assinatura, ticket). O foco é validar: transições permitidas, transições proibidas, e efeitos (campos alterados, timestamps, eventos).
Exemplo (pedido): DRAFT -> CONFIRMED -> PAID -> SHIPPED -> DELIVERED e cancelamento permitido até PAID (por exemplo).
- Positivo:
CONFIRMED -> PAIDretorna sucesso e atualiza status. - Negativo: tentar
SHIPPED -> PAIDdeve falhar. - Limite: ações repetidas (ex.: pagar duas vezes) e concorrência (duas requisições de pagamento simultâneas) devem manter consistência.
Passo a passo prático para modelar casos por endpoint
Passo 1: Liste entradas e saídas observáveis
- Entradas: campos do body, query/path params, headers relevantes ao comportamento, contexto de permissão/escopo.
- Saídas: status, corpo, headers de resposta, e efeitos persistidos (consultáveis via GET).
Passo 2: Identifique regras e invariantes
Invariantes são verdades que devem se manter em qualquer cenário válido. Exemplos típicos:
- Campos obrigatórios sempre presentes em respostas de sucesso.
- IDs retornados são únicos e podem ser consultados.
- Operações de atualização não removem campos não relacionados.
- Listagens respeitam paginação e ordenação solicitadas.
Passo 3: Aplique PE e VL aos campos críticos
Priorize campos com maior risco: dinheiro, datas, identificadores, enums de status, limites de paginação, campos usados em filtros, e qualquer campo com validação complexa.
Passo 4: Use tabela de decisão para combinações de regras
Quando houver múltiplas condições influenciando o resultado, modele com tabela e selecione regras representativas (incluindo as que causam falha).
Passo 5: Modele estados (quando houver ciclo de vida)
Desenhe o grafo de estados e derive casos para: transições válidas, inválidas, repetição, e tentativas fora de ordem.
Passo 6: Defina oráculos de teste (o que validar)
Para cada caso, defina verificações objetivas:
- Status esperado.
- Campos do payload (presença, tipo, valores, coerência).
- Efeitos: consultar depois e comparar (ex.: GET após POST/PUT/PATCH/DELETE).
- Não regressão: mudanças não devem afetar endpoints relacionados (consistência).
Como combinar parâmetros, headers e autenticação sem explosão combinatória
APIs reais têm muitas dimensões de variação: parâmetros, formatos, headers, perfis de usuário, escopos, e estados do recurso. Testar o produto cartesiano completo é inviável. Em vez disso, combine técnicas de seleção baseada em risco com cobertura por pares (pairwise) e testes direcionados para interações críticas.
Estratégia 1: Separe “o que muda o comportamento” do que é “ruído”
- Alta influência: parâmetros que alteram regra de negócio, filtros, ordenação, paginação, flags, modo de cálculo, escopo/role.
- Baixa influência: headers que não alteram lógica (ex.: alguns de rastreio), campos opcionais que não afetam processamento.
Teste exaustivo apenas para alta influência; para o resto, amostre.
Estratégia 2: Matriz mínima por dimensão (baseline + variações)
Crie um caso baseline (requisição válida e comum) e varie uma dimensão por vez para isolar falhas. Depois, adicione poucos testes de interação (2 ou 3) onde há risco conhecido.
Exemplo de dimensões em um GET de listagem:
- Paginação:
page,pageSize - Ordenação:
sort - Filtro:
status,createdFrom/To - Autorização: usuário comum vs admin
Você pode ter: 1 baseline + 1 variação por dimensão + 2 interações críticas (ex.: filtro+ordenação, paginação+ordenação).
Estratégia 3: Pairwise (cobertura por pares) para combinações
Quando há muitas opções por dimensão, use pairwise para garantir que cada par de valores entre dimensões apareça ao menos uma vez. Isso reduz drasticamente o número de testes e ainda captura muitos bugs de integração entre parâmetros.
Quando usar: múltiplos filtros e flags com histórico de bugs, ou endpoints muito usados.
Estratégia 4: Seleção baseada em risco (critérios objetivos)
Para decidir quais combinações entram na suíte, pontue cada variação por:
- Impacto: financeiro, segurança, compliance, dados sensíveis.
- Probabilidade: complexidade da regra, mudanças recentes, incidentes anteriores.
- Exposição: volume de tráfego, clientes críticos, automação existente.
Inclua sempre: caminhos críticos, bordas, e falhas comuns (tipos inválidos, valores fora de faixa, estados inválidos).
Exemplos de suítes modeladas (com cenários positivos, negativos e limites)
Suíte 1: CRUD de um recurso (ex.: /users)
Objetivo: validar ciclo básico e consistência entre operações.
| Categoria | Caso | Validações principais |
|---|---|---|
| Positivo | POST cria usuário válido | Resposta contém id; GET por id retorna os mesmos dados persistidos |
| Positivo | PUT/PATCH atualiza campo permitido | Somente campos alterados mudam; demais permanecem |
| Positivo | DELETE remove usuário | GET após delete não encontra; listagem não inclui |
| Negativo | GET por id inexistente | Erro padronizado; não vaza detalhes internos |
| Negativo | Atualizar com payload inválido | Erros por campo; nada é persistido |
| Limite | Repetir DELETE | Comportamento consistente (idempotência observável conforme regra do sistema) |
Passo a passo sugerido para automatizar:
- Crie um usuário (POST) e capture o
id. - Consulte (GET) e compare campos relevantes.
- Atualize (PATCH/PUT) e valide persistência via novo GET.
- Delete e valide ausência via GET e via listagem.
Suíte 2: Validação de campos (PE + VL)
Objetivo: cobrir regras de validação com poucos casos bem escolhidos.
Exemplo (payload simplificado):
{ "name": "...", "email": "...", "age": 0, "document": "..." }| Campo | Partições (exemplos) | Valores limite (exemplos) |
|---|---|---|
| name | válido; vazio; muito longo; tipo inválido | minLength, minLength-1, maxLength, maxLength+1 |
| válido; sem @; domínio inválido; null | tamanho máximo; caracteres especiais | |
| age | válido; abaixo; acima; tipo inválido | 17/18/19 e 119/120/121 |
| document | válido; formato inválido; dígito verificador errado | tamanho exato; tamanho-1; tamanho+1 |
Dica de modelagem: combine 1 erro por vez para diagnóstico rápido e adicione poucos casos com múltiplos erros apenas para validar agregação de mensagens.
Suíte 3: Paginação (limites e consistência)
Objetivo: garantir previsibilidade em listagens e evitar bugs de borda.
- Positivo: primeira página com tamanho padrão retorna lista e metadados coerentes (ex.: total, page, pageSize, next/prev quando aplicável).
- Limite:
pageSize=1epageSize=max(permitido) retornam quantidades corretas. - Negativo:
page=0,pageSize=0,pageSize=max+1devem falhar de forma consistente. - Consistência: navegar páginas não deve repetir/omitir itens quando a ordenação é estável e o dataset não muda durante o teste.
Passo a passo:
- Crie massa mínima (ex.: 25 itens) para tornar a paginação observável.
- Liste com
pageSize=10e valide contagem por página. - Valide que a união dos IDs das páginas 1..3 tem 25 itens únicos.
- Teste limites de
pageSizee entradas inválidas.
Suíte 4: Erros padronizados (consistência entre cenários)
Objetivo: garantir que diferentes endpoints e diferentes tipos de falha retornem o mesmo “shape” de erro e campos mínimos para rastreio.
Modele uma lista de falhas representativas e aplique em múltiplos endpoints:
- Payload inválido (campo obrigatório ausente).
- Tipo inválido (string onde era número).
- Recurso inexistente.
- Conflito de negócio (duplicidade, estado inválido).
Validações:
- Estrutura do erro é consistente (mesmos campos, tipos, e presença de identificador de correlação quando aplicável).
- Mensagens por campo aparecem onde faz sentido (validação) e não aparecem onde não faz (ex.: inexistente).
- Não há vazamento de stack trace, SQL ou detalhes internos.
Suíte 5: Consistência entre endpoints (invariantes do domínio)
Objetivo: detectar inconsistências quando diferentes endpoints representam o mesmo dado.
Exemplos de invariantes:
- Após criar um recurso em
POST /users, ele deve aparecer emGET /users(respeitando filtros) e emGET /users/{id}. - Se
GET /orders/{id}retornastatus=PAID, entãoGET /orders?status=PAIDdeve incluir esse pedido (quando filtros equivalentes são aplicados). - Se um endpoint retorna um resumo (ex.:
/users) e outro retorna detalhe (ex.:/users/{id}), campos comuns devem ter valores coerentes.
Passo a passo para um teste de consistência típico:
- Crie/atualize um recurso para um estado conhecido.
- Consulte por múltiplos endpoints (detalhe, lista, busca por filtro).
- Compare campos-chave (id, status, timestamps relevantes, relacionamentos).
Modelos prontos (templates) de casos para acelerar a derivação
Template de caso positivo
- Dado: pré-condições (ex.: recurso existe, usuário com permissão X).
- Quando: requisição com payload/params válidos (baseline).
- Então: valida status, schema mínimo, e efeito persistido (GET de verificação).
Template de caso negativo
- Dado: pré-condição válida (para isolar a falha na entrada).
- Quando: 1 variação inválida (tipo, formato, fora de faixa, estado proibido).
- Então: valida erro consistente e ausência de efeito colateral (não persistiu).
Template de caso de limite
- Dado: dataset/estado preparado para tornar o limite observável.
- Quando: valores na borda (min, min-1, max, max+1) ou transição no limiar.
- Então: valida comportamento correto e estabilidade (sem flutuações inesperadas).