Spring Cloud Contract簡介

1.簡介

簡而言之, Spring Cloud Contract是一個項目,可以幫助我們編寫“消費者驅動的合同”(CDC)

這樣可以確保在分佈式系統中, ProducerConsumer之間的合同既基於HTTP交互又基於消息交互。

在這篇快速的文章中,我們將探索通過HTTP交互為Spring Cloud Contract編寫生產方和消費者方測試用例。

2.生產者–服務器端

我們將以EvenOddController的形式編寫一個生產方CDC –它只是告訴number參數是偶數還是奇數:

@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。 Maven依賴

對於我們的生產者端,我們需要spring-cloud-starter-contract-verifier依賴項:

<dependency>

 <groupId>org.springframework.cloud</groupId>

 <artifactId>spring-cloud-starter-contract-verifier</artifactId>

 <version>2.1.1.RELEASE</version>

 <scope>test</scope>

 </dependency>

而且,我們需要使用基本測試類的名稱來配置spring-cloud-contract-maven-plugin ,我們將在下一部分中對其進行描述:

<plugin>

 <groupId>org.springframework.cloud</groupId>

 <artifactId>spring-cloud-contract-maven-plugin</artifactId>

 <version>2.1.1.RELEASE</version>

 <extensions>true</extensions>

 <configuration>

 <baseClassForTests>

 com.baeldung.spring.cloud.springcloudcontractproducer.BaseTestClass

 </baseClassForTests>

 </configuration>

 </plugin>

2.2。生產者端設置

我們需要在測試包中添加一個基類來加載我們的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);

 }

 }

/src/test/resources/contracts/包中,我們將在文件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

 }

 }

當我們運行構建時,**插件會自動生成一個名為ContractVerifierTest的測試類,該類擴展了BaseTestClass**並將其放在/target/generated-test-sources/contracts/

測試方法的名稱是從前綴“ validate_”派生而來的,該前綴與我們的Groovy測試存根的名稱連接在一起。對於上述Groovy文件,生成的方法名稱將為“validate_shouldReturnEvenWhenRequestParamIsEven”

讓我們看一下這個自動生成的測試類:

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

 }

該構建還將在我們的本地Maven存儲庫中添加存根jar,以便我們的使用者可以使用它。

存根將出現在stubs/mapping/下的輸出文件夾中。

3.消費者–客戶端

CDC的消費者方將使用生產者方通過HTTP交互生成的存根來維護合同,因此,生產者方的任何更改都將破壞合同

我們將添加BasicMathController,它將發出HTTP請求以從生成的存根中獲取響應:

@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<String> responseEntity = restTemplate.exchange(

 "http://localhost:8090/validate/prime-number?number=" + number,

 HttpMethod.GET,

 new HttpEntity<>(httpHeaders),

 String.class);



 return responseEntity.getBody();

 }

 }

3.1。 Maven的依賴

對於我們的消費者,我們需要添加spring-cloud-contract-wiremockspring-cloud-contract-stub-runner依賴項:

<dependency>

 <groupId>org.springframework.cloud</groupId>

 <artifactId>spring-cloud-contract-wiremock</artifactId>

 <version>2.1.1.RELEASE</version>

 <scope>test</scope>

 </dependency>

 <dependency>

 <groupId>org.springframework.cloud</groupId>

 <artifactId>spring-cloud-contract-stub-runner</artifactId>

 <version>2.1.1.RELEASE</version>

 <scope>test</scope>

 </dependency>

3.2。消費者方設置

現在是時候配置我們的存根運行程序,它將通知我們的使用者我們本地Maven存儲庫中的可用存根:

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

 }

 }

請注意, @AutoConfigureStubRunner批註的ids屬性指定:

  • com.baeldung.spring.cloud —我們工件的groupId
  • spring-cloud-contract-producer —生產者存根罐的artifactId
  • 8090 —生成的存根將在其上運行的端口

4.當合同失敗時

如果我們在生產方進行任何直接影響合同的更改而不更新消費者方,則可能導致合同失敗。

例如,假設我們要在生產者端將EvenOddController請求URI /validate/change/prime-number/validate/change/prime-number

如果我們無法將此更改通知消費者,則消費者仍將其請求發送到/validate/prime-number URI,並且消費者方測試用例將拋出org.springframework.web.client.HttpClientErrorException: 404 Not Found

5.總結

我們已經看到了Spring Cloud Contract如何能夠幫助我們維護服務使用者和生產者之間的合同,以便我們可以推出新代碼而不必擔心違反合同。

而且,與往常一樣,可以在GitHub上找到本教程的完整實現。