Home > Blog > Desenvolvimento Web
Desenvolvimento Web
JavaScript
Programação

Contract Testing com Pact

Atualizado em: 5 de setembro de 2026

Estação de trabalho usada no desenvolvimento de aplicações Node.js

O Contract Testing com Pact verifica se consumidores e provedores de APIs mantêm expectativas compatíveis sem depender de um ambiente end-to-end completo. O consumidor descreve as interações que realmente utiliza, gera um contrato e o provedor executa uma verificação contra sua implementação.

Essa abordagem detecta mudanças incompatíveis antes do deploy, reduz testes integrados frágeis e documenta o uso real de endpoints. Pact é consumer-driven: o contrato nasce das necessidades do consumidor, não de uma cópia manual da especificação do provedor.

Neste guia, você aprenderá testes de consumidor, provider verification, matchers, provider states, Pact Broker, can-i-deploy, CI/CD, eventos, versionamento, segurança e boas práticas.

O que é Pact?

Pact é uma ferramenta de contract testing para HTTP e sistemas orientados a eventos. A documentação oficial do Pact JS apresenta PactV3, MatchersV3, mock server, provider verification e integração com Pact Broker. O guia geral em Pact Docs explica o fluxo consumer-driven.

Para documentar contratos públicos, consulte OpenAPI com Node.js. Para testes com dependências reais, veja Testcontainers no Node.js.

Problema dos testes end-to-end

Um teste que sobe consumidor, provedor, banco, fila e serviços externos pode falhar por muitos motivos:

  • ambiente indisponível;
  • dados compartilhados;
  • versões incompatíveis;
  • latência;
  • dependência externa;
  • ordem dos testes;
  • configuração;
  • rede.

Mesmo quando passa, pode não cobrir todas as expectativas de cada consumidor.

Fluxo do Pact

  1. O consumidor testa seu cliente contra um mock server Pact.
  2. O teste gera um arquivo pact.
  3. O contrato é publicado no Pact Broker.
  4. O provedor baixa os contratos relevantes.
  5. O provider verification executa cada interação.
  6. Os resultados são publicados.
  7. O pipeline consulta se a versão pode ser implantada.

Instalação

npm install --save-dev @pact-foundation/pact

Verifique os requisitos de Node e plataformas da versão atual.

Cliente HTTP a testar

export class UserApiClient {
  constructor(private readonly baseUrl: string) {}

  async getUser(id: string): Promise<User> {
    const response = await fetch(
      `${this.baseUrl}/users/${id}`,
      {
        headers: {
          accept: 'application/json'
        }
      }
    );

    if (!response.ok) {
      throw new UserApiError(response.status);
    }

    return response.json();
  }
}

O teste Pact deve chamar o cliente real usado pela aplicação.

Teste de consumidor

import {
  PactV3,
  MatchersV3
} from '@pact-foundation/pact';

const pact = new PactV3({
  consumer: 'orders-web',
  provider: 'users-api',
  dir: './pacts'
});

const { like, uuid, string } = MatchersV3;

it('busca um usuário', async () => {
  pact
    .given('o usuário 42 existe')
    .uponReceiving('uma busca pelo usuário 42')
    .withRequest({
      method: 'GET',
      path: '/users/42',
      headers: {
        accept: 'application/json'
      }
    })
    .willRespondWith({
      status: 200,
      headers: {
        'content-type': like('application/json')
      },
      body: {
        id: uuid(),
        name: string('Ana'),
        plan: like('premium')
      }
    });

  await pact.executeTest(async mockServer => {
    const client = new UserApiClient(mockServer.url);
    const user = await client.getUser('42');

    assert.equal(user.plan, 'premium');
  });
});

O mock server verifica método, path, headers e resposta esperada.

Matchers

Matchers evitam contratos frágeis. Em vez de exigir um UUID específico, o contrato exige qualquer UUID válido.

  • like(): mesmo tipo e estrutura.
  • string(): qualquer string.
  • integer(): qualquer inteiro.
  • uuid(): UUID.
  • regex(): padrão.
  • eachLike(): array com elementos semelhantes.

Use matchers apenas onde a variação é permitida.

Não deixe o contrato permissivo demais

Se qualquer campo usa like() sem restrições, uma mudança relevante pode passar. Defina valores exatos para enum, status, content type e códigos que fazem parte da lógica.

Provider state

.given('o usuário 42 existe')

O state descreve a pré-condição, não como criá-la. O provider verification recebe o nome e prepara dados.

Estados claros

Prefira:

  • “o pedido 123 está pendente”;
  • “não existe usuário com o e-mail informado”;
  • “o cliente possui plano premium”.

