Códigos de status HTTP em testes de API: critérios de sucesso e falha

Capítulo 6

Tempo estimado de leitura: 10 minutos

+ Exercício

O que códigos de status HTTP significam em testes de API

Em testes de API, o código de status é o primeiro indicador objetivo de sucesso ou falha de uma operação. Ele deve ser coerente com: (1) a ação executada no servidor, (2) o estado do recurso após a requisição, (3) a presença/ausência de corpo na resposta e (4) headers relevantes (por exemplo, Location, Content-Type, Retry-After).

Um teste bem desenhado não valida apenas “retornou 200”, mas sim “retornou o status correto para este cenário, com corpo e headers compatíveis com esse status”.

Mapeando operações a códigos de sucesso esperados

200 OK

  • Use quando a operação foi bem-sucedida e há uma representação útil para retornar no corpo.
  • Comum em GET (retorna recurso), e também em PUT/PATCH quando a API retorna o recurso atualizado.
GET /users/123  -> 200 OK + JSON do usuário

201 Created

  • Use quando um novo recurso foi criado como resultado da requisição.
  • Boa prática: incluir Location apontando para a URL do novo recurso.
  • O corpo pode trazer a representação criada (frequente) ou metadados.
POST /users -> 201 Created + Location: /users/987

202 Accepted (processamento assíncrono)

  • Use quando a requisição foi aceita para processamento, mas ainda não foi concluída.
  • Indica que o resultado final estará disponível depois (ex.: processamento pesado, integração externa, fila).
  • Recomendado retornar um “recurso de operação” (job) para consulta de status.
POST /reports -> 202 Accepted + body: { "jobId": "abc" } + Location: /jobs/abc

204 No Content

  • Use quando a operação foi bem-sucedida e não há corpo para retornar.
  • Comum em DELETE e em PUT/PATCH quando a API não retorna representação.
  • Regra prática: se for 204, o corpo deve estar vazio.
DELETE /users/123 -> 204 No Content (sem body)

Erros comuns e quando esperar cada um

400 Bad Request

  • Requisição malformada ou inválida em nível sintático/estrutural.
  • Ex.: JSON inválido, tipo incorreto evidente, parâmetro obrigatório ausente.

401 Unauthorized

  • Autenticação ausente ou inválida.
  • Boa prática: incluir WWW-Authenticate quando aplicável.

403 Forbidden

  • Autenticado, mas sem permissão para a ação/recurso.
  • Ex.: usuário comum tentando acessar endpoint de admin.

404 Not Found

  • Recurso não encontrado (ou endpoint inexistente).
  • Em APIs, também pode ser usado para não revelar existência de recursos (evitar enumeração), mas isso deve ser uma decisão explícita de segurança.

409 Conflict

  • Conflito com o estado atual do recurso.
  • Ex.: violação de unicidade (email já cadastrado), conflito de versão (ETag), tentativa de transição de estado inválida.

415 Unsupported Media Type

  • Servidor não suporta o Content-Type enviado.
  • Ex.: enviar text/plain quando a API exige application/json.

422 Unprocessable Entity

  • Requisição bem formada (sintaticamente), mas semanticamente inválida.
  • Ex.: validações de domínio (campo fora do intervalo, formato válido mas regra de negócio violada).

429 Too Many Requests

  • Limite de requisições excedido (rate limit).
  • Boa prática: incluir Retry-After e/ou headers de limite (ex.: X-RateLimit-Remaining).

500 Internal Server Error

  • Erro inesperado no servidor.
  • Em testes, é sinal de bug, instabilidade ou contrato não atendido (a API deveria responder com erro 4xx tratável).

503 Service Unavailable

  • Serviço indisponível temporariamente (manutenção, sobrecarga, dependência fora).
  • Boa prática: incluir Retry-After quando possível.

Diferenças importantes que caem em testes

200 vs 204

200 OK implica que pode haver corpo (e normalmente há, quando faz sentido). 204 No Content implica que não deve haver corpo. Em testes, isso muda as asserções:

  • Se a API promete retornar a entidade atualizada após PUT/PATCH, espere 200 e valide o JSON.
  • Se a API promete apenas confirmar a operação sem payload, espere 204 e valide que o body está vazio.
// Exemplo de asserções conceituais (pseudo) if status == 204: assert response.body is empty assert no Content-Type required (pode existir, mas não deve induzir body) if status == 200: assert Content-Type == application/json assert response.body has expected schema

404 vs 410

