Consumer Driven Contractsで
REST API/マイクロサービスをテスト
Toshiaki Maki (@making)
Sr. Solutions Architect @Pivotal
2016-06-08 M3 Tech Meetup
Who am I ?
•Toshiaki Maki (@making)
•https://blog.ik.am
•Sr. Solutions Architect
•Spring Framework enthusiast
Spring
Framework
徹底⼊⾨
(Coming
Soon?)
パーフェクト
Java EE
(Coming
Soon?)
Background
REST API/マイクロサービスの
テストどうしてますか?
図は
http://codearte.github.io/accurest
より
REST API/マイクロサービスの
テストどうしてますか?
•全サービスをDeploy?
•各サービスをMock化?
•テスト書いてない!?
全サービスをDeploy
•メリット
•本番環境をシミュレートできる
•サービス間が実際の通信
•デメリット
•全サービスのデプロイ/DBの準備が⼤変
•テストに時間がかかる
•デバッグし⾟い
各サービスをMock化
•メリット
•テストが速い
•環境セットアップ不要
•デメリット
•意味のないスタブを
作りがち
•本番環境では結局エラー
図は
http://codearte.github.io/accurest
より
テスト書いてない
Team BTeam A
現実世界(とあるトイレでの会話)
😕 😗
Team BTeam A
現実世界(とあるトイレでの会話)
😕 😗
おたくのチームのAPI
使ってテストするの
に時間かかったよ〜
Team BTeam A
現実世界(とあるトイレでの会話)
😕 😗
おたくのチームのAPI
使ってテストするの
に時間かかったよ〜
お、いいね。
でも、さっきAPI変え
ちゃったよ。
Team BTeam A
現実世界(とあるトイレでの会話)
😕 😗
おたくのチームのAPI
使ってテストするの
に時間かかったよ〜
お、いいね。
でも、さっきAPI変え
ちゃったよ。
😡
あの💢
Consumer Driven Contract
Consumer Driven Contract
(CDC)
• http://martinfowler.com/articles/consumerDrivenContracts.html
•アーキテクチャレベルのTDD
•"Contract"を通じて、ConsumerがProviderに期待内
容を共有する
•ProviderのContract違反をProvider側のテストで検出
Consumer ProviderContract
CDCの流れ
1. Consumerは期待するリクエスト/レスポンスをContractと
して定義
(DSLなどを利⽤)
2. ProviderはConsumerとContractを合意
(Pull Requestなどを利⽤)
3. ProviderはContractが守られていることを⽰すテストを実施
ConsumerはContractを前提にモック化してテストを実施
CDC⽤ライブラリ
• Pact-JVM
• https://github.com/DiUS/pact-jvm
• Pactは多⾔語(Ruby, .NET, Go, JS, Swift, etc)に対応
• Accurest
• https://github.com/Codearte/accurest
• Spring MVC向け
• Eureka, Spring Integration, Spring Cloud Streamにも対応している
• 今後Spring Cloud Contractという名前になるかも?
Accurest
Accurest
• DSLにGroovyを使⽤
• Provider向けにRestAssuredとJSON
Assertを使ったテストを⽣成
(JUnit or Spock)
• Consumer向けにWireMock⽤のスタブ
ファイルを⽣成
• Consumerのテストにスタブサーバーを
組み込める 図は
http://codearte.github.io/accurest
より
Contract
DSL
Consumer Provider
期待するリクエスト/
レスポンスを定義
Contract
DSL
テスト
ケース
スタブ
ファイル
Consumer Provider
RestAssured + JSON
Assertなコード⽣成
WireMock⽤のスタブ
ファイルを⽣成
Contract
DSL
テスト
ケース
スタブ
ファイル
Consumer Provider
RestAssured + JSON
Assertなコード⽣成
WireMock⽤のスタブ
ファイルを⽣成
Providerの破壊的な変更によ
るContract違反を検出可能
Accurest Maven Plugin
$ mvn accurest:convert
$ mvn accurest:generateTests
$ mvn accurest:run
$ mvn accurest:generateStubs
Provider向け
Consumer向け
テストコードを⽣成
WireMock形式に変換
モックサーバーを起動
モックのjarを⽣成
io.codearte.accurest.dsl.GroovyDsl.make {
request {
method 'GET'
urlPath '/foos'
}
response {
status 200
body("""[ {
"value" : 42
}, {
"value" : 100
} ]""")
headers {
header('Content-Type': 'application/json;charset=UTF-8')
}}}
Contract DSL
public class AccurestTest extends MvcTest{
@Test public void validate_requestForFoos() throws Exception {
MockMvcRequestSpecification request = given();
ResponseOptions response = given().spec(request).get("/foos");
assertThat(response.statusCode()).isEqualTo(200);
assertThat(response.header("Content-Type"))
.isEqualTo("application/json;charset=UTF-8");
DocumentContext parsedJson =
JsonPath.parse(response.getBody().asString());
assertThatJson(parsedJson).array().contains("value")
.isEqualTo(42);
assertThatJson(parsedJson).hasSize(2);
assertThatJson(parsedJson).array().contains("value")
.isEqualTo(100);
}}
テストコードを⽣成Provider側
public class AccurestTest extends MvcTest{
@Test public void validate_requestForFoos() throws Exception {
MockMvcRequestSpecification request = given();
ResponseOptions response = given().spec(request).get("/foos");
assertThat(response.statusCode()).isEqualTo(200);
assertThat(response.header("Content-Type"))
.isEqualTo("application/json;charset=UTF-8");
DocumentContext parsedJson =
JsonPath.parse(response.getBody().asString());
assertThatJson(parsedJson).array().contains("value")
.isEqualTo(42);
assertThatJson(parsedJson).hasSize(2);
assertThatJson(parsedJson).array().contains("value")
.isEqualTo(100);
}}
テストコードを⽣成
親クラスを
指定可能
Provider側
public class MvcTest {
@Before
public void setup() {
RestAssuredMockMvc
.standaloneSetup(
new FooController());
}
}
テストコードを⽣成Provider側
public class MvcTest {
@Before
public void setup() {
RestAssuredMockMvc
.standaloneSetup(
new FooController());
}
}
テストコードを⽣成Provider側
MockMVCでも
組み込みサーバーでも可
{
"uuid" : "d0c6c40b-47a8-4504-967d-b470f382d820",
"request" : {
"urlPath" : "/foos",
"method" : "GET"
},
"response" : {
"status" : 200,
"body" : "[{¥"value¥":42},{¥"value¥":100}]",
"headers" : {
"Content-Type" : "application/json;charset=UTF-8"
}
}
}
WireMock形式に変換Consumer側
$ mvn accurest:convert accurest:run
Consumer側
$ mvn accurest:generateStubs install or deploy
Consumer側
myprovider-0.0.1-SNAPSHOT-stubs.jarが
Mavenレポジトリにデプロイされる
Consumer側
Consumer
Provider
/foos /foos
{...}Got "{...}"
テストしたい
classifier
Consumer側
stubrunner.stubs.ids=com.example:myprovider:+:stubs:9999
src/test/resources/application.properties
groupId artifactId version
port
Consumer側
Consumer
Provider
/foos /foos
{...}Got "{...}"
通常の組み込みサーバーテスト
WireMockのスタブサーバー
Consumer側
Consumer
Provider
/foos /foos
{...}Got "{...}"
通常の組み込みサーバーテスト
WireMockのスタブサーバー
Consumer
だけでテストできる🍺
DEMO
https://github.com/making/microservices-accurest
CDCのメリット
• クライアントが求めているリクエスト/レスポンスを知れる
• Contractに違反があれば(APIの互換性が崩れたら)テスト
が失敗する
• テストが⾃動⽣成されるためメンテナンスしやすい
• 新しいAPIが追加されればすぐにモックサーバーに反映で
きる
CDC(Accurest)のデメリット
• Consumer側のテストが、 WireMock起動のため重くなり
やすい
→テストがちょっと億劫に
• まだ開発途上で、時々⽣成されたコードが壊れている
→issueをあげよう
References
• Consumer Driven Contractsについて
• http://martinfowler.com/articles/consumerDrivenContracts.html
• http://www.slideshare.net/MarcinGrzejszczak/stick-to-the-
rules-consumer-driven-contracts-201507-confitura
• http://toomuchcoding.com/blog/2016/04/30/accurest-and-
stub-runner-1-dot-1-0-dot-m3/
• CDC⽤ライブラリ
• http://codearte.github.io/accurest/
• サンプル
• https://github.com/making/microservices-accurest
• https://github.com/Codearte/accurest-samples
Announce
http://pivotal-japan.connpass.com
Pivotal Japan Technical Meetup
2016/06/29(Wed) 18:30-

Consumer Driven Contractsで REST API/マイクロサービスをテスト #m3tech