Evite states técnicos como “execute INSERT na tabela users”.

Provider verification

import { Verifier } from '@pact-foundation/pact';

await new Verifier({
  provider: 'users-api',
  providerBaseUrl: 'http://127.0.0.1:8080',
  pactBrokerUrl: process.env.PACT_BROKER_URL,
  pactBrokerToken: process.env.PACT_BROKER_TOKEN,
  publishVerificationResult: true,
  providerVersion: process.env.GIT_SHA,
  stateHandlers: {
    'o usuário 42 existe': async () => {
      await resetDatabase();
      await createUserFixture({ id: '42' });
    }
  }
}).verifyProvider();

A API deve estar rodando localmente com dependências controladas.

Verificação contra pact local

Durante desenvolvimento, use pactUrls apontando para arquivos locais. No CI, use Broker para encontrar contratos.

Banco do provider

Testcontainers pode iniciar PostgreSQL ou MongoDB, aplicar migrations e preparar provider states. Isso valida o adapter real sem depender de ambiente compartilhado.

Isolamento de provider states

Cada interação deve começar em estado conhecido. Use truncate, rollback ou banco descartável. Não dependa da ordem de execução.

Autenticação

O verifier pode alterar requests antes de enviar para adicionar token de teste. Não grave tokens reais no pact.

requestFilter: (req, res, next) => {
  req.headers.authorization = 'Bearer test-token';
  next();
}

A API ainda deve validar autenticação em testes próprios.

Pact Broker

O Broker armazena:

  • pacts;
  • versões de consumidor;
  • versões de provedor;
  • tags ou branches;
  • environments;
  • resultados de verificação;
  • matriz de compatibilidade.

Publicando pact

Após os testes de consumidor:

pact-broker publish ./pacts \
  --consumer-app-version "$GIT_SHA" \
  --branch "$GIT_BRANCH"

Use a CLI oficial ou biblioteca recomendada pela versão atual.

Versão imutável

Identifique builds com commit SHA. Não publique contratos diferentes com a mesma versão.

Branches e environments

Modelos modernos do Pact Broker usam branches e deploy records. Registre quando uma versão é implantada em test, staging ou production.

can-i-deploy

Antes do deploy, consulte se todos os contratos relevantes foram verificados:

pact-broker can-i-deploy \
  --pacticipant orders-web \
  --version "$GIT_SHA" \
  --to-environment production

O comando precisa bloquear o deploy em incompatibilidade.

Webhook do Broker

Quando um consumidor publica contrato novo, o Broker pode disparar pipeline do provedor. Assim, a compatibilidade é verificada antes de o consumidor ser liberado.

Pending pacts

Pending pacts evitam quebrar o build principal do provedor quando surge uma expectativa nova ainda não implementada, mantendo o resultado visível. Configure a estratégia conforme o fluxo de branches.

WIP pacts

Work-in-progress pacts ajudam a verificar contratos recentes de branches sem exigir seleção manual. Siga as recomendações do Broker atual.

Mudança compatível

Adicionar campo opcional geralmente é compatível se consumidores ignoram campos extras. Remover ou alterar tipo de campo utilizado quebra o contrato.

Campos extras

Pact normalmente verifica apenas o que o consumidor declarou, permitindo que o provedor retorne campos adicionais. Isso reduz acoplamento.

Contrato não substitui testes do provider

Pact confirma expectativas dos consumidores conhecidos, mas não testa:

  • todas as regras internas;
  • segurança completa;
  • performance;
  • observabilidade;
  • migrations;
  • clientes desconhecidos;
  • requisitos regulatórios.

OpenAPI versus Pact

  • OpenAPI: contrato publicado pelo provedor.
  • Pact: expectativas exercidas por consumidores.

As duas abordagens se complementam.

Contract tests versus integração

Pact verifica comunicação na borda. Um teste de integração ainda pode validar repository, fila ou provider externo. Não remova todos os testes end-to-end; mantenha poucos fluxos críticos.

Erros HTTP

Crie interações para:

  • 200 ou 201;
  • 404;
  • 409;
  • 422;
  • 429;
  • 503;
  • timeout tratado pelo cliente.

O consumidor precisa testar seu comportamento, não apenas o caminho feliz.

Arrays

body: MatchersV3.eachLike({
  id: MatchersV3.uuid(),
  name: MatchersV3.string('Ana')
}, { min: 1 })

Também teste lista vazia quando permitida.

Datas

Use matcher de regex ou timestamp conforme o contrato. Evite fixar uma data real sem necessidade.

Query parameters

