Why Mockito can't mock a generic parameter type with number type in Kotlin?
We're moving our project to the Kotlin language. We decided to start from tests but faced with some strange behavior.
Here is our test case:
Service.java
public final class Service {
private final JdbcTemplate jdbcTemplate;
public Service(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
public long check() {
return jdbcTemplate.queryForObject("SELECT COUNT(*) FROM table", Long.class);
}
}
JavaTest.java (works fine)
@RunWith(MockitoJUnitRunner.class)
public final class JavaTest {
@Mock
private JdbcTemplate jdbcTemplate;
@InjectMocks
private Service testSubject;
@Test
public void test() {
//given
when(jdbcTemplate.queryForObject(anyString(), eq(Long.class))).thenReturn(1L);
//when
long result = testSubject.check();
//then
assertThat(result, is(1L));
}
}
KotlinTest.kt (not working)
@RunWith(MockitoJUnitRunner::class)
class KotlinTest {
@Mock
private lateinit var jdbcTemplate: JdbcTemplate
@InjectMocks
private lateinit var testSubject: Service
@Test
fun test() {
//given
`when`(jdbcTemplate.queryForObject(anyString(), eq(Long::class.java))).thenReturn(1L)
//when
val result = testSubject.check()
//then
assertThat(result, `is`(1L))
}
}
Kotlin test fails with NullPointerException:
java.lang.NullPointerException
at c.i.Service.check(Service.java:13)
at c.i.KotlinTest.test(KotlinTest.kt:30)
Also, MockitoHint says:
[MockitoHint] KotlinTest.test (see javadoc for MockitoHint):
[MockitoHint] 1. Unused... -> at org.springframework.jdbc.core.JdbcTemplate.queryForObject(JdbcTemplate.java:500)
[MockitoHint] ...args ok? -> at org.springframework.jdbc.core.JdbcTemplate.queryForObject(JdbcTemplate.java:500)
Can someone describe what is happening here? I'm quite new to Kotlin and might miss something.
Dependencies version: Kotlin 1.1.3-2, Mockito 2.7.19
Please use the KClass#javaObjectType instead, for example:
// use java.lang.Long rather than long ---v
when(jdbcTemplate.queryForObject(anyString(), eq(Long::class.javaObjectType)))
.thenReturn(1L)
Why does this error occurs?
This is because Long::class.java
returns a primitive type long
class rather than a java.lang.Long
class. For example:
println(Long::class.java.name) // long
println(Long::class.javaObjectType.name) // java.lang.Long
println(Long::class.javaObjectType == Long::class.java)
// ^--- false: their class are different
The mocked method parameters matcher is [String, Class<
long
>]
in the Kotlin test code. When mockito can not find the matched method [String, Class<
Long
>]
for mocking in Java Service
class, then it will return a default value for the mismatched getForObject
method call, but the return type of the getForObject
method is Object
- so a null value is returned by default.
However, the return type of the check
method is long
, and the JVM tries unboxing null
into the primitive type long in your Service
class - which causes a NullPointerException
to be thrown. For example:
when(jdbcTemplate.queryForObject(anyString(), eq(Long::class.java)))
.thenReturn(1L)
assertEquals(1, jdbcTemplate.queryForObject("<any>", Long::class.java))
// ^--- matched: return 1
assertNull(jdbcTemplate.queryForObject("<any>", Long::class.javaObjectType))
// ^--- mismatched: return null
testSubject.check()
// ^--- throws NullPointerException
IF you will replace the usage of the Long
class with long.class
- you will also get the same error. For example:
// use long.class rather than Long.class ---v
when(jdbcTemplate.queryForObject(anyString(), eq(long.class))).thenReturn(1L);
// v--- matched: return 1L
assertThat(jdbcTemplate.queryForObject("<any>", long.class), is(1L));
try {
// v--- mismatched: return null
long value = jdbcTemplate.queryForObject("<any>", Long.class);
// ^--- throws NullPointerException when doing unboxing operation
fail();
} catch (NullPointerException expected) {
assertTrue(true);
}
链接地址: http://www.djcxy.com/p/96852.html
上一篇: 什么是“要求:真”包装