2021-12-15 開催 JJUGナイトセミナー「おうちで!ビール片手にLT大会!」でお話した資料です https://jjug.doorkeeper.jp/events/129543
αʔϏεؒΛςετ͢ΔϑϨʔϜϫʔΫूαΠϘζגࣜձ্ࣾԬ ਅ (@ueokande)2021-12-15. JJUGφΠτηϛφʔʮ͓͏ͪͰʂϏʔϧยखʹLTେձʂʯ
View Slide
Me• αΠϘζגࣜձࣾ• άϩʔόϧ͚B2BαʔϏεͷ όοΫΤϯυΛ࡞͍ͬͯ·͢• ݱͰKotlin + Spring Boot͕ϝΠϯ2
ຊͷ͓• ෳαʔϏε͕ϓϩμΫτΛߏ͢Δ࣌ʢMicroservice, SOA, …ʣ• αʔϏεؒͷΓऔΓΛͲ͏ͬͯςετ͢Δ͔ʁ• REST API௨৴ͷݕূʹཱͭϑϨʔϜϫʔΫΛհ3αʔϏε" αʔϏε#)551ϦΫΤετ+40/
հϑϨʔϜϫʔΫ• Mockito … ΫϥΠΞϯτͷ࣮ͱϏδωεϩδοΫΛ• WireMock … ϞοΫαʔόʔΛཱͯͯΫϥΠΞϯτ࣮Λςετ• Pact … αʔϏεؒ௨৴ͷAPIͷมߋΛݕ4
Mockito• JavaͷΠϯλʔϑΣΠεɺΫϥεΛϞοΫ• ΫϥΠΞϯτ࣮ΛϞοΫͯ͠ϏδωεϩδοΫ୯ମΛςετ5αʔϏε" αʔϏε#)551ϦΫΤετ+40/͜͜ΒΜͷςετ
Mockito͔͍͔ͭͨ• MockitoΛͬͯΫϥΠΞϯτ࣮ͷϞοΫΛهड़• ϩδοΫ୯ମΛςε͠ɺΫϥΠΞϯτར༻͞Ε͔ͨݕূ6val client = mock()whenever(client.getItems()).thenReturn(Arrays.asList(item1, …))val logic = ItemLogic(bClient)val actual = sut.calc()assertEquals("...", actual)verify(bClient, times(1)).getItems()ϞοΫΫϥΠΞϯτͷ࡞ϞοΫΫϥΠΞϯτΛͯ͠ ରΫϥεʢϏδωεϩδοΫʣͷݕূMockΫϥΠΞϯτ͕ར༻͞Ε͔ͨݕূ※ Kotlinͷίʔυྫ
WireMock• ϞοΫͷϨεϙϯεΛฦ͢HTTPαʔόʔΛཱͯΔ• HTTPαʔόʔϦΫΤετΛهͯ͠ظ͢ΔϦΫΤετ͔ݕূ7αʔϏε" αʔϏε#)551ϦΫΤετ+40/͜͜ΒΜͷςετ
WireMock͔͍͔ͭͨ• ϦΫΤετʹର͢ΔϨεϙϯεΛఆٛͯ͠ϞοΫαʔόʔΛىಈ• ΫϥΠΞϯτ࣮ͱɺൃߦ͞ΕͨϦΫΤετ͕ਖ਼͍͔͠ݕূ8val server = WireMockServer(12345)server.stubFor(WireMock.post(“/v1/item/1“).willReturn(aResponse().withBody("{ ... }”)))server.start()val client = BClientImpl("127.0.0.1", 12345)List item = bClient.getItem(1L)assertEquals(10, actual.size)server.verify(postRequestedFor(urlPathEqualTo("/v1/items")).withRequestBody(equalToJson("{ ... }")) )ϞοΫαʔόʔͷ࡞ͱىಈϦΫΤετͷൃߦͱΓͷݕূ࣮ߦ͞ΕͨϦΫΤετͷݕূ
Pact/Pact JVM• αʔϏεؒͷAPIͷৼΔ͍Λ୲อ͢ΔϑϨʔϜϫʔΫ1. ίϯγϡʔϚʔɺར༻APIͷظ͢ΔϦΫΤετɾϨεϙϯεʢܖʣΛఆٛ2. ίϯγϡʔϚʔɺΫϥΠΞϯτ࣮͕ܖͷ௨ΓʹৼΔ͏͔ݕূ3. ϓϩόΠμʔɺܖͷϦΫΤετʹର͠ظͷϨεϙϯεΛฦ͔͢ݕূ9αʔϏε" $POTVNFSαʔϏε# 1SPWJFSܖϞοΫ ϞοΫϦΫΤετϨεϙϯεϦΫΤετϨεϙϯε࡞ ݕূᶃᶄ ᶅjson
Pact JVM͍ํʛܖͷఆٛ• ίϯγϡʔϚʔଆ͕ར༻͢ΔAPIͷಈ࡞ΛDLSͰఆٛ10@Pact(provider = “service-b", consumer = “service-a")fun getItemPact(builder: PactDslWithProvider): V4Pact {builder.given("item1 and item2 exist") .uponReceiving("get all items").method(“GET”).matchPath(“/v1/item/1“).willRespondWith().status(200).body(LambdaDsl.newJsonBody { it ->it.`object` {it.stringType(“name", “pen")it.numberType(“cost", 100)}})}ϦΫΤετʹର͢Δظ͢ΔϨεϙϯεΛఆٛ
Pact JVM͍ํʛίϯγϡʔϚʔͷݕূ• هड़ͨ͠ܖʹରͯ͠ΫϥΠΞϯτ࣮͕ਖ਼͘͠ৼΔ͏͔ݕূ• ϞοΫαʔόʔ͕ىಈͯ͠HTTPϦΫΤετΛهɾݕূ11@Test@PactTestFor(providerName = “service-b", pactMethod = "getItemPact")fun getItem(mockServer: MockServer) {val client = BClientImpl(mockServer.getUrl())val item = client.getItems(1L)assertEquals("pen", item.name)assertEquals(100, item.cost)}
Pact JVM͍ํʛϓϩόΠμʔͷݕূ• ϓϩόΠμʔ͕ܖ௨ΓͷϨεϙϯεΛฦ͔͢ݕূ• ܖΛͱʹPactϑϨʔϜϫʔΫ͕ࣗಈͰςετ12@ExtendWith(SpringExtension::class)@Provider(“service-b")@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)class ContractTest {@TestTemplate@ExtendWith(PactVerificationInvocationContextProvider::class)fun testTemplate(pact: Pact, interaction: Interaction, context: PactVerificationContext) {context.verifyInteraction()}}
·ͱΊ• ਖ਼͍͠ϨΠϠʔͰϞοΫͯ͠ਖ਼͘͠ςετ• Mockito … αʔϏεͷΠϯλʔϑΣΠεͱ࣮ΫϥεΛ• WireMock … ϦΫΤετɾϨεϙϯεΛϞοΫ• Pact … ৗʹมԽ͠ଓ͚ΔαʔϏεؒͷAPIͰഁյతมߋ͕ͳ͍͔νΣοΫ• ͍͠ϨΠϠʔςετɾࣗಈԽͯ҆͠৺ײΛಘΑ͏13