25 de set. de 2025·8 min de leitura

Testes de contrato para APIs: evite quebras em equipes rápidas

Testes de contrato para APIs ajudam a detectar mudanças breaking antes das releases web e mobile. Passos práticos, erros a evitar e um checklist rápido de pré‑envio.

Testes de contrato para APIs: evite quebras em equipes rápidas

Por que alterações incompatíveis em APIs continuam entrando nas releases

A maioria das equipes acaba com uma API servindo muitos clientes: um app web, um app iOS, um app Android e, às vezes, ferramentas internas também. Mesmo que todos concordem sobre os “mesmos” endpoints, cada cliente usa a API de formas ligeiramente diferentes. Uma tela pode esperar que um campo sempre exista, enquanto outra só o usa quando um filtro é aplicado.

O problema real aparece quando essas partes são entregues em horários diferentes. O backend pode ir ao ar várias vezes por dia, o web deploya rápido e as releases mobile andam mais devagar por causa de revisões e rollouts graduais. Essa diferença cria quebras-surpresa: a API é atualizada para o cliente mais recente, mas a build mobile de ontem ainda está em uso e agora recebe respostas que não consegue tratar.

Quando isso acontece, os sintomas raramente são sutis:

  • Uma tela que de repente fica vazia porque um campo foi renomeado ou movido
  • Quedas causadas por nulls inesperados ou objetos ausentes
  • Tickets de suporte “algo quebrou” com passos difíceis de reproduzir
  • Pico em logs de erro logo após um deploy do backend
  • Hotfixes que adicionam código defensivo em vez de resolver a causa raiz

Testes manuais e QA frequentemente não pegam esses problemas porque os casos de risco não estão no caminho feliz. Um testador pode verificar que “Criar pedido” funciona, mas não testar uma versão antiga do app, um perfil parcialmente preenchido, um papel de usuário raro, ou uma resposta onde uma lista está vazia. Adicione cache, feature flags e rollouts graduais, e você tem ainda mais combinações do que um plano de testes consegue cobrir.

Um exemplo típico: o backend substitui status: "approved" por status: { code: "approved" } para suportar localização. O web é atualizado no mesmo dia e parece tudo certo. Mas a release iOS atual ainda espera uma string, falha ao parsear a resposta, e usuários veem uma página em branco depois do login.

É por isso que existem testes de contrato para APIs: não para substituir QA, mas para detectar essas mudanças “funciona para meu cliente mais recente” antes de chegarem à produção.

O que é teste de contrato (e o que não é)

Teste de contrato é uma forma de um consumidor de API (um app web, mobile ou outro serviço) e um provedor de API (seu backend) concordarem sobre como vão se comunicar. Esse acordo é o contrato. Um teste de contrato verifica uma coisa simples: o provedor ainda se comporta como o consumidor depende, mesmo após mudanças?

Na prática, o teste de contrato fica entre testes unitários e end-to-end. Testes unitários são rápidos e locais, mas podem deixar passar incompatibilidades entre times porque testam código interno, não a fronteira compartilhada. Testes end-to-end exercitam fluxos reais entre muitos sistemas, mas são mais lentos, difíceis de manter e muitas vezes falham por razões não relacionadas a uma mudança de API (dados de teste, timing da UI, ambientes instáveis).

Um contrato não é um documento enorme. É uma descrição focada das requisições que um consumidor enviará e das respostas que ele precisa receber. Um bom contrato geralmente cobre:

  • Endpoints e métodos (por exemplo, POST /orders)
  • Campos obrigatórios e opcionais, incluindo tipos e regras básicas
  • Códigos de status e formato de resposta de erro (como é um 400 vs 404)
  • Headers e expectativas de autenticação (token presente, content type)
  • Defaults importantes e regras de compatibilidade (o que acontece se um campo faltar)

Aqui está um exemplo simples do tipo de quebra que um teste de contrato pega cedo: o backend renomeia total_price para totalPrice. Testes unitários ainda podem passar. Testes end-to-end podem não cobrir aquela tela ou falhar depois de forma confusa. Um teste de contrato falha imediatamente e aponta a incompatibilidade exata.