404 Not Found indica que o recurso não existe (ou não é acessível/visível). 410 Gone indica que o recurso existia, mas foi removido permanentemente e não voltará.

  • Use 404 para IDs inexistentes ou quando não há garantia de que existiu.
  • Use 410 quando há conceito de “recurso descontinuado” (ex.: conteúdo removido por política) e isso é relevante para clientes.
GET /posts/123 (nunca existiu) -> 404 GET /posts/123 (existia e foi removido permanentemente) -> 410

409 vs 422

Ambos indicam falhas “compreensíveis” pelo servidor, mas com naturezas diferentes:

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

  • 409 Conflict: o problema é o estado do recurso ou do sistema em relação à operação (concorrência, unicidade, transição de estado, versão).
  • 422 Unprocessable Entity: o problema é a semântica dos dados enviados (validações de domínio), mesmo que não haja conflito com estado concorrente.
// Exemplos típicos POST /users { "email": "a@a.com" } (email já existe) -> 409 POST /users { "email": "invalido" } (formato inválido) -> 422

Quando usar 202 para processamento assíncrono

Use 202 Accepted quando a requisição inicia um processo que não termina no tempo da resposta. Em testes, isso exige validar o “contrato de acompanhamento”:

  • Resposta imediata: 202 com identificador da operação (jobId) e/ou Location para consultar.
  • Endpoint de status: GET /jobs/{id} retorna estado (ex.: pending, running, done, failed).
  • Resultado final: pode ser obtido em outro endpoint ou no próprio job quando concluído.
POST /exports -> 202 Accepted { "jobId": "j1" } GET /jobs/j1 -> 200 { "status": "running" } ... GET /jobs/j1 -> 200 { "status": "done", "resultUrl": "/exports/j1/file" }

Matriz de decisão para selecionar o status correto

CenárioStatus recomendadoO que validar em testes
Leitura de recurso existente200Content-Type correto, schema do body, campos esperados
Recurso não existe (sem evidência de remoção permanente)404Body de erro padronizado (se houver), mensagem/código interno
Recurso existia e foi removido permanentemente (conceito explícito)410Body de erro coerente, documentação/contrato consistente
Criação síncrona de recurso201Location presente, body consistente com criado, id gerado
Processo iniciado mas não concluído (assíncrono)202Retorno de jobId/Location, endpoint de acompanhamento funciona
Atualização bem-sucedida retornando representação200Body reflete atualização, headers coerentes
Atualização/remoção bem-sucedida sem retorno de body204Body vazio, ausência de payload
JSON inválido / payload malformado400Erro aponta campo/causa (quando aplicável), não retornar 500
Sem credenciais ou credenciais inválidas401WWW-Authenticate (quando aplicável), não retornar 403
Sem permissão (autenticado)403Mensagem de autorização, não retornar 401
Content-Type não suportado415Erro indica media type aceito, não retornar 400 genérico
Dados semanticamente inválidos (validação de domínio)422Lista de erros por campo/regra, não retornar 400 genérico
Conflito com estado (unicidade, versão, transição)409Erro indica conflito e como resolver (ex.: recurso existente, versão)
Rate limit excedido429Retry-After e headers de limite, comportamento consistente
Falha inesperada no servidor500Não vazar detalhes sensíveis, correlação/trace id (se existir)
Indisponibilidade temporária503Retry-After quando possível, mensagem de indisponibilidade

Passo a passo prático: como testar status + body + headers de forma coerente

Passo 1 — Defina o “resultado esperado” por cenário

Para cada endpoint, descreva cenários mínimos: sucesso, entrada inválida, não encontrado, conflito, autenticação/autorização, e limites (quando aplicável). Para cada cenário, defina o trio: status, headers e body.

  • Ex.: “Criar usuário com payload válido” → 201 + Location + body com id.
  • Ex.: “Criar usuário com email já existente” → 409 + body com código de erro “duplicate_email”.

Passo 2 — Valide o status e a presença/ausência de corpo

Regras úteis em testes:

  • 204: body vazio (e não tentar parsear JSON).
  • 201: geralmente body não vazio e Location presente.
  • 4xx/5xx: body de erro padronizado (se o contrato define), com campos mínimos (ex.: errorCode, message, details).

Passo 3 — Valide headers que “fazem parte do contrato”

Alguns headers são críticos para coerência do status:

  • 201: Location apontando para o recurso criado.
  • 415: resposta deve indicar erro de media type; muitas APIs também retornam quais tipos aceita.
  • 429 e 503: Retry-After quando aplicável.
  • Respostas com JSON: Content-Type: application/json (ou variante com charset).

