Escrevendo os seus primeiros casos de teste

Resultados esperados, testes negativos, valores-limite e classes de equivalência.

Até agora você enviou requisições e olhou o que voltava. O teste começa quando você decide, antes de enviar, o que deveria voltar, e depois compara. Essa decisão por escrito é um caso de teste. Esta lição trata de escrevê-los e de escolher quais escrever, porque não dá para enviar todas as requisições possíveis.

O que um caso de teste contém

As equipes usam modelos diferentes, mas quase todos têm as mesmas partes:

ParteO que vai nelaExemplo
IDUma referência curta para as pessoas falarem delePROD-GET-03
TítuloO que está sendo conferido, numa linhaID de produto desconhecido devolve 404
Pré-condiçõesO que precisa ser verdade antes de começarNão existe produto com ID 9999
RequisiçãoMétodo, URL, cabeçalhos e corpoGET /api/Products/9999
Resultado esperadoStatus, cabeçalhos importantes, corpo404, corpo JSON cuja mensagem cita o ID 9999
Resultado realPreenchido quando você executaComo esperado

O resultado esperado é a parte que as pessoas escrevem mal. "Devolve um erro" não é um resultado esperado, porque quase qualquer coisa passa. Seja específico sobre o código de status, os cabeçalhos que importam (geralmente Content-Type, às vezes cabeçalhos de cache ou de localização) e o corpo: quais campos precisam estar lá, com quais tipos e valores. Se você não consegue dizer qual é a resposta certa, encontrou uma pergunta para o desenvolvedor ou para o dono do produto, e vale a pena fazê-la antes de testar qualquer coisa.

De onde vem o resultado esperado? Da documentação, do ticket que descreveu a funcionalidade, do jeito como endpoints parecidos da mesma API se comportam e, às vezes, do bom senso (um preço não deveria ser negativo). Quando essas fontes discordam, anote qual você seguiu.

Testes positivos e negativos

Um teste positivo envia uma requisição válida e confere se funciona: o produto 1 existe, então GET /api/Products/1 o devolve. Um teste negativo envia algo que a API deveria recusar e confere se ela recusa direito: o status certo, uma mensagem útil e nenhum efeito colateral.

Quem está começando escreve principalmente testes positivos, porque segue a descrição da funcionalidade. Os desenvolvedores geralmente já experimentaram o caminho positivo antes de entregar o trabalho. Os negativos são onde você encontra coisas. Como regra geral, espere escrever mais testes negativos do que positivos para qualquer endpoint que recebe entrada.

Classes de equivalência

O endpoint de listagem GET /api/Products aceita um parâmetro limit. A referência da API diz que é o tamanho da página, de 1 a 100. Existem infinitos valores que você poderia enviar. Partição em classes de equivalência significa agrupar as entradas que o servidor deveria tratar do mesmo jeito e testar um valor de cada grupo em vez de todos:

ClasseValor de exemploEsperado
Válido, dentro da faixalimit=10Dez produtos
Abaixo da faixalimit=-5Recusado, ou ajustado para o mínimo
Acima da faixalimit=500Recusado, ou ajustado para 100
Não é númerolimit=abcRecusado com 400
Não é inteirolimit=2.5Recusado com 400
Vaziolimit=Tratado como ausente
Ausentesem limitTodos os produtos, como documentado

Repare nas entradas "recusado, ou ajustado". A documentação não diz o que acontece fora da faixa, então há dois comportamentos razoáveis. Os dois se defendem. O que você testa é se a API faz um deles, de forma consistente, e não quebra nem devolve algo sem sentido.

Valores-limite

Os bugs se acumulam nas bordas das faixas, porque é ali que os desenvolvedores escrevem < quando queriam dizer <=. Análise de valores-limite significa testar a borda e um passo de cada lado. Para uma faixa de 1 a 100, isso dá 0, 1, 2, 99, 100 e 101. Veja o que esta API realmente faz com esses valores e com as outras classes. O cabeçalho X-Per-Page informa o tamanho de página que o servidor usou.

RequisiçãoStatusItens devolvidosX-Per-Page
?limit=020011
?limit=120011
?limit=-120011
?limit=10020020 (todos)100
?limit=10120020 (todos)100
?limit=abc2002020
?limit=2.52002020
?limit=2002020
GET/api/Products?limit=0

Lendo os resultados: valores abaixo de 1 são ajustados para 1, valores acima de 100 são ajustados para 100, e qualquer coisa que não seja inteiro é ignorada como se limit não existisse. Nada disso está escrito, mas é consistente, então provavelmente é proposital. Ainda assim, duas coisas merecem uma nota no seu relatório de testes. limit=0 devolver um item surpreende; um cliente que pede zero itens (para ler o total sem custo, por exemplo) recebe um produto que não queria. E limit=abc ser ignorado em silêncio significa que um erro de digitação num cliente passa batido, quando um 400 teria pegado.

Há também uma lição sobre dados de teste aqui. A API tem só 20 produtos, então limit=100 e limit=101 devolvem o mesmo corpo. Pelo corpo não dava para saber que 101 foi ajustado. O cabeçalho mostrou. Quando um limite é maior que os seus dados, encontre outro sinal como este ou crie registros suficientes para chegar ao limite.

Limites também se escondem nos IDs

A referência lista o id de /api/Products/{id} como int32, um inteiro de 32 bits cujo maior valor é 2.147.483.647. É um limite que vale testar:

RequisiçãoStatusCorpo
/api/Products/20200O produto 20, o último
/api/Products/21404Mensagem de erro em JSON
/api/Products/0404Mensagem de erro em JSON
/api/Products/-1404Mensagem de erro em JSON
/api/Products/2147483647404Mensagem de erro em JSON
/api/Products/2147483648404Vazio
/api/Products/abc404Vazio
/api/Products/1.5404Vazio
/api/Products/01200O produto 1