Declare parâmetros e encoding esperados. Ordenação de query string pode variar; use a representação suportada pelo Pact em vez de string manual.

Headers

Não inclua headers instáveis como trace ID. Verifique apenas headers relevantes ao contrato, como content type, versionamento ou idempotency key.

GraphQL

Pact JS possui suporte e exemplos para GraphQL. O contrato precisa representar operação, variables e resposta usada pelo consumidor.

Mensagens e eventos

Pact também testa sistemas event-driven. O consumidor descreve a mensagem esperada; o provedor gera o evento para verificação.

Consulte Domain Events no Node.js.

Contrato de evento

{
  "type": "order.approved",
  "version": 1,
  "eventId": "uuid",
  "data": {
    "orderId": "uuid",
    "approvedAt": "timestamp"
  }
}

Teste schema, metadata e campos usados. Não acople o consumidor a campos irrelevantes.

Provider de mensagem

A verificação chama uma função que produz a mensagem para um provider state. Ela não precisa subir o broker, pois o foco é o payload.

Eventos e compatibilidade

Adicionar campo costuma ser seguro; renomear ou alterar semântica exige nova versão. Mantenha consumidores antigos durante a migração.

Mocks tradicionais

Um mock escrito manualmente pode divergir da API real. Pact transforma a expectativa em contrato verificável pelo provedor.

Não teste implementação do provider no consumidor

O consumidor declara comportamento observável. Não especifique SQL, framework, nome de classe ou detalhes internos.

Tamanho dos contratos

Contratos muito grandes podem indicar que o consumidor conhece campos demais. Use DTOs pequenos e endpoints coesos.

Ownership

Consumidor mantém testes de consumidor. Provedor mantém verificação e provider states. Ambas as equipes acompanham falhas de compatibilidade.

CI do consumidor

  1. executar testes;
  2. gerar pacts;
  3. publicar com SHA e branch;
  4. consultar can-i-deploy antes da promoção;
  5. registrar deploy.

CI do provedor

  1. subir API e dependências;
  2. buscar pacts relevantes;
  3. preparar provider states;
  4. verificar;
  5. publicar resultado;
  6. bloquear deploy incompatível;
  7. registrar deploy.

Segurança do Broker

  • use TLS;
  • token por CI;
  • privilégio mínimo;
  • rotação;
  • não publique dados pessoais;
  • proteja contratos internos;
  • audite acessos.

Dados sensíveis no pact

Arquivos pact ficam armazenados e compartilhados. Use dados fictícios e matchers. Não inclua token, documento real ou segredo.

Observabilidade

Monitore:

  • contratos publicados;
  • verificações pendentes;
  • falhas por provider;
  • tempo até verificação;
  • deploy bloqueado;
  • pacts sem consumidor ativo;
  • provider states lentos.

Limpeza de versões

Configure retenção de versões antigas sem remover dados necessários para ambientes ainda implantados.

Testes locais

Desenvolvedores devem executar consumer tests e provider verification localmente. O feedback não pode depender apenas do CI.

Debug

Quando falhar, compare:

  • request recebido;
  • request esperado;
  • response real;
  • matcher;
  • provider state;
  • versões do contrato.

Erros comuns

  • Contrato manual: não representa o cliente real.
  • Valores exatos demais: testes ficam frágeis.
  • Matchers permissivos: quebra passa.
  • Provider state compartilhado: verificação é instável.
  • Sem Broker: compatibilidade não entra no deploy.
  • Mesma versão republicada: matriz fica inconsistente.
  • Apenas caminho feliz: cliente não testa erros.
  • Segredo no pact: dado vaza.

Boas práticas

  • Teste o cliente real.
  • Use provider states semânticos.
  • Use matchers precisos.
  • Publique por SHA.
  • Use branches e environments.
  • Execute provider verification no CI.
  • Bloqueie com can-i-deploy.
  • Teste erros esperados.
  • Proteja dados do contrato.
  • Mantenha poucos E2E críticos.

Conclusão

O Contract Testing com Pact conecta testes do consumidor à verificação do provedor. As expectativas usadas pelo cliente se tornam contratos que impedem mudanças incompatíveis.

Com provider states isolados, matchers equilibrados, Pact Broker e can-i-deploy, a compatibilidade entra no pipeline. Pact não substitui OpenAPI, integração ou E2E, mas reduz a dependência de ambientes compartilhados e acelera a evolução de APIs.

10 melhores cursos de programação em 2026

Descubra os melhores cursos de programação. Aprenda a escolher o curso ideal para iniciar ou avançar na carreira de desenvolvedor

POSTS RELACIONADOS

Ver todos

Seta para a direita