为什么Mockito不能在Kotlin中使用数字类型来模拟一个通用的参数类型?

我们正在将我们的项目转移到Kotlin语言。 我们决定从测试开始,但面临一些奇怪的行为。

这是我们的测试案例:

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 (工作正常)

 @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 (不工作)

 @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测试失败与NullPointerException:

 java.lang.NullPointerException at ciService.check(Service.java:13) at ciKotlinTest.test(KotlinTest.kt:30) 

另外,MockitoHint说:

 [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) 

有人可以描述这里发生了什么? 我对Kotlin很陌生,可能会错过一些东西。

依赖版本:Kotlin 1.1.3-2,Mockito 2.7.19

请使用KClass#javaObjectType来代替,例如:

 // use java.lang.Long rather than long ---v `when`(jdbcTemplate.queryForObject(anyString(), eq(Long::class.javaObjectType))) .thenReturn(1L) 

为什么会发生这个错误?

这是因为Long::class.java返回一个原始类型的long类而不是java.lang.Long类。 例如:

 println(Long::class.java.name) // long println(Long::class.javaObjectType.name) // java.lang.Long println(Long::class.javaObjectType == Long::class.java) // ^--- false: thier class are different 

模拟方法参数匹配器是Kotlin测试代码中的[String, Class< long >] 。 当mockito无法在Java Service类中找到匹配的方法[String, Class< Long >]进行getForObject ,则会为不匹配的 getForObject方法调用返回一个默认值 ,但getForObject方法的返回类型为Object因此为null值是默认返回的。

但是, check方法的返回类型很long ,而且JVM试图将null 拆箱到您的Service类中的原始类型,然后抛出了NullPointerException ,例如:

 `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 

如果你用long.class替换Java测试代码,你也会得到相同的错误,例如:

 // 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); }