Une introduction au contrat Spring Cloud

1. Introduction

Spring Cloud Contract est un projet qui, en termes simples, nous aide à rédiger des contrats axés sur les consommateurs (CDC).

Cela garantit le contrat entre un producteur et un consommateur , dans un système distribué - pour les interactions basées sur HTTP et basées sur des messages.

Dans cet article rapide, nous explorerons l'écriture de cas de test côté producteur et consommateur pour Spring Cloud Contract via une interaction HTTP.

2. Producteur - Côté serveur

Nous allons écrire un CDC côté producteur, sous la forme d'un EvenOddController - qui indique simplement si le paramètre number est pair ou impair:

@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. Dépendances de Maven

Pour notre côté producteur, nous aurons besoin de la dépendance spring-cloud-starter-contract-verifier :

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

Et nous devrons configurer spring-cloud-contract-maven-plugin avec le nom de notre classe de test de base, que nous décrirons dans la section suivante:

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

2.2. Configuration côté producteur

Nous devons ajouter une classe de base dans le package de test qui charge notre contexte 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); } }

Dans le package / src / test / resources / contracts / , nous ajouterons les stubs de test , tels que celui-ci dans le fichier 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 } } 

Lorsque nous exécutons la construction, le plugin génère automatiquement une classe de test nommée ContractVerifierTest qui étend notre BaseTestClass et la place dans / target / generated-test-sources / contracts / .

Les noms des méthodes de test sont dérivés du préfixe « validate_» concaténé avec les noms de nos stubs de test Groovy. Pour le fichier Groovy ci-dessus, le nom de la méthode générée sera «validate_shouldReturnEvenWhenRequestParamIsEven» .

Jetons un coup d'œil à cette classe de test générée automatiquement:

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 compilation ajoutera également le stub jar dans notre référentiel Maven local afin qu'il puisse être utilisé par notre consommateur.

Les stubs seront présents dans le dossier de sortie sous stubs / mapping / .

3. Consommateur - Côté client

Le côté consommateur de notre CDC consommera les stubs générés par le côté producteur via l'interaction HTTP pour maintenir le contrat, de sorte que tout changement du côté producteur romprait le contrat .

Nous ajouterons BasicMathController, qui fera une requête HTTP pour obtenir la réponse des stubs générés:

@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. Les dépendances de Maven

Pour notre consommateur, nous devrons ajouter les dépendances spring-cloud-contract-wiremock et 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. Configuration côté consommateur

Il est maintenant temps de configurer notre stub runner, qui informera notre consommateur des stubs disponibles dans notre référentiel Maven local:

@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")); } }

Notez que la propriété ids de l' annotation @AutoConfigureStubRunner spécifie:

  • com.baeldung.spring.cloud - le groupId de notre artefact
  • Spring-Cloud-Contract-Producer - l' artefactId du pot de stub producteur
  • 8090 - le port sur lequel les stubs générés s'exécuteront

4. Lorsque le contrat est rompu

Si nous apportons des modifications du côté du producteur qui ont un impact direct sur le contrat sans mettre à jour le côté du consommateur, cela peut entraîner l'échec du contrat.

Par exemple, supposons que nous changions l' URI de la requête EvenOddController en / validate / change / prime-number de notre côté producteur.

Si nous ne parvenons pas à informer notre consommateur de ce changement, le consommateur enverra toujours sa demande à l' URI / validate / premier-number , et les cas de test côté consommateur lanceront org.springframework.web.client.HttpClientErrorException: 404 Not Found .

5. Résumé

Nous avons vu comment Spring Cloud Contract peut nous aider à maintenir des contrats entre un consommateur de service et un producteur afin que nous puissions publier un nouveau code sans craindre de rompre les contrats.

Et, comme toujours, la mise en œuvre complète de ce tutoriel peut être trouvée sur GitHub.