Vale deixar claro o que teste de contrato não faz. Não substitui testes de performance, segurança ou jornadas completas do usuário. Também não pega todo bug lógico. O que ele faz é reduzir o risco de release mais comum em equipes rápidas: uma mudança “pequena” na API que silenciosamente quebra um cliente.

Se seu backend é gerado ou muda frequentemente (por exemplo, ao regenerar APIs em uma plataforma como AppMaster), testes de contrato são uma rede de segurança prática porque verificam que as expectativas do cliente continuam válidas após cada mudança.

Escolha uma abordagem de contrato para times web e mobile

Quando web e mobile entregam com frequência, a parte difícil não é “testar a API”. É concordar sobre o que não pode mudar para cada cliente. É aí que o teste de contrato ajuda, mas você ainda precisa escolher como o contrato será governado.

Opção 1: Contratos orientados pelo consumidor (CDCs)

Com contratos orientados pelo consumidor, cada cliente (app web, iOS, Android, integração de parceiro) define o que precisa da API. O provedor então prova que consegue satisfazer essas expectativas.

Isso funciona bem quando os clientes avançam independentemente, porque o contrato reflete o uso real, não o que o time de backend acha que é usado. Também se ajusta à realidade multi-cliente: iOS pode depender de um campo que o web não usa, e o web pode se importar com ordenação ou paginação que o mobile ignora.

Um exemplo simples: o app mobile depende de price_cents ser um inteiro. O web só exibe o preço formatado, então não perceberia se o backend mudasse para string. Um CDC do mobile pegaria essa mudança antes da release.

Opção 2: Schemas mantidos pelo provedor

Com um schema mantido pelo provedor, o time de backend publica um contrato único (frequentemente um schema ou spec) e faz valer. Consumidores testam contra essa fonte única de verdade.

Isso é bom quando a API é pública ou compartilhada com muitos consumidores que você não controla, ou quando precisa de consistência rígida entre times. Também é mais simples para começar: um contrato, um lugar para revisar mudanças, um caminho de aprovação.

Uma maneira rápida de escolher:

  • Escolha CDCs quando clientes entregam com frequência e usam diferentes partes da API.
  • Escolha schemas do provedor quando precisar de um contrato “oficial” e estável para todos.
  • Use um híbrido quando puder: um schema do provedor como base, mais CDCs para endpoints de alto risco.

Se você está construindo com uma plataforma como AppMaster, a mesma ideia vale: trate apps web e nativos como consumidores separados. Mesmo quando compartilham backend, raramente dependem exatamente dos mesmos campos e regras.

O que colocar em um contrato de API (para que ele pegue quebras reais)

Um contrato só ajuda se refletir do que seus clientes web e mobile realmente dependem. Uma spec bonita que ninguém usa não vai pegar a mudança que quebra a produção.

Comece pelo uso real, não por suposições. Pegue as chamadas de cliente mais comuns (do código do app, logs do gateway ou uma lista curta das equipes) e transforme-as em casos de contrato: o caminho exato, método, headers, query params e o formato típico do corpo da requisição. Isso mantém o contrato pequeno, relevante e difícil de contestar.

Inclua respostas de sucesso e de erro. Times muitas vezes testam contrato só no caminho feliz e esquecem que clientes dependem de erros também: o código de status, o formato do corpo de erro e até códigos/mensagens de erro estáveis. Se um app mobile mostra uma mensagem específica “email já usado”, o contrato deve travar aquela resposta 409 para que ela não vire um 400 com corpo diferente.

Preste atenção extra às áreas que mais quebram:

  • Campos opcionais vs obrigatórios: remover um campo costuma ser mais seguro do que tornar um campo opcional em obrigatório.
  • Nulls: alguns clientes tratam null diferente de “ausente”. Decida o que permite e mantenha consistente.
  • Enums: adicionar um novo valor pode quebrar clientes antigos que assumem uma lista fechada.
  • Paginação: combinem parâmetros e campos de resposta (como cursor ou nextPageToken) e mantenham estáveis.
  • Formatos de data e número: tornem explícito (strings ISO, centavos em inteiro, etc.).

