Validação de dados e regras de negócio em testes de API: além do contrato

Capítulo 9

Tempo estimado de leitura: 8 minutos

+ Exercício

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):

Continue em nosso aplicativo e ...
  • Ouça o áudio com a tela desligada
  • Ganhe Certificado após a conclusão
  • + de 5000 cursos para você explorar!
ou continue lendo abaixo...
Download App

Baixar o aplicativo

  • Campos obrigatórios presentes (id, status, items).
  • Tipos corretos (items é array, quantity é inteiro, price é número).
  • Formatos (createdAt em ISO-8601, currency em 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 PAID para CREATED).
  • 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 productId repetido (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 id retornado é 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.
  • paidAt só pode existir se status indicar pagamento concluído.
  • cancelReason obrigatório quando status é CANCELLED.
  • currency deve 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.

  1. Pré-condição: garanta que os produtos existem e têm preços conhecidos (idealmente via fixture/seed do ambiente de teste).
  2. Ação: crie o pedido com itens e cupom.
  3. Validação estrutural: campos e tipos esperados (items, totals, currency).
  4. Validação semântica: calcule no teste o subtotal e aplique a regra de desconto e imposto conforme especificação do domínio.
  5. 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ções

Se 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.: CREATEDPAIDSHIPPED).
  • Transições inválidas (ex.: SHIPPEDCREATED).
  • Campos dependentes do estado (ex.: paidAt preenchido ao pagar; shippedAt ao enviar).

Passo a passo prático: validando transição de status de pedido

  1. Pré-condição: crie um pedido em estado inicial conhecido (CREATED).
  2. Ação 1: execute a operação de pagamento.
  3. Valide: status mudou para PAID e paidAt existe e é recente (dentro de uma janela aceitável).
  4. Ação 2: tente cancelar após pago (se a regra proíbe).
  5. 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

  1. Ação: crie um recurso (ex.: pedido) e capture o id.
  2. Valide na resposta: campos essenciais (incluindo os que o servidor calcula).
  3. Pós-condição 1: consulte por id e compare campos críticos (status, itens, totais).
  4. 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

CategoriaO que validarExemplo de asserção
IntegridadeRelacionamento e existênciacustomerId do pedido existe e está ativo
UnicidadeSem duplicaçãomesmo e-mail não cria dois usuários; mesmo item não duplica no pedido
ConsistênciaCampos coerentes entre sipaidAt só existe se status == PAID
CálculosTotais e arredondamentototal == subtotal - discount + tax com regra de casas decimais
EstadosTransições válidasCREATED -> PAID permitido; SHIPPED -> CREATED proibido
Efeitos colateraisPersistência e impactosapó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.: createdAt entre t0 e t1).
  • 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.

Agora responda o exercício sobre o conteúdo:

Em testes de API, por que é recomendado combinar validações estruturais (schema) com validações semânticas (regras de negócio)?

Você acertou! Parabéns, agora siga para a próxima página

Você errou! Tente novamente.

O schema valida a estrutura (campos, tipos e formatos), mas pode aprovar respostas “parecendo” corretas. As validações semânticas checam a “verdade” do domínio (totais, transições, integridade e persistência), evitando falsos positivos.

Próximo capitúlo

Idempotência, concorrência e consistência em testes de API

Arrow Right Icon
Capa do Ebook gratuito Testes de API: Conceitos Essenciais (REST, HTTP, Status Codes e Contratos)
60%

Testes de API: Conceitos Essenciais (REST, HTTP, Status Codes e Contratos)

Novo curso

15 páginas

Baixe o app para ganhar Certificação grátis e ouvir os cursos em background, mesmo com a tela desligada.