Un'introduzione al contratto Spring Cloud

1. Introduzione

Spring Cloud Contract è un progetto che, in poche parole, ci aiuta a scrivere Consumer-Driven Contracts (CDC).

Ciò garantisce il contratto tra un produttore e un consumatore , in un sistema distribuito, sia per le interazioni basate su HTTP che per quelle basate sui messaggi.

In questo rapido articolo, esploreremo la scrittura di casi di test lato produttore e consumatore per Spring Cloud Contract attraverso un'interazione HTTP.

2. Produttore - lato server

Scriveremo un CDC lato produttore, sotto forma di EvenOddController , che dice solo se il parametro del numero è pari o dispari:

@RestController public class EvenOddController { @GetMapping("/validate/prime-number") public String isNumberPrime(@RequestParam("number") Integer number) { return Integer.parseInt(number) % 2 == 0 ? "Even" : "Odd"; } }

2.1. Dipendenze di Maven

Per il nostro lato produttore, avremo bisogno della dipendenza Spring-Cloud-Starter-Contract-Verifier :

 org.springframework.cloud spring-cloud-starter-contract-verifier 2.1.1.RELEASE test 

E dovremo configurare spring-cloud-contract-maven-plugin con il nome della nostra classe di test di base, che descriveremo nella sezione successiva:

 org.springframework.cloud spring-cloud-contract-maven-plugin 2.1.1.RELEASE true   com.baeldung.spring.cloud.springcloudcontractproducer.BaseTestClass   

2.2. Installazione lato produttore

Dobbiamo aggiungere una classe base nel pacchetto di test che carica il nostro contesto Spring:

@RunWith(SpringRunner.class) @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.MOCK) @DirtiesContext @AutoConfigureMessageVerifier public class BaseTestClass { @Autowired private EvenOddController evenOddController; @Before public void setup() { StandaloneMockMvcBuilder standaloneMockMvcBuilder = MockMvcBuilders.standaloneSetup(evenOddController); RestAssuredMockMvc.standaloneSetup(standaloneMockMvcBuilder); } }

Nel / src / test / resources / contract / package, aggiungeremo gli stub di test , come questo nel file shouldReturnEvenWhenRequestParamIsEven.groovy :

import org.springframework.cloud.contract.spec.Contract Contract.make { description "should return even when number input is even" request{ method GET() url("/validate/prime-number") { queryParameters { parameter("number", "2") } } } response { body("Even") status 200 } } 

Quando eseguiamo la build, il plugin genera automaticamente una classe di test denominata ContractVerifierTest che estende la nostra BaseTestClass e la inserisce in / target / generated-test-sources / contract / .

I nomi dei metodi di test derivano dal prefisso " validate_" concatenato con i nomi dei nostri stub di test Groovy. Per il file Groovy sopra, il nome del metodo generato sarà "validate_shouldReturnEvenWhenRequestParamIsEven" .

Diamo un'occhiata a questa classe di test generata automaticamente:

public class ContractVerifierTest extends BaseTestClass { @Test public void validate_shouldReturnEvenWhenRequestParamIsEven() throws Exception { // given: MockMvcRequestSpecification request = given(); // when: ResponseOptions response = given().spec(request) .queryParam("number","2") .get("/validate/prime-number"); // then: assertThat(response.statusCode()).isEqualTo(200); // and: String responseBody = response.getBody().asString(); assertThat(responseBody).isEqualTo("Even"); } 

La build aggiungerà anche lo stub jar nel nostro repository Maven locale in modo che possa essere utilizzato dal nostro consumatore.

Gli stub saranno presenti nella cartella di output sotto stubs / mapping / .

3. Consumatore - Lato cliente

Il lato consumer del nostro CDC consumerà gli stub generati dal lato produttore tramite l'interazione HTTP per mantenere il contratto, quindi qualsiasi modifica sul lato produttore romperebbe il contratto .

Aggiungeremo BasicMathController, che effettuerà una richiesta HTTP per ottenere la risposta dagli stub generati:

@RestController public class BasicMathController { @Autowired private RestTemplate restTemplate; @GetMapping("/calculate") public String checkOddAndEven(@RequestParam("number") Integer number) { HttpHeaders httpHeaders = new HttpHeaders(); httpHeaders.add("Content-Type", "application/json"); ResponseEntity responseEntity = restTemplate.exchange( "//localhost:8090/validate/prime-number?number=" + number, HttpMethod.GET, new HttpEntity(httpHeaders), String.class); return responseEntity.getBody(); } }

3.1. Le dipendenze di Maven

Per il nostro consumatore, dovremo aggiungere le dipendenze spring-cloud-contract-wiremock e spring-cloud-contract-stub-runner :

 org.springframework.cloud spring-cloud-contract-wiremock 2.1.1.RELEASE test   org.springframework.cloud spring-cloud-contract-stub-runner 2.1.1.RELEASE test  

3.2. Configurazione lato consumatore

Ora è il momento di configurare il nostro stub runner, che informerà il nostro consumatore degli stub disponibili nel nostro repository Maven locale:

@RunWith(SpringRunner.class) @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.MOCK) @AutoConfigureMockMvc @AutoConfigureJsonTesters @AutoConfigureStubRunner( stubsMode = StubRunnerProperties.StubsMode.LOCAL, ids = "com.baeldung.spring.cloud:spring-cloud-contract-producer:+:stubs:8090") public class BasicMathControllerIntegrationTest { @Autowired private MockMvc mockMvc; @Test public void given_WhenPassEvenNumberInQueryParam_ThenReturnEven() throws Exception { mockMvc.perform(MockMvcRequestBuilders.get("/calculate?number=2") .contentType(MediaType.APPLICATION_JSON)) .andExpect(status().isOk()) .andExpect(content().string("Even")); } }

Tieni presente che la proprietà ids dell'annotazione @AutoConfigureStubRunner specifica:

  • com.baeldung.spring.cloud : il groupId del nostro artefatto
  • spring-cloud-contract-producer - the artifactId of the producer stub jar
  • 8090 : la porta su cui verranno eseguiti gli stub generati

4. Quando il contratto viene interrotto

Se apportiamo modifiche dal lato produttore che hanno un impatto diretto sul contratto senza aggiornare il lato consumatore, ciò può comportare il fallimento del contratto.

Ad esempio, supponiamo di dover modificare l' URI della richiesta di EvenOddController in / validate / change / prime-number sul nostro lato produttore.

Se non informiamo il nostro consumatore di questa modifica, il consumatore invierà comunque la sua richiesta all'URI / validate / prime-number , e i casi di test lato consumatore lanceranno org.springframework.web.client.HttpClientErrorException: 404 Not Found .

5. Riepilogo

Abbiamo visto come Spring Cloud Contract può aiutarci a mantenere i contratti tra un consumatore di servizi e un produttore in modo da poter distribuire nuovo codice senza alcuna preoccupazione di rompere i contratti.

E, come sempre, l'implementazione completa di questo tutorial può essere trovata su GitHub.