diff --git a/spring-boot-project/spring-boot-parent/pom.xml b/spring-boot-project/spring-boot-parent/pom.xml index 5815ed0a563..54cf00fe003 100644 --- a/spring-boot-project/spring-boot-parent/pom.xml +++ b/spring-boot-project/spring-boot-parent/pom.xml @@ -53,6 +53,25 @@ log4j 1.2.17 + + com.nhaarman + mockito-kotlin + 1.5.0 + + + org.jetbrains.kotlin + kotlin-stdlib + + + org.jetbrains.kotlin + kotlin-reflect + + + org.mockito + mockito-core + + + com.squareup.okhttp okhttp diff --git a/spring-boot-project/spring-boot-test/pom.xml b/spring-boot-project/spring-boot-test/pom.xml index 2c98894a124..32f2f161787 100644 --- a/spring-boot-project/spring-boot-test/pom.xml +++ b/spring-boot-project/spring-boot-test/pom.xml @@ -115,6 +115,16 @@ htmlunit true + + org.jetbrains.kotlin + kotlin-stdlib + true + + + org.jetbrains.kotlin + kotlin-reflect + true + javax.json @@ -182,6 +192,11 @@ junit-jupiter-api test + + com.nhaarman + mockito-kotlin + test + @@ -196,6 +211,66 @@ + + org.jetbrains.kotlin + kotlin-maven-plugin + + + compile + compile + + compile + + + + ${project.basedir}/src/main/kotlin + ${project.basedir}/src/main/java + + + + + test-compile + test-compile + + test-compile + + + + ${project.basedir}/src/test/kotlin + ${project.basedir}/src/test/java + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + + default-compile + none + + + default-testCompile + none + + + java-compile + compile + + compile + + + + java-test-compile + test-compile + + testCompile + + + + diff --git a/spring-boot-project/spring-boot-test/src/main/kotlin/org/springframework/boot/test/web/client/TestRestTemplateExtensions.kt b/spring-boot-project/spring-boot-test/src/main/kotlin/org/springframework/boot/test/web/client/TestRestTemplateExtensions.kt new file mode 100644 index 00000000000..48848462338 --- /dev/null +++ b/spring-boot-project/spring-boot-test/src/main/kotlin/org/springframework/boot/test/web/client/TestRestTemplateExtensions.kt @@ -0,0 +1,234 @@ +/* + * Copyright 2012-2017 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.boot.test.web.client + +import org.springframework.core.ParameterizedTypeReference +import org.springframework.http.HttpEntity +import org.springframework.http.HttpMethod +import org.springframework.http.RequestEntity +import org.springframework.http.ResponseEntity +import org.springframework.web.client.RestClientException +import java.net.URI + +/** + * Extension for [TestRestTemplate.getForObject] avoiding specifying the type + * parameter thanks to Kotlin reified type parameters. + * + * @author Sebastien Deleuze + * @since 2.0.0 + */ +@Throws(RestClientException::class) +inline fun TestRestTemplate.getForObject(url: String, vararg uriVariables: Any): T? = + getForObject(url, T::class.java, *uriVariables) + +/** + * Extension for [TestRestTemplate.getForObject] avoiding specifying the type + * parameter thanks to Kotlin reified type parameters. + * + * @author Sebastien Deleuze + * @since 2.0.0 + */ +@Throws(RestClientException::class) +inline fun TestRestTemplate.getForObject(url: String, uriVariables: Map): T? = + getForObject(url, T::class.java, uriVariables) + +/** + * Extension for [TestRestTemplate.getForObject] avoiding specifying the type parameter + * thanks to Kotlin reified type parameters. + * + * @author Sebastien Deleuze + * @since 2.0.0 + */ +@Throws(RestClientException::class) +inline fun TestRestTemplate.getForObject(url: URI): T? = + getForObject(url, T::class.java) + +/** + * Extension for [TestRestTemplate.getForEntity] avoiding requiring the type parameter + * thanks to Kotlin reified type parameters. + * + * @author Sebastien Deleuze + * @since 2.0.0 + */ +@Throws(RestClientException::class) +inline fun TestRestTemplate.getForEntity(url: URI): ResponseEntity = + getForEntity(url, T::class.java) + +/** + * Extension for [TestRestTemplate.getForEntity] avoiding requiring the type parameter + * thanks to Kotlin reified type parameters. + * + * @author Sebastien Deleuze + * @since 2.0.0 + */ +@Throws(RestClientException::class) +inline fun TestRestTemplate.getForEntity(url: String, vararg uriVariables: Any): ResponseEntity = + getForEntity(url, T::class.java, *uriVariables) + +/** + * Extension for [TestRestTemplate.getForEntity] avoiding requiring the type parameter + * thanks to Kotlin reified type parameters. + * + * @author Sebastien Deleuze + * @since 2.0.0 + */ +@Throws(RestClientException::class) +inline fun TestRestTemplate.getForEntity(url: String, uriVariables: Map): ResponseEntity = + getForEntity(url, T::class.java, uriVariables) + +/** + * Extension for [TestRestTemplate.patchForObject] avoiding specifying the type parameter + * thanks to Kotlin reified type parameters. + * + * @author Sebastien Deleuze + * @since 2.0.0 + */ +@Throws(RestClientException::class) +inline fun TestRestTemplate.patchForObject(url: String, request: Any, vararg uriVariables: Any): T? = + patchForObject(url, request, T::class.java, *uriVariables) + +/** + * Extension for [TestRestTemplate.patchForObject] avoiding specifying the type parameter + * thanks to Kotlin reified type parameters. + * + * @author Sebastien Deleuze + * @since 2.0.0 + */ +@Throws(RestClientException::class) +inline fun TestRestTemplate.patchForObject(url: String, request: Any, uriVariables: Map): T? = + patchForObject(url, request, T::class.java, uriVariables) + +/** + * Extension for [TestRestTemplate.patchForObject] avoiding specifying the type parameter + * thanks to Kotlin reified type parameters. + * + * @author Sebastien Deleuze + * @since 2.0.0 + */ +@Throws(RestClientException::class) +inline fun TestRestTemplate.patchForObject(url: URI, request: Any): T? = + patchForObject(url, request, T::class.java) + +/** + * Extension for [TestRestTemplate.postForObject] avoiding specifying the type parameter + * thanks to Kotlin reified type parameters. + * + * @author Sebastien Deleuze + * @since 2.0.0 + */ +@Throws(RestClientException::class) +inline fun TestRestTemplate.postForObject(url: String, request: Any, vararg uriVariables: Any): T? = + postForObject(url, request, T::class.java, *uriVariables) + +/** + * Extension for [TestRestTemplate.postForObject] avoiding specifying the type parameter + * thanks to Kotlin reified type parameters. + * + * @author Sebastien Deleuze + * @since 2.0.0 + */ +@Throws(RestClientException::class) +inline fun TestRestTemplate.postForObject(url: String, request: Any, uriVariables: Map): T? = + postForObject(url, request, T::class.java, uriVariables) + +/** + * Extension for [TestRestTemplate.postForObject] avoiding specifying the type parameter + * thanks to Kotlin reified type parameters. + * + * @author Sebastien Deleuze + * @since 2.0.0 + */ +@Throws(RestClientException::class) +inline fun TestRestTemplate.postForObject(url: URI, request: Any): T? = + postForObject(url, request, T::class.java) + +/** + * Extension for [TestRestTemplate.postForEntity] avoiding specifying the type parameter + * thanks to Kotlin reified type parameters. + * + * @author Sebastien Deleuze + * @since 2.0.0 + */ +@Throws(RestClientException::class) +inline fun TestRestTemplate.postForEntity(url: String, request: Any, vararg uriVariables: Any): ResponseEntity = + postForEntity(url, request, T::class.java, *uriVariables) + +/** + * Extension for [TestRestTemplate.postForEntity] avoiding specifying the type parameter + * thanks to Kotlin reified type parameters. + * + * @author Sebastien Deleuze + * @since 2.0.0 + */ +@Throws(RestClientException::class) +inline fun TestRestTemplate.postForEntity(url: String, request: Any, uriVariables: Map): ResponseEntity = + postForEntity(url, request, T::class.java, uriVariables) + +/** + * Extension for [TestRestTemplate.postForEntity] avoiding specifying the type parameter + * thanks to Kotlin reified type parameters. + * + * @author Sebastien Deleuze + * @since 2.0.0 + */ +@Throws(RestClientException::class) +inline fun TestRestTemplate.postForEntity(url: URI, request: Any): ResponseEntity = + postForEntity(url, request, T::class.java) + +/** + * Extension for [TestRestTemplate.exchange] avoiding specifying the type parameter + * thanks to Kotlin reified type parameters. + * + * @author Sebastien Deleuze + * @since 2.0.0 + */ +@Throws(RestClientException::class) +inline fun TestRestTemplate.exchange(url: String, method: HttpMethod, requestEntity: HttpEntity<*>, vararg uriVariables: Any): ResponseEntity = + exchange(url, method, requestEntity, object : ParameterizedTypeReference() {}, *uriVariables) + +/** + * Extension for [TestRestTemplate.exchange] avoiding specifying the type parameter + * thanks to Kotlin reified type parameters. + * + * @author Sebastien Deleuze + * @since 2.0.0 + */ +@Throws(RestClientException::class) +inline fun TestRestTemplate.exchange(url: String, method: HttpMethod, requestEntity: HttpEntity<*>, uriVariables: Map): ResponseEntity = + exchange(url, method, requestEntity, object : ParameterizedTypeReference() {}, uriVariables) + +/** + * Extension for [TestRestTemplate.exchange] avoiding specifying the type parameter + * thanks to Kotlin reified type parameters. + * + * @author Sebastien Deleuze + * @since 2.0.0 + */ +@Throws(RestClientException::class) +inline fun TestRestTemplate.exchange(url: URI, method: HttpMethod, requestEntity: HttpEntity<*>): ResponseEntity = + exchange(url, method, requestEntity, object : ParameterizedTypeReference() {}) + +/** + * Extension for [TestRestTemplate.exchange] avoiding specifying the type parameter + * thanks to Kotlin reified type parameters. + * + * @author Sebastien Deleuze + * @since 2.0.0 + */ +@Throws(RestClientException::class) +inline fun TestRestTemplate.exchange(requestEntity: RequestEntity<*>): ResponseEntity = + exchange(requestEntity, object : ParameterizedTypeReference() {}) \ No newline at end of file diff --git a/spring-boot-project/spring-boot-test/src/test/kotlin/org/springframework/boot/test/web/client/TestRestTemplateExtensionsTests.kt b/spring-boot-project/spring-boot-test/src/test/kotlin/org/springframework/boot/test/web/client/TestRestTemplateExtensionsTests.kt new file mode 100644 index 00000000000..f8b5b0195e3 --- /dev/null +++ b/spring-boot-project/spring-boot-test/src/test/kotlin/org/springframework/boot/test/web/client/TestRestTemplateExtensionsTests.kt @@ -0,0 +1,232 @@ +/* + * Copyright 2012-2017 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.boot.test.web.client + +import com.nhaarman.mockito_kotlin.mock +import org.junit.Assert +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.Answers +import org.mockito.Mock +import org.mockito.Mockito.* +import org.mockito.junit.MockitoJUnitRunner +import org.springframework.core.ParameterizedTypeReference +import org.springframework.http.HttpEntity +import org.springframework.http.HttpMethod +import org.springframework.http.RequestEntity +import org.springframework.util.ReflectionUtils +import org.springframework.web.client.RestOperations +import java.net.URI +import kotlin.reflect.full.createType +import kotlin.reflect.jvm.kotlinFunction + +/** + * Mock object based tests for [TestRestTemplate] Kotlin extensions + * + * @author Sebastien Deleuze + */ +@RunWith(MockitoJUnitRunner::class) +class TestRestTemplateExtensionsTests { + + @Mock(answer = Answers.RETURNS_MOCKS) + lateinit var template: TestRestTemplate + + @Test + fun `getForObject with reified type parameters, String and varargs`() { + val url = "https://spring.io" + val var1 = "var1" + val var2 = "var2" + template.getForObject(url, var1, var2) + template.restTemplate + verify(template, times(1)).getForObject(url, Foo::class.java, var1, var2) + } + + @Test + fun `getForObject with reified type parameters, String and Map`() { + val url = "https://spring.io" + val vars = mapOf(Pair("key1", "value1"), Pair("key2", "value2")) + template.getForObject(url, vars) + verify(template, times(1)).getForObject(url, Foo::class.java, vars) + } + + @Test + fun `getForObject with reified type parameters and URI`() { + val url = URI("https://spring.io") + template.getForObject(url) + verify(template, times(1)).getForObject(url, Foo::class.java) + } + + @Test + fun `getForEntity with reified type parameters, String and URI`() { + val url = URI("https://spring.io") + template.getForEntity(url) + verify(template, times(1)).getForEntity(url, Foo::class.java) + } + + @Test + fun `getForEntity with reified type parameters, String and varargs`() { + val url = "https://spring.io" + val var1 = "var1" + val var2 = "var2" + template.getForEntity(url, var1, var2) + verify(template, times(1)).getForEntity(url, Foo::class.java, var1, var2) + } + + @Test + fun `getForEntity with reified type parameters and Map`() { + val url = "https://spring.io" + val vars = mapOf(Pair("key1", "value1"), Pair("key2", "value2")) + template.getForEntity(url, vars) + verify(template, times(1)).getForEntity(url, Foo::class.java, vars) + } + + @Test + fun `patchForObject with reified type parameters, String and varargs`() { + val url = "https://spring.io" + val body: Any = "body" + val var1 = "var1" + val var2 = "var2" + template.patchForObject(url, body, var1, var2) + verify(template, times(1)).patchForObject(url, body, Foo::class.java, var1, var2) + } + + @Test + fun `patchForObject with reified type parameters, String and Map`() { + val url = "https://spring.io" + val body: Any = "body" + val vars = mapOf(Pair("key1", "value1"), Pair("key2", "value2")) + template.patchForObject(url, body, vars) + verify(template, times(1)).patchForObject(url, body, Foo::class.java, vars) + } + + @Test + fun `patchForObject with reified type parameters`() { + val url = "https://spring.io" + val body: Any = "body" + template.patchForObject(url, body) + verify(template, times(1)).patchForObject(url, body, Foo::class.java) + } + + @Test + fun `postForObject with reified type parameters, String and varargs`() { + val url = "https://spring.io" + val body: Any = "body" + val var1 = "var1" + val var2 = "var2" + template.postForObject(url, body, var1, var2) + verify(template, times(1)).postForObject(url, body, Foo::class.java, var1, var2) + } + + @Test + fun `postForObject with reified type parameters, String and Map`() { + val url = "https://spring.io" + val body: Any = "body" + val vars = mapOf(Pair("key1", "value1"), Pair("key2", "value2")) + template.postForObject(url, body, vars) + verify(template, times(1)).postForObject(url, body, Foo::class.java, vars) + } + + @Test + fun `postForObject with reified type parameters`() { + val url = "https://spring.io" + val body: Any = "body" + template.postForObject(url, body) + verify(template, times(1)).postForObject(url, body, Foo::class.java) + } + + @Test + fun `postForEntity with reified type parameters, String and varargs`() { + val url = "https://spring.io" + val body: Any = "body" + val var1 = "var1" + val var2 = "var2" + template.postForEntity(url, body, var1, var2) + verify(template, times(1)).postForEntity(url, body, Foo::class.java, var1, var2) + } + + @Test + fun `postForEntity with reified type parameters, String and Map`() { + val url = "https://spring.io" + val body: Any = "body" + val vars = mapOf(Pair("key1", "value1"), Pair("key2", "value2")) + template.postForEntity(url, body, vars) + verify(template, times(1)).postForEntity(url, body, Foo::class.java, vars) + } + + @Test + fun `postForEntity with reified type parameters`() { + val url = "https://spring.io" + val body: Any = "body" + template.postForEntity(url, body) + verify(template, times(1)).postForEntity(url, body, Foo::class.java) + } + + @Test + fun `exchange with reified type parameters, String, HttpMethod, HttpEntity and varargs`() { + val url = "https://spring.io" + val method = HttpMethod.GET + val entity = mock>() + val var1 = "var1" + val var2 = "var2" + template.exchange>(url, method, entity, var1, var2) + verify(template, times(1)).exchange(url, method, entity, object : ParameterizedTypeReference>() {}, var1, var2) + } + + @Test + fun `exchange with reified type parameters, String, HttpMethod, HttpEntity and Map`() { + val url = "https://spring.io" + val method = HttpMethod.GET + val entity = mock>() + val vars = mapOf(Pair("key1", "value1"), Pair("key2", "value2")) + template.exchange>(url, method, entity, vars) + verify(template, times(1)).exchange(url, method, entity, object : ParameterizedTypeReference>() {}, vars) + } + + @Test + fun `exchange with reified type parameters, String, HttpMethod, HttpEntity`() { + val url = "https://spring.io" + val method = HttpMethod.GET + val entity = mock>() + template.exchange>(url, method, entity) + verify(template, times(1)).exchange(url, method, entity, object : ParameterizedTypeReference>() {}) + } + + @Test + fun `exchange with reified type parameters, String, HttpEntity`() { + val entity = mock>() + template.exchange>(entity) + verify(template, times(1)).exchange(entity, object : ParameterizedTypeReference>() {}) + } + + @Test + fun `RestOperations are available`() { + val extensions = Class.forName("org.springframework.boot.test.web.client.TestRestTemplateExtensionsKt") + ReflectionUtils.doWithMethods(RestOperations::class.java) { method -> + arrayOf(ParameterizedTypeReference::class, Class::class).forEach { kClass -> + if (method.parameterTypes.contains(kClass.java)) { + val parameters = mutableListOf>(TestRestTemplate::class.java).apply { addAll(method.parameterTypes.filter { it != kClass.java }) } + val f = extensions.getDeclaredMethod(method.name, *parameters.toTypedArray()).kotlinFunction!! + Assert.assertEquals(1, f.typeParameters.size) + Assert.assertEquals(listOf(Any::class.createType()), f.typeParameters[0].upperBounds) + } + } + } + } + + class Foo + +}