Como representar o contrato

Escolha um formato que equipes consigam ler e ferramentas consigam validar. Opções comuns são JSON Schema, contratos baseados em exemplos ou modelos tipados gerados a partir de um OpenAPI spec. Na prática, exemplos mais uma checagem de schema funcionam bem: exemplos mostram payloads reais, enquanto regras de schema pegam “campo renomeado” ou “tipo alterado”.

Uma regra simples: se uma mudança for forçar a atualização de um cliente, ela deve falhar num teste de contrato. Essa mentalidade mantém os contratos focados em quebras reais, não em perfeição teórica.

Passo a passo: adicione testes de contrato no seu pipeline CI

Build APIs without the drift
Create a backend and clients together so your API changes stay predictable.
Start Building

O objetivo dos testes de contrato para APIs é simples: quando alguém muda a API, seu CI deve dizer se algum cliente web ou mobile vai quebrar antes da mudança ir para produção.

1) Comece capturando do que os clientes realmente dependem

Escolha um endpoint e anote as expectativas que importam no uso real: campos obrigatórios, tipos, valores permitidos, códigos de status e respostas de erro comuns. Não tente descrever a API inteira de uma vez. Para apps mobile, inclua também as expectativas de versões antigas do app, já que usuários não atualizam instantaneamente.

Uma forma prática é pegar algumas requisições reais que os clientes fazem hoje (dos logs ou fixtures de teste) e transformá-las em exemplos reprodutíveis.

2) Coloque os contratos onde as equipes vão mantê-los

Contratos falham quando ficam numa pasta esquecida. Mantenha-os perto do código que muda:

  • Se um time controla ambos os lados, armazene contratos no repo da API.
  • Se times diferentes controlam web, mobile e API, use um repo compartilhado mantido pelas equipes, não por uma pessoa só.
  • Trate atualizações de contrato como código: revisadas, versionadas e discutidas.

3) Adicione checagens nos dois lados no CI

Você quer dois sinais:

  • Verificação do provedor a cada build de API: “A API ainda satisfaz todos os contratos conhecidos?”
  • Checagens do consumidor a cada build de cliente: “Este cliente ainda é compatível com o contrato publicado mais recente?”

Isso captura problemas de ambos os lados. Se a API muda um campo de resposta, o pipeline do API falha. Se um cliente começar a esperar um novo campo, o pipeline do cliente falha até a API suportá‑lo.

4) Decida a regra de falha e a aplique

Seja explícito sobre o que bloqueia um merge ou release. Uma regra comum: qualquer mudança que quebre contrato falha no CI e bloqueia o merge para o branch principal. Se precisar de exceções, exija uma decisão escrita (por exemplo, uma data coordenada de release).

Exemplo concreto: uma mudança no backend renomeia totalPrice para total_amount. A verificação do provedor falha imediatamente, então o time de backend adiciona o novo campo mantendo o antigo por um período de transição, e web e mobile continuam entregando com segurança.

Versionamento e compatibilidade retroativa sem desacelerar as equipes

Release faster, with guardrails
Deploy your regenerated build to your cloud setup without slowing your release cycle.
Deploy Now

Times rápidos quebram APIs mais frequentemente ao mudar o que clientes existentes já dependem. Uma “mudança breaking” é qualquer coisa que faça uma requisição antes funcional falhar, ou que torne a resposta significativamente diferente de forma que o cliente não consiga tratar.

Aqui estão mudanças breaking comuns (mesmo quando o endpoint ainda existe):

  • Remover um campo de resposta que clientes leem
  • Mudar o tipo de um campo (como "total": "12" para "total": 12)
  • Tornar um campo opcional em obrigatório (ou adicionar um novo campo de requisição obrigatório)
  • Mudar regras de autenticação (um endpoint público agora precisa de token)
  • Mudar códigos de status ou formato de erro que clientes parseiam (200 para 204, ou um novo formato de erro)

