Spring Cloudコントラクトの紹介
1. 前書き
Spring Cloud Contractは、簡単に言えば、Consumer-Driven Contracts (CDC)の作成に役立つプロジェクトです。
これにより、分散システムでのProducerとConsumerの間のコントラクトが、HTTPベースとメッセージベースの両方の対話で保証されます。
この簡単な記事では、HTTPインタラクションを介したSpring CloudContractのプロデューサー側とコンシューマー側のテストケースの作成について説明します。
2. プロデューサー–サーバー側
プロデューサー側のCDCをEvenOddControllerの形式で記述します。これは、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の依存関係が必要です。
org.springframework.cloud
spring-cloud-starter-contract-verifier
2.1.1.RELEASE
test
そして、次のセクションで説明する基本テストクラスの名前でspring-cloud-contract-maven-pluginを構成する必要があります。
org.springframework.cloud
spring-cloud-contract-maven-plugin
2.1.1.RELEASE
true
com.example.spring.cloud.springcloudcontractproducer.BaseTestClass
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);
}
}
In the /src/test/resources/contracts/ package, we’ll add the test stubs、たとえばファイル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
}
}
ビルドを実行すると、the plugin automatically generates a test class named ContractVerifierTest that extends our BaseTestClassが/target/generated-test-sources/contracts/に配置されます。
テストメソッドの名前は、Groovyテストスタブの名前と連結されたプレフィックス「validate」_から派生しています。 上記の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. 消費者–クライアント側
コントラクトを維持するためのHTTPインタラクションを介したThe consumer side of our CDC will consume stubs generated by the producer side、つまりany changes on the producer side would break the contract。
生成されたスタブから応答を取得するためのHTTPリクエストを作成するBasicMathController,を追加します。
@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. Mavenの依存関係
コンシューマーの場合、spring-cloud-contract-wiremockと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. 消費者側のセットアップ
次に、スタブランナーを構成します。これにより、ローカルのMavenリポジトリで使用可能なスタブがコンシューマーに通知されます。
@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"));
}
}
@AutoConfigureStubRunnerアノテーションのidsプロパティは以下を指定することに注意してください。
-
com.example.spring.cloud —アーティファクトのgroupId
-
spring-cloud-contract-producer —プロデューサースタブjarのartifactId
-
8090 —生成されたスタブが実行されるポート
4. 契約が破られたとき
コンシューマー側を更新せずに契約に直接影響するプロデューサー側の変更を行う場合、this can result in contract failure.
たとえば、プロデューサー側でEvenOddControllerリクエストURIを/validate/change/prime-numberに変更するとします。
この変更をコンシューマーに通知しなかった場合でも、コンシューマーはリクエストを/validate/prime-number URIに送信し、コンシューマー側のテストケースはorg.springframework.web.client.HttpClientErrorException: 404 Not Foundをスローします。
5. 概要
Spring Cloud Contractが、サービスコンシューマーとプロデューサーの間のコントラクトを維持し、コントラクトを壊すことを心配せずに新しいコードをプッシュできるようにする方法を見てきました。
そして、いつものように、このチュートリアルの完全な実装はover on GitHubにあります。