Passo 4 — Valide o schema do body (quando houver)

Para respostas de sucesso, valide campos obrigatórios e tipos. Para erros, valide um schema de erro consistente. Exemplo de schema de erro comum:

{ "errorCode": "string", "message": "string", "details": [ { "field": "string", "reason": "string" } ] }

Passo 5 — Para 202, implemente teste em duas fases (aceite + conclusão)

  • Fase A: dispara a operação e valida 202 + identificador.
  • Fase B: consulta o job até concluir (com timeout) e valida o resultado final (ex.: 200 no job com status=done, e então baixa/consulta o recurso final).
// Pseudofluxo POST /reports -> assert 202, capture jobId repeat GET /jobs/{jobId} until status in [done, failed] or timeout if done: assert result fields if failed: assert error fields

Conjunto de casos de teste: status + corpo + headers coerentes

Abaixo, um conjunto de testes exemplo (adaptável) para um recurso /users. A ideia é cobrir sucesso e erros mais comuns, sempre validando o trio status/body/headers.

1) GET existente retorna 200 com JSON

  • Requisição: GET /users/123
  • Esperado: 200
  • Headers: Content-Type indica JSON
  • Body: contém id=123, campos obrigatórios, tipos corretos

2) GET inexistente retorna 404 (ou 410 se removido permanentemente)

  • Requisição: GET /users/nao-existe
  • Esperado: 404
  • Body: erro padronizado com errorCode (ex.: not_found)

3) POST cria recurso retorna 201 + Location

  • Requisição: POST /users com payload válido
  • Esperado: 201
  • Headers: Location presente e aponta para /users/{id}
  • Body: contém id e dados persistidos

4) POST com JSON malformado retorna 400

  • Requisição: POST /users com body inválido (ex.: vírgula extra)
  • Esperado: 400
  • Body: mensagem indicando erro de parsing/estrutura (sem 500)

5) POST com Content-Type incorreto retorna 415

  • Requisição: POST /users com Content-Type: text/plain
  • Esperado: 415
  • Body: erro indicando media type não suportado

6) POST com validação de domínio falha retorna 422

  • Requisição: POST /users com email em formato inválido ou age fora do intervalo
  • Esperado: 422
  • Body: lista de erros por campo/regra (ex.: details[0].field=email)

7) POST com unicidade violada retorna 409

  • Requisição: POST /users com email já existente
  • Esperado: 409
  • Body: erro de conflito (ex.: duplicate_resource)

8) PUT/PATCH retorna 200 com recurso atualizado (ou 204 sem body)

  • Requisição: PATCH /users/123 alterando um campo
  • Esperado: 200 com body atualizado ou 204 sem body (conforme contrato)
  • Teste: se 200, validar schema e valores; se 204, validar body vazio

9) DELETE retorna 204 e body vazio

  • Requisição: DELETE /users/123
  • Esperado: 204
  • Body: vazio
  • Teste adicional: GET /users/123 após delete retorna 404 (ou 410 se aplicável)

10) Sem autenticação retorna 401; sem permissão retorna 403

  • Requisição: chamada sem credenciais
  • Esperado: 401 (e header WWW-Authenticate quando aplicável)
  • Requisição: chamada com credenciais válidas porém sem permissão
  • Esperado: 403

11) Rate limit retorna 429 com Retry-After

  • Requisição: disparar N requisições acima do limite
  • Esperado: 429
  • Headers: Retry-After presente (segundos ou data HTTP)
  • Body: erro indicando limite excedido

12) Indisponibilidade temporária retorna 503

  • Requisição: simular dependência fora/serviço em manutenção (ambiente de teste)
  • Esperado: 503
  • Headers: Retry-After quando aplicável
  • Body: mensagem de indisponibilidade sem detalhes sensíveis

13) Falha inesperada não deve mascarar erros tratáveis (evitar 500)

  • Requisição: enviar dados que deveriam gerar 400/422
  • Esperado: 400 ou 422, não 500
  • Objetivo: garantir tratamento de erro e previsibilidade do contrato

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

Em um teste de API, ao verificar uma resposta com status 204 No Content, qual validação é a mais adequada para garantir coerência entre status e payload?

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

Você errou! Tente novamente.

O status 204 No Content indica sucesso sem retorno de payload. Em testes, a asserção principal é que o body esteja vazio e que não haja tentativa de interpretar JSON, mantendo coerência entre status e resposta.

Próximo capitúlo

Padrões de erro e mensagens em testes de API: consistência e rastreabilidade

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

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.