A maioria das equipes pode evitar bumps de versão escolhendo alternativas mais seguras. Se precisa de mais dados, adicione um novo campo em vez de renomear. Se precisa de um endpoint melhor, adicione uma nova rota e mantenha a antiga funcionando. Se precisa apertar validação, aceite ambas as entradas (antiga e nova) por um tempo, depois aplique as novas regras gradualmente. Testes de contrato ajudam porque forçam a provar que consumidores existentes ainda recebem o que esperam.

Deprecar é a parte que mantém a velocidade alta sem prejudicar usuários. Clientes web podem atualizar diariamente, mas apps mobile podem atrasar semanas por fila de revisão e adoção lenta. Planeje deprecação com base no comportamento real dos clientes, não em esperanças.

Uma política de deprecação prática:

  • Anuncie a mudança cedo (notas de release, canal interno, ticket)
  • Mantenha o comportamento antigo até o uso cair abaixo de um limiar acordado
  • Retorne avisos em headers/logs quando a rota deprecada for usada
  • Defina uma data de remoção apenas após confirmar que a maioria dos clientes upgradou
  • Apague o comportamento antigo só depois que testes de contrato mostrarem que nenhum consumidor ativo precisa dele

Use versionamento explícito apenas quando não for possível manter compatibilidade retroativa (por exemplo, uma mudança fundamental na forma do recurso ou no modelo de segurança). Versionar adiciona custo contínuo: você passa a manter dois comportamentos, dois conjuntos de docs e mais casos de borda. Mantenha versões raras e deliberadas, e use contratos para garantir que ambas as versões se mantenham corretas até que a antiga seja realmente segura para remover.

Erros comuns em testes de contrato (e como evitá‑los)

Testes de contrato funcionam melhor quando checam expectativas reais, não uma versão “toy” do seu sistema. A maioria das falhas vem de alguns padrões previsíveis que fazem as equipes se sentirem seguras enquanto bugs ainda chegam à produção.

Erro 1: Tratar contratos como “mocks sofisticados”

Over‑mocking é a armadilha clássica: o teste de contrato passa porque o comportamento do provedor foi mockado para casar com o contrato, não porque o serviço real consegue fazê‑lo. Ao deployar, a primeira chamada real falha.

Uma regra mais segura é simples: contratos devem ser verificados contra o provedor em execução (ou um artefato de build que se comporte igual), com serialização real, validação real e regras de auth reais.

Aqui estão os erros que aparecem mais seguido, e o conserto que geralmente funciona:

  • Over‑mocking do provedor: verifique contratos contra um build real do provedor, não um serviço stubado.
  • Tornar contratos rígidos demais: use correspondência flexível para IDs, timestamps e arrays; evite afirmar todo campo se os clientes não dependem deles.
  • Ignorar respostas de erro: teste pelo menos os principais casos de erro (401, 403, 404, 409, 422, 500) e o formato do corpo de erro que o cliente parseia.
  • Sem propriedade clara: atribua quem atualiza o contrato quando os requisitos mudarem; faça disso parte da “definição de pronto” para mudanças de API.
  • Esquecer as realidades do mobile: teste com redes mais lentas e versões antigas do app em mente, não só a build mais nova em Wi‑Fi rápido.

Erro 2: Contratos frágeis que bloqueiam mudanças inofensivas

Se um contrato falha sempre que você adiciona um novo campo opcional ou reordena chaves JSON, desenvolvedores aprendem a ignorar o build vermelho. Isso derrota o propósito.

Mire em “rigidez onde importa”. Seja rígido quanto a campos obrigatórios, tipos, valores de enum e regras de validação. Seja flexível quanto a campos extras, ordenação e valores que variam naturalmente.

Um exemplo pequeno: seu backend muda status de "active" | "paused" para "active" | "paused" | "trial". Se um app mobile trata valores desconhecidos como crash, isso é breaking. O contrato deve pegar isso checando como o cliente lida com enums desconhecidos, ou exigindo que o provedor continue retornando só valores conhecidos até que todos os clientes dêem suporte ao novo valor.

