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