Além do contrato: estrutura vs. significado
Validar o contrato (por exemplo, via schema) garante que a resposta “parece correta”: tipos, campos obrigatórios, formatos e limites básicos. Já a validação de regras de negócio garante que a resposta “é correta”: valores coerentes com o domínio, cálculos consistentes, estados válidos e efeitos colaterais realmente aplicados.
Em testes de API, uma boa estratégia combina:
- Validações estruturais (schema): forma e tipos do payload.
- Validações semânticas (regras de negócio): invariantes do domínio, integridade, consistência temporal, transições de estado, cálculos e efeitos colaterais.
O objetivo é reduzir falsos positivos: testes que passam porque o JSON tem o formato certo, mas o sistema está errado (ex.: total do pedido incorreto, item duplicado, status inválido, atualização não persistida).
Camadas de validação recomendadas
1) Estrutural (schema) como “porta de entrada”
Use a validação estrutural para garantir que o teste não quebre por detalhes triviais e para detectar regressões de contrato rapidamente. Em seguida, aplique validações semânticas específicas do caso de uso.
Exemplo de checagens estruturais típicas (independente da ferramenta):
- Ouça o áudio com a tela desligada
- Ganhe Certificado após a conclusão
- + de 5000 cursos para você explorar!
Baixar o aplicativo
- Campos obrigatórios presentes (
id,status,items). - Tipos corretos (
itemsé array,quantityé inteiro,priceé número). - Formatos (
createdAtem ISO-8601,currencyem padrão esperado). - Restrições simples (quantidade > 0, strings não vazias).
2) Semântica (regras de negócio) como “garantia de verdade”
Depois do schema, valide o que o domínio exige. Essas validações variam por produto, mas há categorias recorrentes:
- Integridade: relacionamentos, unicidade, consistência entre campos.
- Cálculos: totais, descontos, impostos, arredondamentos.
- Estados e transições: máquina de estados (ex.: pedido não volta de
PAIDparaCREATED). - Efeitos colaterais: criação/atualização persistida, eventos gerados, estoques alterados.
Técnicas para validar integridade (relacionamentos, unicidade, consistência)
Relacionamentos: IDs referenciam entidades existentes
Quando um recurso referencia outro (ex.: customerId, productId), valide que:
- O ID existe (consulta posterior ao recurso referenciado).
- O relacionamento é permitido (ex.: produto pertence ao catálogo da loja, endereço pertence ao cliente).
Exemplo: ao criar um pedido, a resposta traz customerId. Um teste robusto consulta o cliente e valida que o cliente está ativo e corresponde ao esperado.
Unicidade: evitar duplicidades silenciosas
Unicidade pode existir em vários níveis:
- Chaves naturais: e-mail de usuário, SKU de produto.
- Itens dentro de um agregado: um pedido não deve ter o mesmo
productIdrepetido (ou, se permitir, deve consolidar quantidades). - Idempotência: a mesma requisição não deve criar múltiplos recursos quando repetida (quando aplicável).
Checagens práticas:
- Após criar, liste/consulte e garanta que há exatamente 1 ocorrência do identificador único.
- Se a API suporta chave de idempotência, repita a operação com a mesma chave e valide que o
idretornado é o mesmo e que não houve duplicação.
Consistência entre campos: invariantes do domínio
Exemplos comuns de invariantes:
total= soma de (item.unitPrice * item.quantity) - descontos + impostos.paidAtsó pode existir sestatusindicar pagamento concluído.cancelReasonobrigatório quandostatuséCANCELLED.currencydeve ser a mesma em todos os itens e no total.
Essas validações normalmente não estão no schema (ou ficam complexas demais), então devem estar no teste.
Validação de cálculos: precisão, arredondamento e regras
O que validar
- Fórmula: total, subtotal, descontos, frete, impostos.
- Arredondamento: regras de casas decimais (ex.: 2 casas para moeda), arredondamento bancário vs. padrão.
- Ordem de aplicação: desconto antes do imposto? imposto por item ou no total?
- Limites: desconto não pode exceder subtotal; total não pode ser negativo.
Passo a passo prático: testando total de pedido
Exemplo de cenário: criar pedido com dois itens e cupom de desconto.
- Pré-condição: garanta que os produtos existem e têm preços conhecidos (idealmente via fixture/seed do ambiente de teste).
- Ação: crie o pedido com itens e cupom.
- Validação estrutural: campos e tipos esperados (
items,totals,currency). - Validação semântica: calcule no teste o subtotal e aplique a regra de desconto e imposto conforme especificação do domínio.
- Pós-condição: consulte o pedido novamente e valide que os totais persistiram iguais (evita falso positivo por resposta “montada” sem persistência).
Exemplo de pseudo-código de asserções (ilustrativo):
// dados do teste (controlados no ambiente de teste) itemA: 10.00, itemB: 25.00 subtotal = 10*2 + 25*1 = 45.00 desconto = 10% => 4.50 totalEsperado = 40.50 assert response.totals.subtotal == 45.00 assert response.totals.discount == 4.50 assert response.totals.total == 40.50 // pós-condição: GET /orders/{id} e repetir asserçõesSe houver arredondamento, evite comparar floats diretamente; compare com tolerância ou compare valores em centavos (inteiros) quando possível.
Estados e transições: testando máquinas de estado
Modelando transições permitidas
Recursos com ciclo de vida (pedido, assinatura, entrega) costumam ter estados e transições válidas. Um teste eficaz valida:
- Estado inicial após criação.
- Transições válidas (ex.:
CREATED→PAID→SHIPPED). - Transições inválidas (ex.:
SHIPPED→CREATED). - Campos dependentes do estado (ex.:
paidAtpreenchido ao pagar;shippedAtao enviar).
Passo a passo prático: validando transição de status de pedido
- Pré-condição: crie um pedido em estado inicial conhecido (
CREATED). - Ação 1: execute a operação de pagamento.
- Valide: status mudou para
PAIDepaidAtexiste e é recente (dentro de uma janela aceitável). - Ação 2: tente cancelar após pago (se a regra proíbe).
- Valide: a API rejeita e o status permanece
PAID(pós-condição via consulta).
Validação temporal: ao checar timestamps, use janelas (ex.: “entre início do teste e agora”) para evitar flakiness.
Efeitos colaterais: criação, atualização e persistência real
Por que validar efeitos colaterais
Uma resposta pode estar correta, mas a alteração não ter sido persistida (ou ter sido persistida parcialmente). Para evitar isso, inclua pós-condições verificáveis:
- Consulta posterior do recurso criado/atualizado.
- Consulta de recursos relacionados (ex.: estoque, saldo, histórico).
- Listagens para garantir que o recurso aparece onde deveria (com filtros previsíveis).
Passo a passo prático: criação com verificação posterior
- Ação: crie um recurso (ex.: pedido) e capture o
id. - Valide na resposta: campos essenciais (incluindo os que o servidor calcula).
- Pós-condição 1: consulte por
ide compare campos críticos (status, itens, totais). - Pós-condição 2: valide efeito colateral (ex.: reserva de estoque diminuiu; ou registro de auditoria foi criado, se exposto por API).
Quando efeitos colaterais são assíncronos, use polling com timeout e condição clara (ex.: “até 10s, consultando a cada 500ms, até status virar RESERVED”).
Desenho de testes com pré-condições e pós-condições verificáveis
Estrutura recomendada: Arrange / Act / Assert + Verificações posteriores
Um padrão simples que reduz falsos positivos:
- Arrange (pré-condições): preparar dados e garantir estado inicial verificável.
- Act (ação): executar a chamada principal.
- Assert (validações imediatas): schema + regras de negócio no payload retornado.
- Assert (pós-condições): consultar novamente e validar persistência e efeitos colaterais.
Pré-condições: como torná-las verificáveis
Pré-condições frágeis geram testes instáveis. Boas práticas:
- Dados controlados: use entidades criadas pelo próprio teste ou fixtures do ambiente.
- Checagem do estado inicial: antes de agir, confirme que o recurso está no estado esperado (ex.: estoque disponível, pedido inexistente).
- Isolamento: use identificadores únicos por execução (ex.: e-mail com sufixo aleatório) para evitar colisões.
Pós-condições: o que checar para evitar falsos positivos
- Persistência: o recurso existe e mantém os valores esperados.
- Imutabilidade: campos que não deveriam mudar permanecem iguais (ex.:
createdAt,id). - Atualização parcial: ao atualizar um campo, valide que outros não foram alterados indevidamente.
- Consistência eventual: quando aplicável, valide convergência do estado com polling e timeout.
Checklist de validações semânticas frequentes
| Categoria | O que validar | Exemplo de asserção |
|---|---|---|
| Integridade | Relacionamento e existência | customerId do pedido existe e está ativo |
| Unicidade | Sem duplicação | mesmo e-mail não cria dois usuários; mesmo item não duplica no pedido |
| Consistência | Campos coerentes entre si | paidAt só existe se status == PAID |
| Cálculos | Totais e arredondamento | total == subtotal - discount + tax com regra de casas decimais |
| Estados | Transições válidas | CREATED -> PAID permitido; SHIPPED -> CREATED proibido |
| Efeitos colaterais | Persistência e impactos | após pagar, consulta mostra status=PAID e estoque reservado |
Padrões para escrever asserções mais robustas
Compare o que importa (e só o que importa)
Evite asserções frágeis em campos voláteis (timestamps exatos, IDs gerados, ordenação não garantida). Prefira:
- Janelas de tempo (ex.:
createdAtentret0et1). - Comparação por conjuntos (itens contêm os IDs esperados, independentemente da ordem).
- Normalização (valores monetários em centavos; strings com trim/case quando a regra permitir).
Valide invariantes em múltiplos pontos
Uma regra de negócio crítica deve ser validada:
- Na resposta imediata (feedback rápido).
- Na consulta posterior (persistência).
- Em recursos relacionados quando aplicável (efeito colateral).
Crie funções de asserção reutilizáveis por domínio
Em vez de repetir lógica de cálculo e invariantes em cada teste, encapsule em helpers, por exemplo:
assertOrderTotals(order)assertValidOrderState(order)assertNoDuplicateItems(order.items)
Isso melhora manutenção e reduz o risco de testes inconsistentes entre si.