Middle+
Fale sobre os testes de contrato de um microserviço.
sobes.tech IA
Resposta da IA
Os testes de contrato concentram-se na verificação da interação entre microserviços ao nível da sua API (contrato). O objetivo é garantir que o consumidor (serviço que utiliza a API) e o fornecedor (serviço que fornece a API) cumpram um "contrato" definido (esquema de pedidos/respostas, formato de dados, expectativas).
Princípios básicos:
- Consumidor-dirigido: Os testes baseiam-se nas expectativas do consumidor. O consumidor determina quais dados e formatos necessita.
- Testes bidirecionais: Verifica-se tanto a capacidade do consumidor (para trabalhar com o contrato atual) como a do fornecedor (para cumprir as expectativas do consumidor).
- Velocidade: Os testes são executados rapidamente, muitas vezes numa pipeline CI/CD, pois não requerem a implantação de todos os serviços dependentes.
O processo geralmente inclui:
- O consumidor cria testes que descrevem as suas expectativas para a API do fornecedor. Estes testes geram um "contrato".
- O contrato é publicado (por exemplo, num repositório centralizado).
- O fornecedor usa o contrato publicado para verificar a sua implementação da API. Executa testes (geralmente do lado do fornecedor), que verificam se a sua API cumpre o contrato.
- Em caso de discrepância entre as expectativas do consumidor e a implementação do fornecedor, o teste falha, indicando uma violação do contrato.
Ferramentas:
- Pact (o mais popular)
- Spring Cloud Contract
- Swagger/OpenAPI com ferramentas de validação
Vantagens:
- Detecção precoce de erros de integração: Os problemas de compatibilidade são detectados antes do deployment.
- Redução da necessidade de testes de integração: Diminui a dependência de testes de ponta a ponta complexos e lentos.
- Testes independentes: Permite testar os serviços de forma independente.
- Definição clara de expectativas: Define explicitamente o contrato entre serviços.
Desvantagens:
- Não substitui completamente os testes de integração (apenas verifica a interação ao nível da API).
- Requer disciplina na gestão de versões dos contratos.
- Pode ser mais complexo para serviços com contratos muito complexos.
Exemplo de uso com Pact:
- O consumidor (por exemplo, o serviço
Order Service) usa a biblioteca Pact para criar testes do lado do consumidor.// Exemplo de teste do consumidor com Pact JVM @ExtendWith(PactConsumerTestExt.class) @PactTestFor(providerName = "ProductService", port = "8080") public class ProductServiceContractTest { @Pact(consumer = "OrderService") public RequestResponsePact createPact(PactDslWithRequest r) { return r.given("a product with id 1 exists") .uponReceiving("a request for product by id") .path("/products/1") .method("GET") .willRespondWith() .status(200) .headers(Map.of("Content-Type", "application/json")) .body(new PactDslJsonBody() .stringValue("id", "1") .stringValue("name", "Laptop") .numberType("price", 1200.00)) .toPact(); } @Test void testGetProductById(MockServer mockServer) throws IOException { // Lógica para chamar a API do fornecedor através de mockServer // Verifica que o consumidor manipula corretamente a resposta mockada HttpResponse response = Request.Get(mockServer.getUrl() + "/products/1").execute().returnResponse(); assertThat(response.getStatusLine().getStatusCode(), equalTo(200)); } } - Ao executar os testes do lado do consumidor, é gerado um arquivo de contrato (
.json). - Este arquivo de contrato é publicado no Pact Broker ou noutro repositório centralizado.
- O fornecedor (
Product Service) usa a biblioteca Pact (por exemplo, Pact Provider Verifier) para verificar a sua implementação da API com base no contrato obtido do Pact Broker.// Exemplo de teste do fornecedor com Pact Provider Verifier @Provider("ProductService") @PactFolder("relative/path/to/pacts") // ou usar Pact Broker public class ProductServiceVerificationTest { @TestTemplate @ExtendWith(PactVerificationInvocationContextProvider.class) public void verifyPact(PactVerificationContext context) { context.verifyInteraction(); } @State("a product with id 1 exists") public void productWithId1Exists() { // Lógica para preparar o estado dos dados no serviço do fornecedor para o teste } // Adiciona a URL do provedor onde o serviço testado está em execução @BeforeEach void beforeEach(PactVerificationContext context) { context.setTarget(new HttpTestTarget("localhost", 8080)); } } - O verificador do lado do fornecedor chama a API real do fornecedor de acordo com a especificação do contrato e compara a resposta recebida com a esperada no contrato.
O teste de contrato é uma prática importante para garantir a fiabilidade e a manutenção de sistemas baseados em microserviços.