为什么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 c.i.Service.check(Service.java:13)
    at c.i.KotlinTest.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: their class are different

模拟的方法的参数匹配是[String, Class< long >]在科特林测试代码。 当的Mockito找不到匹配的方法[String, Class< Long >]在Java 嘲讽 Service类,那么它会返回一个默认值 不匹配 getForObject方法调用,而是返回类型getForObject方法是Object -这样一值是默认返回的。

但是, check方法的返回类型很long ,并且JVM尝试将unboxing null放入Service类中的原始类型long中 - 这会导致抛出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替换Long类的long.class - 您也将得到相同的错误。 例如:

//      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/96851.html

上一篇: Why Mockito can't mock a generic parameter type with number type in Kotlin?

下一篇: How large are the steps on onAdjustVolume of the VolumeProvider?