Como testar APIs de forma automatizada sem suíte inútil
Teste a borda real da sua API, não os detalhes internos: endpoint, contrato e fumaça em produção, o que asseverar, fixtures, mock server e CI.
Toda API que sobrevive ao primeiro deploy acaba acumulando uma suíte de testes confusa: dezenas de unitários que mockam o controller inteiro, validam que um método foi chamado e não pegam nenhum bug de verdade. Enquanto isso, o cliente que consome a API recebe um 500 porque alguém mudou o nome de um campo no JSON e nenhum teste reclamou. O problema é quase sempre o mesmo: testamos os detalhes internos em vez da borda que o cliente realmente vê. Testar API de forma automatizada não é cobrir cada linha de código — é provar que o contrato externo continua de pé.
Este artigo cobre os níveis de teste que importam (endpoint/integração, contrato e fumaça em produção), o que de fato vale a pena asseverar, como lidar com dados de teste e dependências externas, e como rodar tudo isso no CI sem que ele vire o gargalo da equipe.
Os três níveis que importam
O teste de endpoint, ou de integração, é o que mais paga a conta. Ele sobe a aplicação de verdade — ou pelo menos a camada HTTP completa —, dispara um request real contra o endpoint e valida o que voltou: status, body, headers e os side effects no banco. É o teste que mais se aproxima do que o cliente experimenta, e por isso pega bugs que um unitário de controller jamais alcança. Um teste que sobe a app e bate no endpoint encontra mais bug real que dez unitários que mockam o request e a resposta.
import request from 'supertest';
import { app } from '../app.js';
test('POST /users cria usuário e retorna 201', async () => {
const res = await request(app)
.post('/users')
.send({ name: 'Ana', email: 'ana@example.com' });
expect(res.status).toBe(201);
expect(res.body).toMatchObject({ id: expect.any(String), name: 'Ana' });
expect(res.headers['content-type']).toMatch(/application\/json/);
const row = await db.query('SELECT * FROM users WHERE email = $1', ['ana@example.com']);
expect(row.rowCount).toBe(1);
});
Repare que a asserção não para no status. Ela verifica o shape do JSON, o header de content-type e o side effect no banco. Esse é o nível em que a maior parte da sua confiança deve morar.
O teste de contrato resolve um problema diferente: garantir que o request e a response batem com o schema acordado entre dois serviços. Quando um produtor e um consumidor evoluem em repositórios separados, o contrato é a única coisa que os mantém compatíveis. Ferramentas como Pact ou validação de schema (OpenAPI, JSON Schema) verificam que o produtor não removeu um campo que o consumidor espera, nem mudou um tipo. É barato, roda rápido e evita a classe de quebra mais cara: a integração que só falha em produção.
O teste de fumaça em produção é a última linha. Depois do deploy, um punhado de requests contra os endpoints críticos confirma que a aplicação subiu, autentica, conecta no banco e responde. Não é exaustivo de propósito — é um sinal de vida. Se o health check e o login passam, o deploy está de pé; se falham, o rollback dispara antes do usuário perceber.
O que asseverar (e não é só o caminho feliz)
A armadilha mais comum é testar só o sucesso. Um 200 com o body certo é metade do trabalho. A outra metade — onde os bugs caros se escondem — são os erros e os casos de borda.
Vale asseverar, no mínimo: o status correto para cada cenário (201 na criação, 404 em recurso inexistente, 422 em payload inválido, 401 sem autenticação); o shape do JSON, não apenas a presença de campos, mas tipos e estrutura; os headers relevantes (Content-Type, Cache-Control, headers de paginação); e o corpo das respostas de erro, que precisa ser tão consistente quanto o de sucesso. Um cliente que recebe {"error": "..."} num caso e uma stack trace HTML em outro vai quebrar.
Casos de borda merecem teste explícito: lista vazia, paginação no último item, campos opcionais ausentes, payload no limite de tamanho, caracteres especiais. São esses que o caminho feliz nunca exercita e que invariavelmente aparecem em produção.
Dados de teste e fixtures
Teste de API precisa de dados previsíveis. Fixtures dão isso: um conjunto conhecido de registros que você semeia antes do teste e limpa depois. A regra de ouro é o isolamento — cada teste deve montar seu próprio estado e não depender da ordem de execução nem de resquícios de outro teste. Transações que dão rollback ao final, ou um banco recriado por suíte, mantêm tudo determinístico.
Para as respostas que vêm de fora — um gateway de pagamento, uma API de terceiros —, você precisa de payloads de exemplo realistas sem chamar o serviço real. Gerar um JSON de mock estruturado entrega fixtures e respostas falsas para os testes sem depender de um serviço externo, com o shape e os tipos que o seu código espera consumir.
Isolar dependências externas com mock server
Aqui mora a opinião mais importante e a regra que mais se viola: não mocke o que você está justamente tentando testar. Se o objetivo é testar o seu endpoint, o seu endpoint roda de verdade — você não substitui o controller por um stub. O que você isola são as dependências externas que estão fora do escopo do teste e que você não controla: a API de terceiros, o gateway de pagamento, o serviço de e-mail.
Um mock server intercepta essas chamadas externas e devolve respostas determinísticas. Assim você consegue exercitar o cenário em que o gateway retorna 402, ou demora demais, sem depender da disponibilidade do serviço real nem gastar dinheiro de verdade. A distinção entre o que é mock, stub e fake importa mais do que parece na hora de decidir o que substituir; vale entender as diferenças entre mocks, stubs e fakes antes de encher a suíte de dublês que escondem bug em vez de revelá-lo.
A linha divisória é simples: mocke a borda que está fora do seu controle, nunca a borda que você está testando.
Rodar no CI
Um teste de API só protege a equipe se roda automaticamente em cada push. No CI, suba os serviços de que a suíte depende — banco e mock server — como containers, espere pelo health check, rode a suíte e derrube tudo. Postgres e Redis como service containers, ou um docker-compose dedicado de teste, resolvem o ambiente sem que cada dev precise configurar a máquina.
Mantenha os testes rápidos e paralelizáveis: bancos isolados por worker, sem sleep fixo esperando coisa subir. Um pipeline que demora dez minutos é um pipeline que a equipe vai aprender a ignorar.
Sobre ferramentas, há um cardápio largo e nenhuma é obrigatória. Postman com newman roda coleções no CI e é ótimo para quem já documenta a API ali. supertest brilha em projetos Node por subir o app em memória. pytest com requests é o padrão pragmático em Python. Escolha a que conversa com a sua stack e resista à tentação de adotar três ao mesmo tempo — a consistência da suíte vale mais que a ferramenta da moda.
Perguntas frequentes
Qual a diferença entre teste de integração e teste de contrato de API?
O teste de integração sobe a sua aplicação e valida o comportamento real do endpoint — status, body, side effects no banco. O teste de contrato verifica apenas que o formato do request e da response bate com o schema acordado entre dois serviços, sem necessariamente executar a lógica completa. Integração prova que a sua API funciona; contrato prova que ela continua compatível com quem a consome.
Preciso mockar o banco de dados nos testes de API?
Em geral, não. Se o objetivo é testar o endpoint de ponta a ponta, o banco faz parte do que você quer validar — inclusive os side effects. Use um banco real de teste, isolado por transação ou recriado por suíte. Mocke o banco apenas em testes unitários focados em lógica que não toca persistência.
Com que frequência devo rodar testes de fumaça em produção?
A cada deploy, no mínimo, como porta final antes de liberar o tráfego. Muitas equipes também os rodam em intervalos curtos como monitoramento contínuo, confirmando que os endpoints críticos seguem de pé entre deploys. O ponto é que sejam rápidos e cubram só o essencial.
Quantos testes de endpoint são suficientes?
Não há número mágico. Cubra cada endpoint nos cenários que importam: o caminho feliz, os erros previstos e os casos de borda que o seu domínio expõe. É melhor ter um teste sólido por cenário real do que vinte que repetem variações do mesmo sucesso.
Onde colocar a sua confiança
Teste a borda real da sua API — o contrato que o cliente vê —, não os detalhes internos que vão mudar na próxima refatoração. Priorize os testes de endpoint que sobem a app e batem no HTTP de verdade, complete com testes de contrato entre serviços, e proteja o deploy com fumaça em produção. Asseverere o erro e a borda com o mesmo rigor do caminho feliz, isole só as dependências que estão fora do seu controle, e mantenha tudo rodando no CI. A suíte que sobra é pequena, rápida e — o que importa — pega bug de verdade.
- 01 Portfólio de desenvolvedor: o que colocar (e o que cortar) Recrutador olha seu portfólio por vinte segundos. Um projeto terminado e no ar vence dez clones de tutorial. O que incluir, o que cortar e por que o README é metade da impressão.
- 02 Nubank Croma: o que vem no plano, quanto custa e pra quem realmente compensa O Nubank lançou o Croma, um plano de média renda entre o cartão gratuito e o Ultravioleta. Veja o que está incluído, o cashback real, os R$ 39 de mensalidade (e como zerar) e faça a conta antes de aderir.