Clientes mobile merecem atenção extra porque ficam mais tempo no wild. Antes de chamar uma mudança de API de “segura”, pergunte:

  • Versões antigas do app ainda conseguem parsear a resposta?
  • O que acontece se a requisição for re‑tentada após um timeout?
  • Dados em cache vão colidir com o novo formato?
  • Temos fallback quando um campo falta?

Se suas APIs são geradas ou atualizadas rápido (incluindo com plataformas como AppMaster), contratos são uma salvaguarda prática: permitem avançar rápido enquanto provam que web e mobile continuarão funcionando após cada mudança.

Checklist rápido antes de enviar mudanças na API

Keep web and API in sync
Create a web client that stays aligned with your API as your team iterates.
Generate Web App

Use isso pouco antes de fazer merge ou release de uma mudança de API. Foi desenhado para pegar as pequenas edições que causam os maiores incêndios quando web e mobile entregam com frequência. Se você já faz testes de contrato, esta lista ajuda a focar nas quebras que os contratos devem bloquear.

As 5 perguntas a fazer sempre

  • Adicionamos, removemos ou renomeamos algum campo de resposta que clientes leem (incluindo campos aninhados)?
  • Algum código de status mudou (200 vs 201, 400 vs 422, 404 vs 410), ou o formato do corpo de erro mudou?
  • Algum campo virou obrigatório quando antes era opcional (incluindo “pode ser null” vs “deve estar presente”)?
  • Ordenação, paginação ou filtros padrão mudaram (tamanho da página, ordenação, tokens de cursor, defaults)?
  • Testes de contrato rodaram para o provedor e todos os consumidores ativos (web, iOS, Android e ferramentas internas)?

Um exemplo simples: sua API costumava retornar totalCount, e um cliente usa isso para mostrar “24 resultados”. Você o remove porque “a lista já tem itens”. Nada quebra no backend, mas a UI começa a mostrar em branco ou “0 resultados” para alguns usuários. Isso é uma quebra real, mesmo que o endpoint ainda retorne 200.

Se respondeu “sim” para qualquer item

Faça estes passos rápidos antes de enviar:

  • Confirme se clientes antigos ainda funcionam sem update. Se não, adicione um caminho compatível retroativamente (mantenha o campo antigo ou suporte ambos os formatos por um tempo).
  • Verifique o tratamento de erros nos clientes. Muitos apps tratam formatos de erro desconhecidos como “Algo deu errado” e escondem mensagens úteis.
  • Rode testes de contrato de consumidor para cada versão do cliente que você ainda suporta, não só o branch mais recente.

Se você constrói ferramentas internas rápido (por exemplo, um painel admin ou dashboard de suporte), inclua também esses consumidores. No AppMaster, times frequentemente geram web apps e mobile apps a partir dos mesmos modelos de backend, o que facilita esquecer que um pequeno ajuste de schema ainda pode quebrar um cliente já publicado se o contrato não for checado no CI.

Exemplo: pegando uma mudança breaking antes de web e mobile entregarem

Protect mobile releases
Deliver native iOS and Android apps without rewriting logic after every API update.
Build Mobile App

Imagine uma configuração comum: o time de API deploya várias vezes por dia, o app web entrega diariamente e os apps mobile semanalmente (por causa de review na app store e rollouts). Todo mundo anda rápido, então o risco real não é má intenção, é pequenas mudanças que parecem inofensivas.

Um ticket de suporte pede nomes mais claros na resposta do perfil do usuário. O time de API renomeia um campo em GET /users/{id} de phone para mobileNumber.

Esse rename parece caprichado, mas é uma mudança breaking. O web pode renderizar o número do telefone em branco na página de perfil. Pior, o mobile pode travar se tratar phone como obrigatório, ou falhar validação ao salvar o perfil.

Com testes de contrato, isso é pego antes de chegar aos usuários. Veja como normalmente falha, dependendo de como as checagens rodam:

  • Build do provedor falha (lado da API): o job de CI da API verifica o provedor contra contratos salvos do web e mobile. Vê que consumidores ainda esperam phone, mas o provedor agora retorna mobileNumber, então a verificação falha e o deploy é bloqueado.
  • Build do consumidor falha (lado do cliente): o time web atualiza seu contrato para exigir mobileNumber antes da API disponibilizá‑lo. O teste de contrato deles falha porque o provedor ainda não fornece esse campo.

