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
This commit is contained in:
@@ -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<RestOperations>(relaxed = true)
|
||||
val template = mockk<RestOperations>()
|
||||
|
||||
val foo = mockk<Foo>()
|
||||
|
||||
val entity = mockk<ResponseEntity<Foo>>()
|
||||
|
||||
@Test
|
||||
fun `getForObject with reified type parameters, String and varargs`() {
|
||||
val url = "https://spring.io"
|
||||
val var1 = "var1"
|
||||
val var2 = "var2"
|
||||
template.getForObject<Foo>(url, var1, var2)
|
||||
every { template.getForObject(url, Foo::class.java, var1, var2) } returns foo
|
||||
assertEquals(foo, template.getForObject<Foo>(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<Foo>(url, vars)
|
||||
every { template.getForObject(url, Foo::class.java, vars) } returns foo
|
||||
assertEquals(foo, template.getForObject<Foo>(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<Foo>(url)
|
||||
every { template.getForObject(url, Foo::class.java) } returns foo
|
||||
assertEquals(foo, template.getForObject<Foo>(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<Foo>(url)
|
||||
every { template.getForEntity(url, Foo::class.java) } returns entity
|
||||
assertEquals(entity, template.getForEntity<Foo>(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<Foo>(url, var1, var2)
|
||||
every { template.getForEntity(url, Foo::class.java, var1, var2) } returns entity
|
||||
assertEquals(entity, template.getForEntity<Foo>(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<Foo>(url, vars)
|
||||
every { template.getForEntity(url, Foo::class.java, vars) } returns entity
|
||||
assertEquals(entity, template.getForEntity<Foo>(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<Foo>(url, body, var1, var2)
|
||||
every { template.patchForObject(url, body, Foo::class.java, var1, var2) } returns foo
|
||||
assertEquals(foo, template.patchForObject<Foo>(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<Foo>(url, body, vars)
|
||||
every { template.patchForObject(url, body, Foo::class.java, vars) } returns foo
|
||||
assertEquals(foo, template.patchForObject<Foo>(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<Foo>(url, body)
|
||||
every { template.patchForObject(url, body, Foo::class.java) } returns foo
|
||||
assertEquals(foo, template.patchForObject<Foo>(url, body))
|
||||
verify { template.patchForObject(url, body, Foo::class.java) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `patchForObject with reified type parameters`() {
|
||||
val url = "https://spring.io"
|
||||
template.patchForObject<Foo>(url)
|
||||
every { template.patchForObject(url, null, Foo::class.java) } returns foo
|
||||
assertEquals(foo, template.patchForObject<Foo>(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<Foo>(url, body, var1, var2)
|
||||
every { template.postForObject(url, body, Foo::class.java, var1, var2) } returns foo
|
||||
assertEquals(foo, template.postForObject<Foo>(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<Foo>(url, body, vars)
|
||||
every { template.postForObject(url, body, Foo::class.java, vars) } returns foo
|
||||
assertEquals(foo, template.postForObject<Foo>(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<Foo>(url, body)
|
||||
every { template.postForObject(url, body, Foo::class.java) } returns foo
|
||||
assertEquals(foo, template.postForObject<Foo>(url, body))
|
||||
verify { template.postForObject(url, body, Foo::class.java) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `postForObject with reified type parameters`() {
|
||||
val url = "https://spring.io"
|
||||
template.postForObject<Foo>(url)
|
||||
every { template.postForObject(url, null, Foo::class.java) } returns foo
|
||||
assertEquals(foo, template.postForObject<Foo>(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<Foo>(url, body, var1, var2)
|
||||
every { template.postForEntity(url, body, Foo::class.java, var1, var2) } returns entity
|
||||
assertEquals(entity, template.postForEntity<Foo>(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<Foo>(url, body, vars)
|
||||
every { template.postForEntity(url, body, Foo::class.java, vars) } returns entity
|
||||
assertEquals(entity, template.postForEntity<Foo>(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<Foo>(url, body)
|
||||
every { template.postForEntity(url, body, Foo::class.java) } returns entity
|
||||
assertEquals(entity, template.postForEntity<Foo>(url, body))
|
||||
verify { template.postForEntity(url, body, Foo::class.java) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `postForEntity with reified type parameters`() {
|
||||
val url = "https://spring.io"
|
||||
template.postForEntity<Foo>(url)
|
||||
verify { template.postForEntity(url, null, Foo::class.java) }
|
||||
every { template.postForEntity(url, null, Foo::class.java) } returns entity
|
||||
assertEquals(entity, template.postForEntity<Foo>(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<HttpEntity<Foo>>()
|
||||
val var1 = "var1"
|
||||
val var2 = "var2"
|
||||
template.exchange<List<Foo>>(url, method, entity, var1, var2)
|
||||
verify { template.exchange(url, method, entity,
|
||||
object : ParameterizedTypeReference<List<Foo>>() {}, var1, var2) }
|
||||
val entityList = mockk<ResponseEntity<List<Foo>>>()
|
||||
val responseType = object : ParameterizedTypeReference<List<Foo>>() {}
|
||||
every { template.exchange(url, method, entity, responseType, var1, var2) } returns entityList
|
||||
assertEquals(entityList, template.exchange<List<Foo>>(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<HttpEntity<Foo>>()
|
||||
val vars = mapOf(Pair("key1", "value1"), Pair("key2", "value2"))
|
||||
template.exchange<List<Foo>>(url, method, entity, vars)
|
||||
verify { template.exchange(url, method, entity,
|
||||
object : ParameterizedTypeReference<List<Foo>>() {}, vars) }
|
||||
val entityList = mockk<ResponseEntity<List<Foo>>>()
|
||||
val responseType = object : ParameterizedTypeReference<List<Foo>>() {}
|
||||
every { template.exchange(url, method, entity, responseType, vars) } returns entityList
|
||||
assertEquals(entityList, template.exchange<List<Foo>>(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<HttpEntity<Foo>>()
|
||||
template.exchange<List<Foo>>(url, method, entity)
|
||||
verify { template.exchange(url, method, entity,
|
||||
object : ParameterizedTypeReference<List<Foo>>() {}) }
|
||||
val entityList = mockk<ResponseEntity<List<Foo>>>()
|
||||
val responseType = object : ParameterizedTypeReference<List<Foo>>() {}
|
||||
every { template.exchange(url, method, entity, responseType) } returns entityList
|
||||
assertEquals(entityList, template.exchange<List<Foo>>(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<List<Foo>>(url, method)
|
||||
verify { template.exchange(url, method, null,
|
||||
object : ParameterizedTypeReference<List<Foo>>() {}) }
|
||||
val entityList = mockk<ResponseEntity<List<Foo>>>()
|
||||
val responseType = object : ParameterizedTypeReference<List<Foo>>() {}
|
||||
every { template.exchange(url, method, null, responseType) } returns entityList
|
||||
assertEquals(entityList, template.exchange<List<Foo>>(url, method))
|
||||
verify { template.exchange(url, method, null, responseType) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `exchange with reified type parameters, String and HttpEntity`() {
|
||||
val entity = mockk<RequestEntity<Foo>>()
|
||||
template.exchange<List<Foo>>(entity)
|
||||
verify { template.exchange(entity,
|
||||
object : ParameterizedTypeReference<List<Foo>>() {}) }
|
||||
val entityList = mockk<ResponseEntity<List<Foo>>>()
|
||||
val responseType = object : ParameterizedTypeReference<List<Foo>>() {}
|
||||
every { template.exchange(entity, responseType) } returns entityList
|
||||
assertEquals(entityList, template.exchange<List<Foo>>(entity))
|
||||
verify { template.exchange(entity, responseType) }
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -247,7 +278,8 @@ class RestOperationsExtensionsTests {
|
||||
val parameters = mutableListOf<Class<*>>(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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user