¿Por qué Mockito no puede burlarse de un tipo de parámetro genérico con el tipo de número en Kotlin?

Estamos moviendo nuestro proyecto al lenguaje Kotlin. Decidimos comenzar con las testings pero enfrentamos un comportamiento extraño.

Aquí está nuestro caso de testing:

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 (funciona bien)

 @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 (no funciona)

 @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)) } } 

La testing de Kotlin falla con NullPointerException:

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

Además, MockitoHint dice:

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

¿Alguien puede describir lo que está sucediendo aquí? Soy bastante nuevo para Kotlin y podría perder algo.

Versión de dependencies: Kotlin 1.1.3-2, Mockito 2.7.19

Por favor, use KClass # javaObjectType en su lugar, por ejemplo:

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

¿ Por qué ocurre este error?

Esto se debe a que Long::class.java devuelve una class long tipo primitivo en lugar de una class java.lang.Long . por ejemplo:

 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 

El parche de parameters de método simulado es [String, Class< long >] en el código de testing de Kotlin. cuando Mockito no puede encontrar el método coincidente [String, Class< Long >] para burlarse en la class de Service Java, devolverá un valor pnetworkingeterminado para la llamada al método getForObject no getForObject , pero el tipo de getForObject método getForObject es Object por lo que es un valor nulo es devuelto por defecto.

Sin embargo, el tipo de retorno del método de check es long , y JVM intenta desempaquetar null a un tipo primitivo largo en su class de Service y luego se arrojó una NullPointerException , por ejemplo:

 `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 

SI reemplaza el código de testing de Java con long.class también obtiene el mismo error, por ejemplo:

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