Duas coisas chamam atenção. Tudo o que não é um int32 válido recebe um 404 sem corpo nenhum, enquanto IDs válidos mas inexistentes recebem um erro em JSON. E 01 é aceito como 1. O segundo é inofensivo aqui, mas numa API que guarda respostas em cache por URL, /1 e /01 seriam guardados separadamente. Nenhum dos dois resultados é obviamente errado. Os dois valem a pena conhecer.

Um conjunto de casos de teste para um endpoint

Juntando tudo, um primeiro conjunto razoável de casos de teste para GET /api/Products/{id} fica assim. Não é tudo o que você poderia testar, mas cobre cada classe uma vez e cada limite.

IDRequisiçãoResultado esperado
PROD-GET-01GET /api/Products/1200, JSON, id é 1, title é uma string não vazia, price é um número acima de 0
PROD-GET-02GET /api/Products/20200, o maior ID inicial
PROD-GET-03GET /api/Products/21404, corpo de erro em JSON citando o ID 21
PROD-GET-04GET /api/Products/0404, corpo de erro em JSON
PROD-GET-05GET /api/Products/-1404, corpo de erro em JSON
PROD-GET-06GET /api/Products/abc400 ou 404, com corpo de erro em JSON
PROD-GET-07GET /api/Products/2147483648400 ou 404, com corpo de erro em JSON
PROD-GET-08HEAD /api/Products/1Mesmo status e cabeçalhos do GET, sem corpo
PROD-GET-09GET /api/Products/1, duas vezesCorpos idênticos nas duas vezes

PROD-GET-06 e 07 falham contra a API atual, porque o corpo vem vazio. PROD-GET-08 também falha: HEAD /api/Products/1 devolve 405 Method Not Allowed, enquanto recursos mais novos, como /api/Books/{id}entre para ver, aceitam HEAD. Se o suporte a HEAD é esperado aqui é uma pergunta para quem é dono da API. Tudo bem. Um caso de teste registra o que deveria acontecer; um caso de teste que falha é como você descobre que não acontece. PROD-GET-02 tem um ponto fraco: se alguém apagar o produto 20 ou adicionar o 21, ele deixa de significar o que diz. A próxima lição mostra como criar os seus próprios dados de teste para que os testes não dependam de registros que outra pessoa controla.

Escrevendo um relatório de bug

Quando um caso de teste falha, você o documenta. Um bom relatório de bug permite que um desenvolvedor reproduza o problema em menos de um minuto sem perguntar nada a você. Usando como exemplo os 404 vazios acima, compare primeiro as duas respostas:

GET /api/Products/9999
HTTP/1.1 404 Not Found
Content-Type: application/json; charset=utf-8

{"status":404,"error":"Not Found","message":"Product with ID 9999 does not exist."}
GET /api/Products/abc
HTTP/1.1 404 Not Found
Content-Length: 0

Um relatório poderia dizer (aqui em inglês, como muitas equipes escrevem):

Relatório de bug
Title: GET /api/Products/{id} returns an empty 404 for non-numeric
       and out-of-range IDs, instead of the JSON error body

Environment: https://api.testingapis.com, 30 Sep 2026

Steps:
  curl -i https://api.testingapis.com/api/Products/9999
  curl -i https://api.testingapis.com/api/Products/abc
  curl -i https://api.testingapis.com/api/Products/2147483648

Expected:
  All three return 404 with Content-Type: application/json and a body
  like {"status":404,"error":"Not Found","message":"..."}, as documented
  under "Responses and errors" in the API reference.

Actual:
  9999        404, JSON body with a message
  abc         404, Content-Length: 0, no Content-Type, no body
  2147483648  404, empty body (one above the int32 maximum; 2147483647
              returns the JSON error)

Impact:
  Clients that parse every error body as JSON fail on the empty
  responses. The empty 404 also looks like "this URL does not exist"
  rather than "this product does not exist".

Notes:
  Probably the route only matches int32 values, so other input never
  reaches the controller. Could be intended; if so, the docs should say
  which errors have no body.

O que torna este relatório útil:

  • O título diz o que está errado e onde, então fica claro numa lista de cinquenta tickets.
  • Os passos são comandos que dá para colar. Nada de "chame o endpoint de produtos com um ID ruim".
  • O comportamento esperado aponta para uma fonte (a documentação), então não é só a sua preferência.
  • Mostra o padrão em vez de um único exemplo. Três requisições, com uma que funciona para comparar, dizem mais do que uma requisição que falha.
  • Informa o impacto num cliente real. Os desenvolvedores priorizam pelo impacto, e "inconsistente" sozinho raramente é corrigido.
  • Admite a incerteza. O comportamento pode ser proposital, e o relatório diz o que resolveria a questão de qualquer jeito (corrigir o código ou corrigir a documentação).

Exercício

Escolha GET /api/Books/{id}entre para ver ou GET /api/Countries/code/{code}entre para ver. Procure o endpoint na referência da API e então:

  • Liste as classes de equivalência do parâmetro de caminho.
  • Escolha os valores-limite. Para códigos de país, pense no tamanho e em maiúsculas e minúsculas.
  • Escreva pelo menos oito casos de teste no formato de tabela acima, com resultados esperados específicos.
  • Rode-os com o curl e preencha os resultados reais.

Se algo se comportar de um jeito diferente do que você esperava, escreva um relatório de bug com a estrutura acima, incluindo aquilo de que você não tem certeza.

Pratique e vá além