Why can't Mockito make fun of the generic parameter type with the number type in Kotlin?

We translate our project into the language of Kotlin. We decided to start with the tests, but we encountered 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 great)

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

The Kotlin test fails with a NullPointerException:

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

In addition, 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 going on here? I am completely new to Kotlin and can miss something.

Dependency Version: Kotlin 1.1.3-2, Mockito 2.7.19

+5
source share
1 answer

Use KClass # javaObjectType instead , for example:

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

Why is this error occurring?

This is because Long::class.java returns the primitive type long , and not the class java.lang.Long . eg:

 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 

The mocked parameters subroutine is [String, Class< long >] in the Kotlin test code. when mockito cannot find the associated [String, Class< long >] method for mocking in the Java Service class, then return the default value for the inappropriate getForObject method, but the return type of the getForObject method is Object , so null The value is returned by default.

However, the return type of the check method is long , and the JVM tries to unpack the null primitive type long in your Service class, then a NullPointerException was 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 replace Java test code long.class , you 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); } 
+2
source

Source: https://habr.com/ru/post/1269949/


All Articles