Une introduction au contrat Cloud Spring

Une introduction au contrat Cloud Spring

1. introduction

Spring Cloud Contract est un projet qui, en termes simples, nous aide à écrireConsumer-Driven Contracts (CDC).

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

Dans cet article rapide, nous allons explorer 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'unEvenOddController - qui indique simplement si le paramètrenumber 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 Maven

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


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

Et nous devrons configurerspring-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.example.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);
    }
}

In the /src/test/resources/contracts/ package, we’ll add the test stubs, comme celui-ci dans le fichiershouldReturnEvenWhenRequestParamIsEven.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 compilation,the plugin automatically generates a test class named ContractVerifierTest that extends our 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 sousstubs/mapping/.

3. Consommateur - Côté client

The consumer side of our CDC will consume stubs generated by the producer side via l'interaction HTTP pour maintenir le contrat, doncany changes on the producer side would break the contract.

Nous ajouteronsBasicMathController, 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(
          "http://localhost:8090/validate/prime-number?number=" + number,
          HttpMethod.GET,
          new HttpEntity<>(httpHeaders),
          String.class);

        return responseEntity.getBody();
    }
}

3.1. Les dépendances Maven

Pour notre consommateur, nous devrons ajouter les dépendancesspring-cloud-contract-wiremock etspring-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.example.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.example.spring.cloud - lesgroupId de notre artefact

  • spring-cloud-contract-producer - lesartifactId du pot de remplacement du producteur

  • 8090 - le port sur lequel les stubs générés seront exécutés

4. Lorsque le contrat est rompu

Si nous apportons des modifications du côté producteur qui ont un impact direct sur le contrat sans mettre à jour le côté consommateur,this can result in contract failure.

Par exemple, supposons que nous devions changer l'URI de la requêteEvenOddController en/validate/change/prime-number du côté producteur.

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

5. Sommaire

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

Et, comme toujours, l'implémentation complète de ce tutoriel peut être trouvéeover on GitHub.