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 emPUT/PATCHquando a API retorna o recurso atualizado.
GET /users/123 -> 200 OK + JSON do usuário201 Created
- Use quando um novo recurso foi criado como resultado da requisição.
- Boa prática: incluir
Locationapontando para a URL do novo recurso. - O corpo pode trazer a representação criada (frequente) ou metadados.
POST /users -> 201 Created + Location: /users/987202 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/abc204 No Content
- Use quando a operação foi bem-sucedida e não há corpo para retornar.
- Comum em
DELETEe emPUT/PATCHquando 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-Authenticatequando 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-Typeenviado. - Ex.: enviar
text/plainquando a API exigeapplication/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-Aftere/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-Afterquando 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, espere200e valide o JSON. - Se a API promete apenas confirmar a operação sem payload, espere
204e 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 schema404 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
404para IDs inexistentes ou quando não há garantia de que existiu. - Use
410quando 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) -> 410409 vs 422
Ambos indicam falhas “compreensíveis” pelo servidor, mas com naturezas diferentes:
- Ouça o áudio com a tela desligada
- Ganhe Certificado após a conclusão
- + de 5000 cursos para você explorar!
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) -> 422Quando 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:
202com identificador da operação (jobId) e/ouLocationpara 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ário | Status recomendado | O que validar em testes |
|---|---|---|
| Leitura de recurso existente | 200 | Content-Type correto, schema do body, campos esperados |
| Recurso não existe (sem evidência de remoção permanente) | 404 | Body de erro padronizado (se houver), mensagem/código interno |
| Recurso existia e foi removido permanentemente (conceito explícito) | 410 | Body de erro coerente, documentação/contrato consistente |
| Criação síncrona de recurso | 201 | Location presente, body consistente com criado, id gerado |
| Processo iniciado mas não concluído (assíncrono) | 202 | Retorno de jobId/Location, endpoint de acompanhamento funciona |
| Atualização bem-sucedida retornando representação | 200 | Body reflete atualização, headers coerentes |
| Atualização/remoção bem-sucedida sem retorno de body | 204 | Body vazio, ausência de payload |
| JSON inválido / payload malformado | 400 | Erro aponta campo/causa (quando aplicável), não retornar 500 |
| Sem credenciais ou credenciais inválidas | 401 | WWW-Authenticate (quando aplicável), não retornar 403 |
| Sem permissão (autenticado) | 403 | Mensagem de autorização, não retornar 401 |
Content-Type não suportado | 415 | Erro indica media type aceito, não retornar 400 genérico |
| Dados semanticamente inválidos (validação de domínio) | 422 | Lista de erros por campo/regra, não retornar 400 genérico |
| Conflito com estado (unicidade, versão, transição) | 409 | Erro indica conflito e como resolver (ex.: recurso existente, versão) |
| Rate limit excedido | 429 | Retry-After e headers de limite, comportamento consistente |
| Falha inesperada no servidor | 500 | Não vazar detalhes sensíveis, correlação/trace id (se existir) |
| Indisponibilidade temporária | 503 | Retry-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 comid. - 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 eLocationpresente.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:Locationapontando para o recurso criado.415: resposta deve indicar erro de media type; muitas APIs também retornam quais tipos aceita.429e503:Retry-Afterquando 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.:
200no job comstatus=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 fieldsConjunto 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-Typeindica 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 /userscom payload válido - Esperado:
201 - Headers:
Locationpresente e aponta para/users/{id} - Body: contém
ide dados persistidos
4) POST com JSON malformado retorna 400
- Requisição:
POST /userscom 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 /userscomContent-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 /userscomemailem formato inválido ouagefora 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 /userscom 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/123alterando um campo - Esperado:
200com body atualizado ou204sem body (conforme contrato) - Teste: se
200, validar schema e valores; se204, validar body vazio
9) DELETE retorna 204 e body vazio
- Requisição:
DELETE /users/123 - Esperado:
204 - Body: vazio
- Teste adicional:
GET /users/123após delete retorna404(ou410se aplicável)
10) Sem autenticação retorna 401; sem permissão retorna 403
- Requisição: chamada sem credenciais
- Esperado:
401(e headerWWW-Authenticatequando 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-Afterpresente (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-Afterquando 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:
400ou422, não500 - Objetivo: garantir tratamento de erro e previsibilidade do contrato