De qualquer forma, a falha é precoce, alta e específica: aponta o endpoint exato e a incompatibilidade de campo, em vez de aparecer como “página de perfil quebrada” depois da release.

O conserto costuma ser simples: tornar a mudança aditiva, não destrutiva. A API passa a retornar ambos os campos por um tempo:

  • Adicione mobileNumber.
  • Mantenha phone como alias (mesmo valor).
  • Marque phone como deprecado nas notas do contrato.
  • Atualize web e mobile para ler mobileNumber.
  • Remova phone só depois de confirmar que todas as versões suportadas migraram.

Um cronograma realista sob pressão de release poderia ser:

  • Seg 10:00: Time de API adiciona mobileNumber e mantém phone. Testes de contrato do provedor passam.
  • Seg 16:00: Web troca para mobileNumber e entrega.
  • Qui: Mobile troca para mobileNumber e submete release.
  • Ter seguinte: Release mobile chega à maioria dos usuários.
  • Sprint seguinte: API remove phone, e testes de contrato confirmam que nenhum consumidor suportado ainda depende dele.

Esse é o valor central: testes de contrato transformam “roleta russa de mudanças breaking” em uma transição controlada e temporizada.

Próximos passos para equipes que se movem rápido (incluindo opção sem código)

Se quer que testes de contrato realmente evitem quebras (e não só adicionem mais checagens), mantenha a implantação pequena e deixe a propriedade clara. O objetivo é simples: pegar mudanças breaking antes que atinjam web e mobile.

Comece com um rollout leve. Escolha os 3 endpoints que mais causam dor quando mudam, normalmente auth, perfil de usuário e um endpoint principal de “lista ou busca”. Coloque esses sob contrato primeiro e expanda quando a equipe confiar no fluxo.

Um rollout prático e gerenciável:

  • Semana 1: testes de contrato para os 3 endpoints principais, rodando em cada pull request
  • Semana 2: adicione os próximos 5 endpoints com maior uso mobile
  • Semana 3: cubra respostas de erro e casos de borda (estados vazios, erros de validação)
  • Semana 4: torne “contrato verde” um gate de release para mudanças de backend

Depois, decida quem faz o quê. Times avançam mais rápido quando fica óbvio quem é dono de uma falha e quem aprova uma mudança.

Mantenha papéis simples:

  • Dono do contrato: normalmente o time de backend, responsável por atualizar contratos quando o comportamento muda
  • Revisores consumidores: leads de web e mobile que confirmam que mudanças são seguras para seus clientes
  • Build sheriff: rota diária ou semanal que triageia falhas de teste de contrato no CI
  • Dono da release: toma a decisão de bloquear uma release se um contrato estiver quebrado

Acompanhe uma métrica de sucesso que todos se importem. Para muitas equipes, o melhor sinal é menos hotfixes após releases e menos “regressões de cliente” como crashes, telas em branco ou checkouts quebrados ligados a mudanças de API.

Se quer feedback ainda mais rápido, plataformas no‑code podem reduzir drift ao regenerar código limpo após mudanças. Quando lógica ou modelos de dados mudam, regeneraçã o ajuda a evitar o acúmulo lento de patches que acidentalmente mudam comportamento.

Se você constrói APIs e clientes com AppMaster, um próximo passo prático é criar uma aplicação, modelar seus dados no Data Designer (PostgreSQL), atualizar fluxos no Business Process Editor e então regenerar e deployar para sua cloud (ou exportar o código‑fonte). Emparelhe isso com checagens de contrato no CI para que cada build regenerado prove que ainda corresponde ao que web e mobile esperam.

Fácil de começar
Criar algo espantoso

Experimente o AppMaster com plano gratuito.
Quando estiver pronto, você poderá escolher a assinatura adequada.

Comece