API

Contract testing: garantindo o contrato entre serviços

Em arquiteturas com muitos serviços, o maior risco não está dentro de cada serviço — está no contrato entre eles. Um time muda o formato de uma resposta e, sem perceber, quebra três consumidores. Contract testing existe para evitar exatamente isso.

O problema dos testes E2E entre serviços

Subir todos os serviços juntos para testar a integração é lento, caro e frágil. Contract testing troca isso por uma verificação leve: cada lado prova que respeita o contrato acordado, sem precisar do outro no ar.

Consumer-driven contracts

Com ferramentas como o Pact, o consumidor declara o que espera da API. Isso gera um contrato que o provedor verifica no próprio pipeline.

// Lado consumidor: "espero este formato"
await provider.addInteraction({
  state: 'usuário 1 existe',
  uponReceiving: 'um pedido pelo usuário 1',
  withRequest:  { method: 'GET', path: '/users/1' },
  willRespondWith: {
    status: 200,
    body: { id: 1, name: like('Diogo') }
  }
});

O fluxo

  1. O consumidor define expectativas e gera o pact (o contrato).
  2. O contrato é publicado num broker.
  3. O provedor roda o contrato contra sua API real e prova que o respeita.
  4. Quebrou o contrato? O pipeline do provedor falha — antes de chegar em produção.
Contract testing dá o melhor dos dois mundos: a confiança da integração com a velocidade do teste de unidade.

Conclusão

Para sistemas distribuídos, contratos versionados e verificados automaticamente são uma rede de segurança poderosa. Eles transformam "será que quebrei alguém?" em uma resposta objetiva dada pelo pipeline.

In architectures with many services, the biggest risk is not inside each service; it's in the contract between them. One team changes the shape of a response and, without realizing it, breaks three consumers. Contract testing exists to prevent exactly that.

The problem with E2E tests across services

Spinning up all the services together to test the integration is slow, expensive, and brittle. Contract testing swaps that for a lightweight check: each side proves it honors the agreed contract, without needing the other one to be up.

Consumer-driven contracts

With tools like Pact, the consumer declares what it expects from the API. This generates a contract that the provider verifies in its own pipeline.

// Consumer side: "I expect this format"
await provider.addInteraction({
  state: 'user 1 exists',
  uponReceiving: 'a request for user 1',
  withRequest:  { method: 'GET', path: '/users/1' },
  willRespondWith: {
    status: 200,
    body: { id: 1, name: like('Diogo') }
  }
});

The flow

  1. The consumer defines expectations and generates the pact (the contract).
  2. The contract is published to a broker.
  3. The provider runs the contract against its real API and proves it honors it.
  4. Broke the contract? The provider's pipeline fails, before it ever reaches production.
Contract testing gives you the best of both worlds: the confidence of integration with the speed of a unit test.

Conclusion

For distributed systems, versioned and automatically verified contracts are a powerful safety net. They turn "did I break anyone?" into an objective answer given by the pipeline.

Leia também

Voltar ao blog