Testes de contrato em microsserviços
1. Fundamentos dos Testes de Contrato
Testes de contrato são uma abordagem de verificação de compatibilidade entre serviços que estabelecem um "acordo formal" sobre como dois serviços devem interagir. Diferentemente de testes de integração tradicionais, que exigem que ambos os serviços estejam em execução simultaneamente, os testes de contrato validam as expectativas de comunicação sem a necessidade de infraestrutura completa.
O propósito central é garantir que um serviço consumidor possa confiar que o serviço provedor atenderá às suas necessidades, sem quebrar acidentalmente integrações existentes. Essa abordagem reduz drasticamente o acoplamento entre equipes e acelera ciclos de desenvolvimento.
Diferenças fundamentais:
Teste de contrato: Verifica se provedor atende às expectativas do consumidor
Teste de integração: Verifica comunicação real entre serviços em execução
Teste end-to-end: Verifica fluxo completo através de múltiplos serviços
Enquanto testes de integração podem ser lentos e frágeis, e testes E2E são caros e demorados, os testes de contrato oferecem feedback rápido e isolado sobre compatibilidade.
2. Abordagens de Teste de Contrato: Consumer-Driven vs. Provider-Driven
Consumer-Driven Contracts (CDC)
Nesta abordagem, o consumidor define suas expectativas e as publica como contrato. O provedor então valida se atende a todas as expectativas registradas. É a abordagem mais comum e é implementada pelo Pact.
Exemplo de contrato CDC (Pact):
{
"consumer": {
"name": "ServicoPedidos"
},
"provider": {
"name": "ServicoClientes"
},
"interactions": [
{
"description": "uma requisição para buscar cliente por ID",
"request": {
"method": "GET",
"path": "/clientes/123",
"headers": {
"Accept": "application/json"
}
},
"response": {
"status": 200,
"headers": {
"Content-Type": "application/json"
},
"body": {
"id": 123,
"nome": "Maria Silva",
"email": "maria@exemplo.com"
}
}
}
]
}
Provider-Driven Contracts
Aqui o provedor define o contrato e o consumidor valida se consegue consumir a API conforme especificado. Útil quando o provedor possui múltiplos consumidores e precisa garantir estabilidade.
Trade-offs:
| Abordagem | Vantagens | Desvantagens |
|---|---|---|
| CDC | Consumidor define necessidades reais | Pode gerar contratos conflitantes |
| Provider-Driven | Provedor mantém controle | Consumidor pode ter necessidades não atendidas |
3. Ferramentas e Frameworks Principais
Pact
Ferramenta mais popular para CDC, suporta REST, GraphQL e mensageria. Oferece bibliotecas para múltiplas linguagens.
Exemplo de teste consumidor com Pact (JUnit):
@ExtendWith(PactConsumerTestExt.class)
@PactTestFor(providerName = "ServicoClientes", port = "8080")
public class ClienteConsumerTest {
@Pact(consumer = "ServicoPedidos")
public RequestResponsePact criarPact(PactDslWithProvider builder) {
return builder
.given("cliente com ID 123 existe")
.uponReceiving("uma requisição para buscar cliente")
.path("/clientes/123")
.method("GET")
.willRespondWith()
.status(200)
.headers(Map.of("Content-Type", "application/json"))
.body(new PactDslJsonBody()
.integerType("id", 123)
.stringType("nome", "Maria Silva")
.stringType("email", "maria@exemplo.com"))
.toPact();
}
@Test
@PactTestFor(pactMethod = "criarPact")
public void testBuscarCliente(MockServer mockServer) {
ClienteService clienteService = new ClienteService(mockServer.getUrl());
Cliente cliente = clienteService.buscarPorId(123);
assertEquals("Maria Silva", cliente.getNome());
}
}
Spring Cloud Contract
Integração nativa com ecossistema Spring, suporta contratos definidos em YAML, Groovy ou Java.
Exemplo de contrato Spring Cloud Contract (YAML):
request:
method: GET
url: /clientes/123
headers:
Accept: application/json
response:
status: 200
headers:
Content-Type: application/json
body:
id: 123
nome: "Maria Silva"
email: "maria@exemplo.com"
matchers:
body:
- path: "$.id"
type: by_regex
value: "\\d+"
Outras Ferramentas
- PactFlow: Plataforma gerenciada para Pact com publicação e versionamento de contratos
- Specmatic: Foco em contratos como especificações executáveis baseadas em OpenAPI
4. Ciclo de Vida de um Teste de Contrato
Criação do Contrato
O consumidor define as interações esperadas durante o desenvolvimento, geralmente usando um framework de teste.
Execução no Lado Consumidor
O teste consumidor gera um arquivo de contrato (Pact) que é armazenado localmente ou publicado em um repositório.
Pipeline do consumidor:
1. Desenvolvedor cria teste consumidor
2. Teste gera contrato Pact
3. Contrato é publicado no repositório (Pact Broker)
4. Pipeline do provedor recupera contratos
5. Provedor executa verificações contra contratos
6. Resultado é reportado ao broker
Execução no Lado Provedor
O provedor recupera os contratos e executa verificações para garantir que sua API atende às expectativas.
Exemplo de verificação provedor com Pact:
@Provider("ServicoClientes")
@PactBroker(url = "http://pact-broker:9292")
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class ClienteProviderTest {
@LocalServerPort
int port;
@BeforeEach
void setup() {
System.setProperty("pact.verifier.publishResults", "true");
}
@TestTemplate
@ExtendWith(PactVerificationInvocationContextProvider.class)
void pactVerificationTestTemplate(PactVerificationContext context) {
context.verifyInteraction();
}
@TestTarget
public final Target target = new HttpTarget("http", "localhost", port);
}
5. Estratégias de Versionamento e Compatibilidade
Versionamento Semântico de Contratos
Adote versionamento semântico (MAJOR.MINOR.PATCH) para contratos:
- MAJOR: Mudança incompatível (remoção de campo obrigatório)
- MINOR: Adição compatível (novo campo opcional)
- PATCH: Correção sem alteração de interface
Detecção de Quebras
Quando um provedor modifica sua API, os testes de contrato falham automaticamente, sinalizando a quebra. Estratégias de mitigação:
1. Notificar equipe consumidora sobre mudança planejada
2. Implementar versão compatível temporariamente
3. Coordenar deploy simultâneo de consumidor e provedor
4. Usar feature flags para controlar exposição de mudanças
Compatibilidade Retroativa
Sempre que possível, adicione novos campos como opcionais e mantenha campos antigos até que todos os consumidores migrem.
6. Integração com Pipeline de CI/CD
Automação em Pipelines
Configure pipelines para executar testes de contrato automaticamente:
# Exemplo de pipeline GitLab CI
stages:
- test
- verify-contracts
- deploy
verify-contracts:
stage: verify-contracts
script:
- ./gradlew pactVerify
only:
- main
- develop
Verificação Pré-Deploy
Antes de promover um deploy para produção, verifique a compatibilidade com todos os consumidores registrados:
1. Pipeline do provedor executa verificações de contrato
2. Se falhar, bloqueia deploy (quebra de build)
3. Se passar, permite deploy para ambiente de staging
4. Após validação em staging, promove para produção
5. Publica resultado da verificação no Pact Broker
Estratégias de Deploy Seguro
- Canary: Libere nova versão para subconjunto de tráfego
- Blue-Green: Mantenha versão antiga como fallback
- Feature Flags: Desative mudanças que quebram contratos
7. Desafios e Boas Práticas
Gerenciamento de Múltiplos Consumidores
Quando vários consumidores têm contratos conflitantes, estabeleça um processo de governança:
1. Reunião periódica de alinhamento de contratos
2. Priorização baseada em impacto de negócio
3. Versões alternativas da API (v1, v2)
4. Depreciação gradual de endpoints antigos
Testes em Ambientes Assíncronos
Para mensageria e eventos, use contratos baseados em mensagens:
Exemplo de contrato de mensageria com Pact:
{
"description": "uma mensagem de pedido criado",
"providerStates": [
{
"name": "um pedido com ID 456 foi criado"
}
],
"contents": {
"id": 456,
"status": "CRIADO",
"total": 150.00
},
"messageType": "PedidoCriado"
}
Monitoramento Contínuo
Implemente monitoramento para detectar quebras em produção:
- Pact Broker: Dashboard com status de verificações
- Alertas: Notificações quando contrato quebra
- Métricas: Taxa de sucesso de verificações ao longo do tempo
Boas Práticas Essenciais
- Mantenha contratos simples: Foco em interações reais, não em todos os cenários possíveis
- Versionamento explícito: Use tags e versões no Pact Broker
- Testes em isolamento: Não dependa de outros serviços durante testes de contrato
- Documentação viva: Contratos servem como documentação executável
- Feedback rápido: Execute verificações em cada commit
Referências
- Documentação Oficial do Pact — Guia completo sobre implementação de testes de contrato com Pact, incluindo CDC para REST e mensageria
- Spring Cloud Contract Reference — Documentação oficial do Spring Cloud Contract com exemplos de contratos em YAML e Groovy
- Martin Fowler: Consumer-Driven Contracts — Artigo seminal de Martin Fowler sobre a abordagem de contratos orientados pelo consumidor
- PactFlow Documentation — Plataforma gerenciada para publicação, versionamento e verificação de contratos Pact
- Testes de Contrato em Microsserviços: Guia Prático — Artigo da InfoQ com casos reais e estratégias de implementação
- Specmatic Documentation — Ferramenta para contratos baseados em especificações OpenAPI executáveis
- Building Microservices: Contract Testing Chapter — Capítulo do livro de Sam Newman sobre testes de contrato em arquitetura de microsserviços