From cbb5a78aa0f052724f4a9e0a5d25f62d8425f610 Mon Sep 17 00:00:00 2001 From: Sebastien Deleuze Date: Wed, 27 Mar 2019 08:50:42 +0100 Subject: [PATCH] Infer Kotlin null-safety from type variables This commit removes the constraint from type variables in PropertyResolver, JdbcOperations and RestOperations Kotlin extensions in order to get null-safety inferred from the type declared by the user. Closes gh-22687 --- .../core/env/PropertyResolverExtensions.kt | 8 +- .../env/PropertyResolverExtensionsTests.kt | 13 +- .../jdbc/core/JdbcOperationsExtensions.kt | 30 ++--- .../core/JdbcOperationsExtensionsTests.kt | 70 ++++++----- .../web/client/RestOperationsExtensions.kt | 66 +++++------ .../client/RestOperationsExtensionsTests.kt | 112 +++++++++++------- 6 files changed, 175 insertions(+), 124 deletions(-) diff --git a/spring-core/src/main/kotlin/org/springframework/core/env/PropertyResolverExtensions.kt b/spring-core/src/main/kotlin/org/springframework/core/env/PropertyResolverExtensions.kt index 2d45cdf742..9a62d18a29 100644 --- a/spring-core/src/main/kotlin/org/springframework/core/env/PropertyResolverExtensions.kt +++ b/spring-core/src/main/kotlin/org/springframework/core/env/PropertyResolverExtensions.kt @@ -1,5 +1,5 @@ /* - * Copyright 2002-2018 the original author or authors. + * Copyright 2002-2019 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. @@ -39,8 +39,8 @@ operator fun PropertyResolver.get(key: String) : String? = getProperty(key) * @author Sebastien Deleuze * @since 5.1 */ -inline fun PropertyResolver.getProperty(key: String) : T? = - getProperty(key, T::class.java) +inline fun PropertyResolver.getProperty(key: String) : T = + getProperty(key, T::class.java) as T /** * Extension for [PropertyResolver.getRequiredProperty] providing a @@ -49,5 +49,5 @@ inline fun PropertyResolver.getProperty(key: String) : T? = * @author Sebastien Deleuze * @since 5.1 */ -inline fun PropertyResolver.getRequiredProperty(key: String) : T = +inline fun PropertyResolver.getRequiredProperty(key: String) : T = getRequiredProperty(key, T::class.java) diff --git a/spring-core/src/test/kotlin/org/springframework/core/env/PropertyResolverExtensionsTests.kt b/spring-core/src/test/kotlin/org/springframework/core/env/PropertyResolverExtensionsTests.kt index 0327d2ae56..af70282238 100644 --- a/spring-core/src/test/kotlin/org/springframework/core/env/PropertyResolverExtensionsTests.kt +++ b/spring-core/src/test/kotlin/org/springframework/core/env/PropertyResolverExtensionsTests.kt @@ -16,6 +16,7 @@ package org.springframework.core.env +import io.mockk.every import io.mockk.mockk import io.mockk.verify import org.junit.Test @@ -27,22 +28,32 @@ import org.junit.Test */ class PropertyResolverExtensionsTests { - val propertyResolver = mockk(relaxed = true) + val propertyResolver = mockk() @Test fun `get operator`() { + every { propertyResolver.getProperty("name") } returns "foo" propertyResolver["name"] verify { propertyResolver.getProperty("name") } } @Test fun `getProperty extension`() { + every { propertyResolver.getProperty("name", String::class.java) } returns "foo" propertyResolver.getProperty("name") verify { propertyResolver.getProperty("name", String::class.java) } } + @Test + fun `getProperty extension with nullable type`() { + every { propertyResolver.getProperty("name", String::class.java) } returns null + propertyResolver.getProperty("name") + verify { propertyResolver.getProperty("name", String::class.java) } + } + @Test fun `getRequiredProperty extension`() { + every { propertyResolver.getRequiredProperty("name", String::class.java) } returns "foo" propertyResolver.getRequiredProperty("name") verify { propertyResolver.getRequiredProperty("name", String::class.java) } } diff --git a/spring-jdbc/src/main/kotlin/org/springframework/jdbc/core/JdbcOperationsExtensions.kt b/spring-jdbc/src/main/kotlin/org/springframework/jdbc/core/JdbcOperationsExtensions.kt index 5fce306450..531b6ccdd3 100644 --- a/spring-jdbc/src/main/kotlin/org/springframework/jdbc/core/JdbcOperationsExtensions.kt +++ b/spring-jdbc/src/main/kotlin/org/springframework/jdbc/core/JdbcOperationsExtensions.kt @@ -24,8 +24,8 @@ import java.sql.ResultSet * @author Mario Arias * @since 5.0 */ -inline fun JdbcOperations.queryForObject(sql: String): T? = - queryForObject(sql, T::class.java) +inline fun JdbcOperations.queryForObject(sql: String): T = + queryForObject(sql, T::class.java) as T /** * Extensions for [JdbcOperations.queryForObject] providing a RowMapper-like function @@ -34,8 +34,8 @@ inline fun JdbcOperations.queryForObject(sql: String): T? = * @author Mario Arias * @since 5.0 */ -fun JdbcOperations.queryForObject(sql: String, vararg args: Any, function: (ResultSet, Int) -> T): T? = - queryForObject(sql, RowMapper { resultSet, i -> function(resultSet, i) }, *args) +inline fun JdbcOperations.queryForObject(sql: String, vararg args: Any, crossinline function: (ResultSet, Int) -> T): T = + queryForObject(sql, RowMapper { resultSet, i -> function(resultSet, i) }, *args) as T /** * Extension for [JdbcOperations.queryForObject] providing a @@ -44,8 +44,8 @@ fun JdbcOperations.queryForObject(sql: String, vararg args: Any, func * @author Mario Arias * @since 5.0 */ -inline fun JdbcOperations.queryForObject(sql: String, args: Array, argTypes: IntArray): T? = - queryForObject(sql, args, argTypes, T::class.java) +inline fun JdbcOperations.queryForObject(sql: String, args: Array, argTypes: IntArray): T? = + queryForObject(sql, args, argTypes, T::class.java) as T /** * Extension for [JdbcOperations.queryForObject] providing a @@ -54,8 +54,8 @@ inline fun JdbcOperations.queryForObject(sql: String, args: Ar * @author Mario Arias * @since 5.0 */ -inline fun JdbcOperations.queryForObject(sql: String, args: Array): T? = - queryForObject(sql, args, T::class.java) +inline fun JdbcOperations.queryForObject(sql: String, args: Array): T? = + queryForObject(sql, args, T::class.java) as T /** * Extension for [JdbcOperations.queryForList] providing a `queryForList("...")` variant. @@ -64,7 +64,7 @@ inline fun JdbcOperations.queryForObject(sql: String, args: Ar * @since 5.0 */ @Suppress("EXTENSION_SHADOWED_BY_MEMBER") -inline fun JdbcOperations.queryForList(sql: String): List = +inline fun JdbcOperations.queryForList(sql: String): List = queryForList(sql, T::class.java) /** @@ -75,7 +75,7 @@ inline fun JdbcOperations.queryForList(sql: String): List = * @since 5.0 */ @Suppress("EXTENSION_SHADOWED_BY_MEMBER") -inline fun JdbcOperations.queryForList(sql: String, args: Array, +inline fun JdbcOperations.queryForList(sql: String, args: Array, argTypes: IntArray): List = queryForList(sql, args, argTypes, T::class.java) @@ -86,7 +86,7 @@ inline fun JdbcOperations.queryForList(sql: String, args: Arra * @author Mario Arias * @since 5.0 */ -inline fun JdbcOperations.queryForList(sql: String, args: Array): List = +inline fun JdbcOperations.queryForList(sql: String, args: Array): List = queryForList(sql, args, T::class.java) /** @@ -96,9 +96,9 @@ inline fun JdbcOperations.queryForList(sql: String, args: Arra * @author Mario Arias * @since 5.0 */ -inline fun JdbcOperations.query(sql: String, vararg args: Any, - crossinline function: (ResultSet) -> T): T? = - query(sql, ResultSetExtractor { function(it) }, *args) +inline fun JdbcOperations.query(sql: String, vararg args: Any, + crossinline function: (ResultSet) -> T): T = + query(sql, ResultSetExtractor { function(it) }, *args) as T /** * Extension for [JdbcOperations.query] providing a RowCallbackHandler-like function @@ -117,5 +117,5 @@ fun JdbcOperations.query(sql: String, vararg args: Any, function: (ResultSet) -> * @author Mario Arias * @since 5.0 */ -fun JdbcOperations.query(sql: String, vararg args: Any, function: (ResultSet, Int) -> T): List = +fun JdbcOperations.query(sql: String, vararg args: Any, function: (ResultSet, Int) -> T): List = query(sql, RowMapper { rs, i -> function(rs, i) }, *args) diff --git a/spring-jdbc/src/test/kotlin/org/springframework/jdbc/core/JdbcOperationsExtensionsTests.kt b/spring-jdbc/src/test/kotlin/org/springframework/jdbc/core/JdbcOperationsExtensionsTests.kt index 4b276622e5..41e106b617 100644 --- a/spring-jdbc/src/test/kotlin/org/springframework/jdbc/core/JdbcOperationsExtensionsTests.kt +++ b/spring-jdbc/src/test/kotlin/org/springframework/jdbc/core/JdbcOperationsExtensionsTests.kt @@ -16,9 +16,11 @@ package org.springframework.jdbc.core +import io.mockk.every import io.mockk.mockk import io.mockk.verify import org.junit.Assert.assertEquals +import org.junit.Assert.assertNull import org.junit.Test import java.sql.* @@ -30,91 +32,96 @@ import java.sql.* */ class JdbcOperationsExtensionsTests { - val template = mockk(relaxed = true) + val template = mockk() + + val sql = "select age from customer where id = 3" @Test fun `queryForObject with reified type parameters`() { - val sql = "select age from customer where id = 3" - template.queryForObject(sql) - verify { template.queryForObject(sql, Integer::class.java) } + every { template.queryForObject(sql, any>()) } returns 2 + assertEquals(2, template.queryForObject(sql)) + verify { template.queryForObject(sql, any>()) } } @Test fun `queryForObject with RowMapper-like function`() { - val sql = "select age from customer where id = ?" - template.queryForObject(sql, 3) { rs: ResultSet, _: Int -> rs.getInt(1) } + every { template.queryForObject(sql, any>(), any()) } returns 2 + assertEquals(2, template.queryForObject(sql, 3) { rs: ResultSet, _: Int -> rs.getInt(1) }) verify { template.queryForObject(eq(sql), any>(), eq(3)) } } @Test // gh-22682 fun `queryForObject with nullable RowMapper-like function`() { - val sql = "select age from customer where id = ?" - template.queryForObject(sql, 3) { _, _ -> null as Int? } + every { template.queryForObject(sql, any>(), 3) } returns null + assertNull(template.queryForObject(sql, 3) { _, _ -> null as Int? }) verify { template.queryForObject(eq(sql), any>(), eq(3)) } } @Test fun `queryForObject with reified type parameters and argTypes`() { - val sql = "select age from customer where id = ?" val args = arrayOf(3) val argTypes = intArrayOf(JDBCType.INTEGER.vendorTypeNumber) - template.queryForObject(sql, args, argTypes) - verify { template.queryForObject(sql, args, argTypes, Integer::class.java) } + every { template.queryForObject(sql, args, argTypes, any>()) } returns 2 + assertEquals(2, template.queryForObject(sql, args, argTypes)) + verify { template.queryForObject(sql, args, argTypes, any>()) } } @Test fun `queryForObject with reified type parameters and args`() { - val sql = "select age from customer where id = ?" val args = arrayOf(3) - template.queryForObject(sql, args) - verify { template.queryForObject(sql, args, Integer::class.java) } + every { template.queryForObject(sql, args, any>()) } returns 2 + assertEquals(2, template.queryForObject(sql, args)) + verify { template.queryForObject(sql, args, any>()) } } @Test fun `queryForList with reified type parameters`() { - val sql = "select age from customer where id = 3" - template.queryForList(sql) - verify { template.queryForList(sql, Integer::class.java) } + val list = listOf(1, 2, 3) + every { template.queryForList(sql, any>()) } returns list + assertEquals(list, template.queryForList(sql)) + verify { template.queryForList(sql, any>()) } } @Test fun `queryForList with reified type parameters and argTypes`() { - val sql = "select age from customer where id = ?" + val list = listOf(1, 2, 3) val args = arrayOf(3) val argTypes = intArrayOf(JDBCType.INTEGER.vendorTypeNumber) - template.queryForList(sql, args, argTypes) - verify { template.queryForList(sql, args, argTypes, Integer::class.java) } + every { template.queryForList(sql, args, argTypes, any>()) } returns list + assertEquals(list, template.queryForList(sql, args, argTypes)) + verify { template.queryForList(sql, args, argTypes, any>()) } } @Test fun `queryForList with reified type parameters and args`() { - val sql = "select age from customer where id = ?" + val list = listOf(1, 2, 3) val args = arrayOf(3) + every { template.queryForList(sql, args, any>()) } returns list template.queryForList(sql, args) - verify { template.queryForList(sql, args, Integer::class.java) } + verify { template.queryForList(sql, args, any>()) } } @Test fun `query with ResultSetExtractor-like function`() { - val sql = "select age from customer where id = ?" - template.query(sql, 3) { rs -> + every { template.query(eq(sql), any>(), eq(3)) } returns 2 + assertEquals(2, template.query(sql, 3) { rs -> rs.next() rs.getInt(1) - } + }) verify { template.query(eq(sql), any>(), eq(3)) } } @Test // gh-22682 fun `query with nullable ResultSetExtractor-like function`() { - val sql = "select age from customer where id = ?" - template.query(sql, 3) { _ -> null } + every { template.query(eq(sql), any>(), eq(3)) } returns null + assertNull(template.query(sql, 3) { _ -> null }) verify { template.query(eq(sql), any>(), eq(3)) } } @Suppress("RemoveExplicitTypeArguments") @Test fun `query with RowCallbackHandler-like function`() { - val sql = "select age from customer where id = ?" + every { template.query(sql, ofType(), 3) } returns Unit template.query(sql, 3) { rs -> assertEquals(22, rs.getInt(1)) } @@ -123,10 +130,11 @@ class JdbcOperationsExtensionsTests { @Test fun `query with RowMapper-like function`() { - val sql = "select age from customer where id = ?" - template.query(sql, 3) { rs, _ -> + val list = listOf(1, 2, 3) + every { template.query(sql, ofType>(), 3) } returns list + assertEquals(list, template.query(sql, 3) { rs, _ -> rs.getInt(1) - } + }) verify { template.query(sql, ofType>(), 3) } } diff --git a/spring-web/src/main/kotlin/org/springframework/web/client/RestOperationsExtensions.kt b/spring-web/src/main/kotlin/org/springframework/web/client/RestOperationsExtensions.kt index e4d6278e74..a6234cfb14 100644 --- a/spring-web/src/main/kotlin/org/springframework/web/client/RestOperationsExtensions.kt +++ b/spring-web/src/main/kotlin/org/springframework/web/client/RestOperationsExtensions.kt @@ -1,5 +1,5 @@ /* - * Copyright 2002-2017 the original author or authors. + * Copyright 2002-2019 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. @@ -34,8 +34,8 @@ import java.net.URI * @since 5.0 */ @Throws(RestClientException::class) -inline fun RestOperations.getForObject(url: String, vararg uriVariables: Any): T? = - getForObject(url, T::class.java, *uriVariables) +inline fun RestOperations.getForObject(url: String, vararg uriVariables: Any): T = + getForObject(url, T::class.java, *uriVariables) as T /** * Extension for [RestOperations.getForObject] providing a `getForObject(...)` @@ -48,8 +48,8 @@ inline fun RestOperations.getForObject(url: String, vararg uriV * @since 5.0 */ @Throws(RestClientException::class) -inline fun RestOperations.getForObject(url: String, uriVariables: Map): T? = - getForObject(url, T::class.java, uriVariables) +inline fun RestOperations.getForObject(url: String, uriVariables: Map): T = + getForObject(url, T::class.java, uriVariables) as T /** * Extension for [RestOperations.getForObject] providing a `getForObject(...)` @@ -62,8 +62,8 @@ inline fun RestOperations.getForObject(url: String, uriVariable * @since 5.0 */ @Throws(RestClientException::class) -inline fun RestOperations.getForObject(url: URI): T? = - getForObject(url, T::class.java) +inline fun RestOperations.getForObject(url: URI): T = + getForObject(url, T::class.java) as T /** * Extension for [RestOperations.getForEntity] providing a `getForEntity(...)` @@ -75,7 +75,7 @@ inline fun RestOperations.getForObject(url: URI): T? = * @since 5.0.2 */ @Throws(RestClientException::class) -inline fun RestOperations.getForEntity(url: URI): ResponseEntity = +inline fun RestOperations.getForEntity(url: URI): ResponseEntity = getForEntity(url, T::class.java) /** @@ -89,7 +89,7 @@ inline fun RestOperations.getForEntity(url: URI): ResponseEntit * @since 5.0 */ @Throws(RestClientException::class) -inline fun RestOperations.getForEntity(url: String, vararg uriVariables: Any): ResponseEntity = +inline fun RestOperations.getForEntity(url: String, vararg uriVariables: Any): ResponseEntity = getForEntity(url, T::class.java, *uriVariables) /** @@ -102,7 +102,7 @@ inline fun RestOperations.getForEntity(url: String, vararg uriV * @since 5.0.2 */ @Throws(RestClientException::class) -inline fun RestOperations.getForEntity(url: String, uriVariables: Map): ResponseEntity = +inline fun RestOperations.getForEntity(url: String, uriVariables: Map): ResponseEntity = getForEntity(url, T::class.java, uriVariables) /** @@ -115,9 +115,9 @@ inline fun RestOperations.getForEntity(url: String, uriVariable * @since 5.0.2 */ @Throws(RestClientException::class) -inline fun RestOperations.patchForObject(url: String, request: Any? = null, - vararg uriVariables: Any): T? = - patchForObject(url, request, T::class.java, *uriVariables) +inline fun RestOperations.patchForObject(url: String, request: Any? = null, + vararg uriVariables: Any): T = + patchForObject(url, request, T::class.java, *uriVariables) as T /** * Extension for [RestOperations.patchForObject] providing a `patchForObject(...)` @@ -129,9 +129,9 @@ inline fun RestOperations.patchForObject(url: String, request: * @since 5.0.2 */ @Throws(RestClientException::class) -inline fun RestOperations.patchForObject(url: String, request: Any? = null, - uriVariables: Map): T? = - patchForObject(url, request, T::class.java, uriVariables) +inline fun RestOperations.patchForObject(url: String, request: Any? = null, + uriVariables: Map): T = + patchForObject(url, request, T::class.java, uriVariables) as T /** * Extension for [RestOperations.patchForObject] providing a `patchForObject(...)` @@ -143,8 +143,8 @@ inline fun RestOperations.patchForObject(url: String, request: * @since 5.0.2 */ @Throws(RestClientException::class) -inline fun RestOperations.patchForObject(url: URI, request: Any? = null): T? = - patchForObject(url, request, T::class.java) +inline fun RestOperations.patchForObject(url: URI, request: Any? = null): T = + patchForObject(url, request, T::class.java) as T /** * Extension for [RestOperations.postForObject] providing a `postForObject(...)` @@ -157,9 +157,9 @@ inline fun RestOperations.patchForObject(url: URI, request: Any * @since 5.0 */ @Throws(RestClientException::class) -inline fun RestOperations.postForObject(url: String, request: Any? = null, - vararg uriVariables: Any): T? = - postForObject(url, request, T::class.java, *uriVariables) +inline fun RestOperations.postForObject(url: String, request: Any? = null, + vararg uriVariables: Any): T = + postForObject(url, request, T::class.java, *uriVariables) as T /** * Extension for [RestOperations.postForObject] providing a `postForObject(...)` @@ -172,9 +172,9 @@ inline fun RestOperations.postForObject(url: String, request: A * @since 5.0 */ @Throws(RestClientException::class) -inline fun RestOperations.postForObject(url: String, request: Any? = null, - uriVariables: Map): T? = - postForObject(url, request, T::class.java, uriVariables) +inline fun RestOperations.postForObject(url: String, request: Any? = null, + uriVariables: Map): T = + postForObject(url, request, T::class.java, uriVariables) as T /** * Extension for [RestOperations.postForObject] providing a `postForObject(...)` @@ -187,8 +187,8 @@ inline fun RestOperations.postForObject(url: String, request: A * @since 5.0 */ @Throws(RestClientException::class) -inline fun RestOperations.postForObject(url: URI, request: Any? = null): T? = - postForObject(url, request, T::class.java) +inline fun RestOperations.postForObject(url: URI, request: Any? = null): T = + postForObject(url, request, T::class.java) as T /** * Extension for [RestOperations.postForEntity] providing a `postForEntity(...)` @@ -201,7 +201,7 @@ inline fun RestOperations.postForObject(url: URI, request: Any? * @since 5.0 */ @Throws(RestClientException::class) -inline fun RestOperations.postForEntity(url: String, request: Any? = null, +inline fun RestOperations.postForEntity(url: String, request: Any? = null, vararg uriVariables: Any): ResponseEntity = postForEntity(url, request, T::class.java, *uriVariables) @@ -216,7 +216,7 @@ inline fun RestOperations.postForEntity(url: String, request: A * @since 5.0 */ @Throws(RestClientException::class) -inline fun RestOperations.postForEntity(url: String, request: Any? = null, +inline fun RestOperations.postForEntity(url: String, request: Any? = null, uriVariables: Map): ResponseEntity = postForEntity(url, request, T::class.java, uriVariables) @@ -231,7 +231,7 @@ inline fun RestOperations.postForEntity(url: String, request: A * @since 5.0 */ @Throws(RestClientException::class) -inline fun RestOperations.postForEntity(url: URI, request: Any? = null): ResponseEntity = +inline fun RestOperations.postForEntity(url: URI, request: Any? = null): ResponseEntity = postForEntity(url, request, T::class.java) /** @@ -244,7 +244,7 @@ inline fun RestOperations.postForEntity(url: URI, request: Any? * @since 5.0 */ @Throws(RestClientException::class) -inline fun RestOperations.exchange(url: String, method: HttpMethod, +inline fun RestOperations.exchange(url: String, method: HttpMethod, requestEntity: HttpEntity<*>? = null, vararg uriVariables: Any): ResponseEntity = exchange(url, method, requestEntity, object : ParameterizedTypeReference() {}, *uriVariables) @@ -258,7 +258,7 @@ inline fun RestOperations.exchange(url: String, method: HttpMet * @since 5.0 */ @Throws(RestClientException::class) -inline fun RestOperations.exchange(url: String, method: HttpMethod, +inline fun RestOperations.exchange(url: String, method: HttpMethod, requestEntity: HttpEntity<*>? = null, uriVariables: Map): ResponseEntity = exchange(url, method, requestEntity, object : ParameterizedTypeReference() {}, uriVariables) @@ -272,7 +272,7 @@ inline fun RestOperations.exchange(url: String, method: HttpMet * @since 5.0 */ @Throws(RestClientException::class) -inline fun RestOperations.exchange(url: URI, method: HttpMethod, +inline fun RestOperations.exchange(url: URI, method: HttpMethod, requestEntity: HttpEntity<*>? = null): ResponseEntity = exchange(url, method, requestEntity, object : ParameterizedTypeReference() {}) @@ -286,5 +286,5 @@ inline fun RestOperations.exchange(url: URI, method: HttpMethod * @since 5.0 */ @Throws(RestClientException::class) -inline fun RestOperations.exchange(requestEntity: RequestEntity<*>): ResponseEntity = +inline fun RestOperations.exchange(requestEntity: RequestEntity<*>): ResponseEntity = exchange(requestEntity, object : ParameterizedTypeReference() {}) diff --git a/spring-web/src/test/kotlin/org/springframework/web/client/RestOperationsExtensionsTests.kt b/spring-web/src/test/kotlin/org/springframework/web/client/RestOperationsExtensionsTests.kt index e8f41d27f5..445f6252f7 100644 --- a/spring-web/src/test/kotlin/org/springframework/web/client/RestOperationsExtensionsTests.kt +++ b/spring-web/src/test/kotlin/org/springframework/web/client/RestOperationsExtensionsTests.kt @@ -16,14 +16,14 @@ package org.springframework.web.client +import io.mockk.every import io.mockk.mockk import io.mockk.verify import org.junit.Assert +import org.junit.Assert.assertEquals import org.junit.Test import org.springframework.core.ParameterizedTypeReference -import org.springframework.http.HttpEntity -import org.springframework.http.HttpMethod -import org.springframework.http.RequestEntity +import org.springframework.http.* import org.springframework.util.ReflectionUtils import java.net.URI import kotlin.reflect.full.createType @@ -36,14 +36,19 @@ import kotlin.reflect.jvm.kotlinFunction */ class RestOperationsExtensionsTests { - val template = mockk(relaxed = true) + val template = mockk() + + val foo = mockk() + + val entity = mockk>() @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) + every { template.getForObject(url, Foo::class.java, var1, var2) } returns foo + assertEquals(foo, template.getForObject(url, var1, var2)) verify { template.getForObject(url, Foo::class.java, var1, var2) } } @@ -51,21 +56,24 @@ class RestOperationsExtensionsTests { 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) + every { template.getForObject(url, Foo::class.java, vars) } returns foo + assertEquals(foo, template.getForObject(url, vars)) verify { template.getForObject(url, Foo::class.java, vars) } } @Test fun `getForObject with reified type parameters and URI`() { val url = URI("https://spring.io") - template.getForObject(url) + every { template.getForObject(url, Foo::class.java) } returns foo + assertEquals(foo, template.getForObject(url)) verify { template.getForObject(url, Foo::class.java) } } @Test fun `getForEntity with reified type parameters, String and URI`() { val url = URI("https://spring.io") - template.getForEntity(url) + every { template.getForEntity(url, Foo::class.java) } returns entity + assertEquals(entity, template.getForEntity(url)) verify { template.getForEntity(url, Foo::class.java) } } @@ -74,7 +82,8 @@ class RestOperationsExtensionsTests { val url = "https://spring.io" val var1 = "var1" val var2 = "var2" - template.getForEntity(url, var1, var2) + every { template.getForEntity(url, Foo::class.java, var1, var2) } returns entity + assertEquals(entity, template.getForEntity(url, var1, var2)) verify { template.getForEntity(url, Foo::class.java, var1, var2) } } @@ -82,7 +91,8 @@ class RestOperationsExtensionsTests { 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) + every { template.getForEntity(url, Foo::class.java, vars) } returns entity + assertEquals(entity, template.getForEntity(url, vars)) verify { template.getForEntity(url, Foo::class.java, vars) } } @@ -92,7 +102,8 @@ class RestOperationsExtensionsTests { val body: Any = "body" val var1 = "var1" val var2 = "var2" - template.patchForObject(url, body, var1, var2) + every { template.patchForObject(url, body, Foo::class.java, var1, var2) } returns foo + assertEquals(foo, template.patchForObject(url, body, var1, var2)) verify { template.patchForObject(url, body, Foo::class.java, var1, var2) } } @@ -101,7 +112,8 @@ class RestOperationsExtensionsTests { val url = "https://spring.io" val body: Any = "body" val vars = mapOf(Pair("key1", "value1"), Pair("key2", "value2")) - template.patchForObject(url, body, vars) + every { template.patchForObject(url, body, Foo::class.java, vars) } returns foo + assertEquals(foo, template.patchForObject(url, body, vars)) verify { template.patchForObject(url, body, Foo::class.java, vars) } } @@ -109,14 +121,16 @@ class RestOperationsExtensionsTests { fun `patchForObject with reified type parameters and String`() { val url = "https://spring.io" val body: Any = "body" - template.patchForObject(url, body) + every { template.patchForObject(url, body, Foo::class.java) } returns foo + assertEquals(foo, template.patchForObject(url, body)) verify { template.patchForObject(url, body, Foo::class.java) } } @Test fun `patchForObject with reified type parameters`() { val url = "https://spring.io" - template.patchForObject(url) + every { template.patchForObject(url, null, Foo::class.java) } returns foo + assertEquals(foo, template.patchForObject(url)) verify { template.patchForObject(url, null, Foo::class.java) } } @@ -126,7 +140,8 @@ class RestOperationsExtensionsTests { val body: Any = "body" val var1 = "var1" val var2 = "var2" - template.postForObject(url, body, var1, var2) + every { template.postForObject(url, body, Foo::class.java, var1, var2) } returns foo + assertEquals(foo, template.postForObject(url, body, var1, var2)) verify { template.postForObject(url, body, Foo::class.java, var1, var2) } } @@ -135,7 +150,8 @@ class RestOperationsExtensionsTests { val url = "https://spring.io" val body: Any = "body" val vars = mapOf(Pair("key1", "value1"), Pair("key2", "value2")) - template.postForObject(url, body, vars) + every { template.postForObject(url, body, Foo::class.java, vars) } returns foo + assertEquals(foo, template.postForObject(url, body, vars)) verify { template.postForObject(url, body, Foo::class.java, vars) } } @@ -143,14 +159,16 @@ class RestOperationsExtensionsTests { fun `postForObject with reified type parameters and String`() { val url = "https://spring.io" val body: Any = "body" - template.postForObject(url, body) + every { template.postForObject(url, body, Foo::class.java) } returns foo + assertEquals(foo, template.postForObject(url, body)) verify { template.postForObject(url, body, Foo::class.java) } } @Test fun `postForObject with reified type parameters`() { val url = "https://spring.io" - template.postForObject(url) + every { template.postForObject(url, null, Foo::class.java) } returns foo + assertEquals(foo, template.postForObject(url)) verify { template.postForObject(url, null, Foo::class.java) } } @@ -160,7 +178,8 @@ class RestOperationsExtensionsTests { val body: Any = "body" val var1 = "var1" val var2 = "var2" - template.postForEntity(url, body, var1, var2) + every { template.postForEntity(url, body, Foo::class.java, var1, var2) } returns entity + assertEquals(entity, template.postForEntity(url, body, var1, var2)) verify { template.postForEntity(url, body, Foo::class.java, var1, var2) } } @@ -169,7 +188,8 @@ class RestOperationsExtensionsTests { val url = "https://spring.io" val body: Any = "body" val vars = mapOf(Pair("key1", "value1"), Pair("key2", "value2")) - template.postForEntity(url, body, vars) + every { template.postForEntity(url, body, Foo::class.java, vars) } returns entity + assertEquals(entity, template.postForEntity(url, body, vars)) verify { template.postForEntity(url, body, Foo::class.java, vars) } } @@ -177,27 +197,30 @@ class RestOperationsExtensionsTests { fun `postForEntity with reified type parameters and String`() { val url = "https://spring.io" val body: Any = "body" - template.postForEntity(url, body) + every { template.postForEntity(url, body, Foo::class.java) } returns entity + assertEquals(entity, template.postForEntity(url, body)) verify { template.postForEntity(url, body, Foo::class.java) } } @Test fun `postForEntity with reified type parameters`() { val url = "https://spring.io" - template.postForEntity(url) - verify { template.postForEntity(url, null, Foo::class.java) } + every { template.postForEntity(url, null, Foo::class.java) } returns entity + assertEquals(entity, template.postForEntity(url)) + verify { template.postForEntity(url, null, 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 = mockk>() val var1 = "var1" val var2 = "var2" - template.exchange>(url, method, entity, var1, var2) - verify { template.exchange(url, method, entity, - object : ParameterizedTypeReference>() {}, var1, var2) } + val entityList = mockk>>() + val responseType = object : ParameterizedTypeReference>() {} + every { template.exchange(url, method, entity, responseType, var1, var2) } returns entityList + assertEquals(entityList, template.exchange>(url, method, entity, var1, var2)) + verify { template.exchange(url, method, entity, responseType, var1, var2) } } @Test @@ -206,9 +229,11 @@ class RestOperationsExtensionsTests { val method = HttpMethod.GET val entity = mockk>() val vars = mapOf(Pair("key1", "value1"), Pair("key2", "value2")) - template.exchange>(url, method, entity, vars) - verify { template.exchange(url, method, entity, - object : ParameterizedTypeReference>() {}, vars) } + val entityList = mockk>>() + val responseType = object : ParameterizedTypeReference>() {} + every { template.exchange(url, method, entity, responseType, vars) } returns entityList + assertEquals(entityList, template.exchange>(url, method, entity, vars)) + verify { template.exchange(url, method, entity, responseType, vars) } } @Test @@ -216,26 +241,32 @@ class RestOperationsExtensionsTests { val url = "https://spring.io" val method = HttpMethod.GET val entity = mockk>() - template.exchange>(url, method, entity) - verify { template.exchange(url, method, entity, - object : ParameterizedTypeReference>() {}) } + val entityList = mockk>>() + val responseType = object : ParameterizedTypeReference>() {} + every { template.exchange(url, method, entity, responseType) } returns entityList + assertEquals(entityList, template.exchange>(url, method, entity)) + verify { template.exchange(url, method, entity, responseType) } } @Test fun `exchange with reified type parameters, String and HttpMethod`() { val url = "https://spring.io" val method = HttpMethod.GET - template.exchange>(url, method) - verify { template.exchange(url, method, null, - object : ParameterizedTypeReference>() {}) } + val entityList = mockk>>() + val responseType = object : ParameterizedTypeReference>() {} + every { template.exchange(url, method, null, responseType) } returns entityList + assertEquals(entityList, template.exchange>(url, method)) + verify { template.exchange(url, method, null, responseType) } } @Test fun `exchange with reified type parameters, String and HttpEntity`() { val entity = mockk>() - template.exchange>(entity) - verify { template.exchange(entity, - object : ParameterizedTypeReference>() {}) } + val entityList = mockk>>() + val responseType = object : ParameterizedTypeReference>() {} + every { template.exchange(entity, responseType) } returns entityList + assertEquals(entityList, template.exchange>(entity)) + verify { template.exchange(entity, responseType) } } @Test @@ -247,7 +278,8 @@ class RestOperationsExtensionsTests { val parameters = mutableListOf>(RestOperations::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) + System.out.println(method.name + f.typeParameters) + Assert.assertEquals("Failed: " + method.name, listOf(Any::class.createType(nullable = true)), f.typeParameters[0].upperBounds) } } }