diff --git a/spring-test/src/main/kotlin/org/springframework/test/web/reactive/server/WebTestClientExtensions.kt b/spring-test/src/main/kotlin/org/springframework/test/web/reactive/server/WebTestClientExtensions.kt index e55735c6489..5cdc763fd6a 100644 --- a/spring-test/src/main/kotlin/org/springframework/test/web/reactive/server/WebTestClientExtensions.kt +++ b/spring-test/src/main/kotlin/org/springframework/test/web/reactive/server/WebTestClientExtensions.kt @@ -17,6 +17,7 @@ package org.springframework.test.web.reactive.server import org.reactivestreams.Publisher +import org.springframework.test.util.AssertionErrors.assertEquals import org.springframework.test.web.reactive.server.WebTestClient.* /** @@ -30,14 +31,55 @@ inline fun > RequestBodySpec.body(publisher: S = body(publisher, T::class.java) /** - * Extension for [ResponseSpec.expectBody] providing a `expectBody()` variant. + * Extension for [ResponseSpec.expectBody] providing an `expectBody()` variant and + * a workaround for [KT-5464](https://youtrack.jetbrains.com/issue/KT-5464) which + * prevents to use `WebTestClient.BodySpec` in Kotlin. * * @author Sebastien Deleuze * @since 5.0 */ @Suppress("EXTENSION_SHADOWED_BY_MEMBER") -inline fun ResponseSpec.expectBody(): BodySpec = - expectBody(B::class.java) +inline fun ResponseSpec.expectBody(): KotlinBodySpec = + expectBody(B::class.java).returnResult().let { + object : KotlinBodySpec { + + override fun isEqualTo(expected: B): KotlinBodySpec = it + .assertWithDiagnostics({ assertEquals("Response body", expected, it.responseBody) }) + .let { this } + + override fun consumeWith(consumer: (EntityExchangeResult) -> Unit): KotlinBodySpec = + it + .assertWithDiagnostics({ consumer.invoke(it) }) + .let { this } + + override fun returnResult(): EntityExchangeResult = it + } + } + +/** + * Kotlin compliant `WebTestClient.BodySpec` for expectations on the response body decoded + * to a single Object, see [KT-5464](https://youtrack.jetbrains.com/issue/KT-5464) for + * more details. + * @since 5.0.6 + */ +interface KotlinBodySpec { + + /** + * Assert the extracted body is equal to the given value. + */ + fun isEqualTo(expected: B): KotlinBodySpec + + /** + * Assert the exchange result with the given consumer. + */ + fun consumeWith(consumer: (EntityExchangeResult) -> Unit): KotlinBodySpec + + /** + * Exit the chained API and return an `ExchangeResult` with the + * decoded response content. + */ + fun returnResult(): EntityExchangeResult +} /** * Extension for [ResponseSpec.expectBodyList] providing a `expectBodyList()` variant. diff --git a/spring-test/src/test/kotlin/org/springframework/test/web/reactive/server/WebTestClientExtensionsTests.kt b/spring-test/src/test/kotlin/org/springframework/test/web/reactive/server/WebTestClientExtensionsTests.kt index 13dc600984f..d334ec58900 100644 --- a/spring-test/src/test/kotlin/org/springframework/test/web/reactive/server/WebTestClientExtensionsTests.kt +++ b/spring-test/src/test/kotlin/org/springframework/test/web/reactive/server/WebTestClientExtensionsTests.kt @@ -19,12 +19,15 @@ package org.springframework.test.web.reactive.server import com.nhaarman.mockito_kotlin.mock import com.nhaarman.mockito_kotlin.times import com.nhaarman.mockito_kotlin.verify +import org.junit.Assert.assertEquals import org.junit.Test import org.junit.runner.RunWith import org.mockito.Answers import org.mockito.Mock import org.mockito.junit.MockitoJUnitRunner import org.reactivestreams.Publisher +import org.springframework.web.reactive.function.server.ServerResponse.* +import org.springframework.web.reactive.function.server.router /** * Mock object based tests for [WebTestClient] Kotlin extensions @@ -54,6 +57,30 @@ class WebTestClientExtensionsTests { verify(responseSpec, times(1)).expectBody(Foo::class.java) } + @Test + fun `KotlinBodySpec#isEqualTo`() { + WebTestClient + .bindToRouterFunction( router { GET("/") { ok().syncBody("foo") } } ) + .build() + .get().uri("/").exchange().expectBody().isEqualTo("foo") + } + + @Test + fun `KotlinBodySpec#consumeWith`() { + WebTestClient + .bindToRouterFunction( router { GET("/") { ok().syncBody("foo") } } ) + .build() + .get().uri("/").exchange().expectBody().consumeWith { assertEquals("foo", it.responseBody) } + } + + @Test + fun `KotlinBodySpec#returnResult`() { + WebTestClient + .bindToRouterFunction( router { GET("/") { ok().syncBody("foo") } } ) + .build() + .get().uri("/").exchange().expectBody().returnResult().apply { assertEquals("foo", responseBody) } + } + @Test fun `ResponseSpec#expectBodyList with reified type parameters`() { responseSpec.expectBodyList() diff --git a/src/docs/asciidoc/languages/kotlin.adoc b/src/docs/asciidoc/languages/kotlin.adoc index 6f9d29722ab..ffaf4b98699 100644 --- a/src/docs/asciidoc/languages/kotlin.adoc +++ b/src/docs/asciidoc/languages/kotlin.adoc @@ -663,13 +663,11 @@ class SpecificationLikeTests { [[kotlin-webtestclient-issue]] ==== `WebTestClient` type inference issue in Kotlin -`WebTestClient` is not usable yet in Kotlin due to a -https://youtrack.jetbrains.com/issue/KT-5464[type inference issue] which is -expected to be fixed as of Kotlin 1.3. You can watch -https://jira.spring.io/browse/SPR-16057[SPR-16057] for up-to-date information. Meanwhile, -the proposed alternative is to use directly `WebClient` with its Reactor and Spring Kotlin -extensions to perform integration tests on an embedded WebFlux server. +Due to a https://youtrack.jetbrains.com/issue/KT-5464[type inference issue], make sure to +use Kotlin `expectBody` extension (like `.expectBody().isEqualTo("foo")`) since it +provides a workaround for the Kotlin issue with the Java API. +See also the related https://jira.spring.io/browse/SPR-16057[SPR-16057] issue.