From 0a5be393e4f7ec90f07827b54f8612f50fe0b6a2 Mon Sep 17 00:00:00 2001 From: Andreas Gebhardt Date: Sun, 21 Jul 2024 18:06:11 +0200 Subject: [PATCH] =?UTF-8?q?add=20snippet=20for=20=C2=BBSpring=C2=AB=20(RES?= =?UTF-8?q?T)=20HTTP=20Interface?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit https://docs.spring.io/spring-framework/reference/integration/rest-clients.html#rest-http-interface --- spring-boot/pom.xml | 20 +++ .../agebhar1/snippets/HttpExchangeTest.kt | 100 ++++++++++++++ .../test/resources/wiremock/todo-stubs.json | 125 ++++++++++++++++++ 3 files changed, 245 insertions(+) create mode 100644 spring-boot/src/test/kotlin/io/github/agebhar1/snippets/HttpExchangeTest.kt create mode 100644 spring-boot/src/test/resources/wiremock/todo-stubs.json diff --git a/spring-boot/pom.xml b/spring-boot/pom.xml index d40c73c..9e4802b 100644 --- a/spring-boot/pom.xml +++ b/spring-boot/pom.xml @@ -22,8 +22,19 @@ 1.2.5 2.2.0 + 1.0-alpha-13 + + + + org.wiremock.integrations.testcontainers + wiremock-testcontainers-module + ${wiremock-testcontainers-module.version} + + + + @@ -58,6 +69,10 @@ org.springframework.boot spring-boot-starter-validation + + org.springframework.boot + spring-boot-starter-webflux + org.springframework.integration spring-integration-file @@ -114,6 +129,11 @@ postgresql test + + org.wiremock.integrations.testcontainers + wiremock-testcontainers-module + test + diff --git a/spring-boot/src/test/kotlin/io/github/agebhar1/snippets/HttpExchangeTest.kt b/spring-boot/src/test/kotlin/io/github/agebhar1/snippets/HttpExchangeTest.kt new file mode 100644 index 0000000..5b0cf98 --- /dev/null +++ b/spring-boot/src/test/kotlin/io/github/agebhar1/snippets/HttpExchangeTest.kt @@ -0,0 +1,100 @@ +package io.github.agebhar1.snippets + +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.extension.ExtendWith +import org.slf4j.Logger +import org.slf4j.LoggerFactory +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.boot.test.context.TestConfiguration +import org.springframework.context.annotation.Bean +import org.springframework.test.context.junit.jupiter.SpringExtension +import org.springframework.web.bind.annotation.PathVariable +import org.springframework.web.bind.annotation.RequestBody +import org.springframework.web.reactive.function.client.WebClient +import org.springframework.web.reactive.function.client.support.WebClientAdapter +import org.springframework.web.service.annotation.DeleteExchange +import org.springframework.web.service.annotation.GetExchange +import org.springframework.web.service.annotation.HttpExchange +import org.springframework.web.service.annotation.PostExchange +import org.springframework.web.service.invoker.HttpServiceProxyFactory +import org.testcontainers.containers.output.Slf4jLogConsumer +import org.testcontainers.junit.jupiter.Container +import org.testcontainers.junit.jupiter.Testcontainers +import org.wiremock.integrations.testcontainers.WireMockContainer + +data class ToDo( + val id: String, + val title: String, + val completed: Boolean +) + +@HttpExchange(url = "/todos", accept = ["application/json"], contentType = "application/json") +interface ToDosService { + @GetExchange + fun getAll(): List + + @GetExchange("/{id}") + fun getById(@PathVariable id: String): ToDo? + + @PostExchange + fun save(@RequestBody todo: ToDo): ToDo + + @DeleteExchange("/{id}") + fun delete(@PathVariable id: String) +} + +@ExtendWith(SpringExtension::class) +@Testcontainers +class HttpExchangeTest(@Autowired private val client: ToDosService) { + @Test + fun `(service) client should get all ToDo items`() { + assertThat(client.getAll()) + .containsExactly( + ToDo(id = "199d25ae-42e2-4cc8-9a97-571a46e25689", title = "Item #1", completed = true), + ToDo(id = "d2db635a-27e4-4a63-9d07-1a19aa52ace8", title = "Item #2", completed = false), + ToDo(id = "dc800874-bad2-4aeb-ada6-ca8f7630c6a2", title = "Item #3", completed = false) + ) + } + + @Test + fun `(service) client should get ToDo item by Id`() { + assertThat(client.getById("199d25ae-42e2-4cc8-9a97-571a46e25689")) + .isEqualTo( + ToDo(id = "199d25ae-42e2-4cc8-9a97-571a46e25689", title = "Item #1", completed = true) + ) + } + + @Test + fun `(service) client should save ToDo item`() { + val todo = ToDo(id = "34c93fba-da84-45f6-921a-9ab6322d9ee0", title = "Item #4", completed = false) + assertThat(client.save(todo)).isEqualTo(todo) + } + + @Test + fun `(service) client should delete ToDo item by Id`() { + client.delete("199d25ae-42e2-4cc8-9a97-571a46e25689") + } + + @TestConfiguration + class Configuration { + @Bean + fun client(): ToDosService { + val webClient = WebClient.builder().baseUrl(wm.baseUrl).build() + val factory = HttpServiceProxyFactory + .builderFor(WebClientAdapter.create(webClient)) + .build() + + return factory.createClient(ToDosService::class.java) + } + } + + companion object { + private val logger: Logger = LoggerFactory.getLogger(HttpExchangeTest::class.java) + + @Container + val wm: WireMockContainer = WireMockContainer("wiremock/wiremock:3.9.0-1") + .withLogConsumer(Slf4jLogConsumer(logger).withSeparateOutputStreams()) + .withMappingFromResource("todo", "wiremock/todo-stubs.json") + } +} diff --git a/spring-boot/src/test/resources/wiremock/todo-stubs.json b/spring-boot/src/test/resources/wiremock/todo-stubs.json new file mode 100644 index 0000000..3e33636 --- /dev/null +++ b/spring-boot/src/test/resources/wiremock/todo-stubs.json @@ -0,0 +1,125 @@ +{ + "mappings": [ + { + "id": "ff4d49aa-5e41-42fe-8a65-79ceb56f6558", + "request": { + "urlPath": "/todos", + "method": "GET" + }, + "response": { + "status": 200, + "jsonBody": [ + { + "id": "199d25ae-42e2-4cc8-9a97-571a46e25689", + "title": "Item #1", + "completed": true + }, + { + "id": "d2db635a-27e4-4a63-9d07-1a19aa52ace8", + "title": "Item #2", + "completed": false + }, + { + "id": "dc800874-bad2-4aeb-ada6-ca8f7630c6a2", + "title": "Item #3", + "completed": false + } + ], + "headers": { + "Content-Type": "application/json" + } + } + }, + { + "id": "44ba9592-01a7-4402-8cd1-04526b0e3d5f", + "request": { + "urlPath": "/todos/199d25ae-42e2-4cc8-9a97-571a46e25689", + "method": "GET" + }, + "response": { + "status": 200, + "jsonBody": { + "id": "199d25ae-42e2-4cc8-9a97-571a46e25689", + "title": "Item #1", + "completed": true + }, + "headers": { + "Content-Type": "application/json" + } + } + }, + { + "id": "22f08c71-5d3f-4d37-bcc8-8f994942f79c", + "request": { + "urlPath": "/todos/d2db635a-27e4-4a63-9d07-1a19aa52ace8", + "method": "GET" + }, + "response": { + "status": 200, + "jsonBody": { + "id": "d2db635a-27e4-4a63-9d07-1a19aa52ace8", + "title": "Item #2", + "completed": false + }, + "headers": { + "Content-Type": "application/json" + } + } + }, + { + "id": "0d464423-94f8-491b-969e-954aee74d35b", + "request": { + "urlPath": "/todos/dc800874-bad2-4aeb-ada6-ca8f7630c6a2", + "method": "GET" + }, + "response": { + "status": 200, + "jsonBody": { + "id": "dc800874-bad2-4aeb-ada6-ca8f7630c6a2", + "title": "Item #3", + "completed": false + }, + "headers": { + "Content-Type": "application/json" + } + } + }, + { + "id": "e5a243d5-e5d4-4230-9784-8a301492e8c0", + "request": { + "urlPath": "/todos", + "method": "POST", + "bodyPatterns": [ + { + "equalToJson": { + "id": "34c93fba-da84-45f6-921a-9ab6322d9ee0", + "title": "Item #4", + "completed": false + } + } + ] + }, + "response": { + "status": 201, + "jsonBody": { + "id": "34c93fba-da84-45f6-921a-9ab6322d9ee0", + "title": "Item #4", + "completed": false + }, + "headers": { + "Content-Type": "application/json" + } + } + }, + { + "id": "17e883e8-9147-4483-8945-36105a0c1304", + "request": { + "urlPath": "/todos/199d25ae-42e2-4cc8-9a97-571a46e25689", + "method": "DELETE" + }, + "response": { + "status": 204 + } + } + ] +} \ No newline at end of file