Kotlin如何在单元测试中模拟泛型类?

huangapple go评论76阅读模式
英文:

Kotlin How to mock generic class in unit test?

问题

我正在尝试在 Kotlin 中模拟 MongoCollection<Document> 类

```kotlin
class MyTest {

  val mockCollection: MongoCollection<Document> = mock(MongoCollection<Document>::class.java)

}

它会报错 只允许在类文字的左侧使用类

在进行一些研究后,我找到了这个解答:
https://stackoverflow.com/questions/46401104/only-classes-are-allowed-on-the-left-hand-side-of-a-class-literal
我尝试为 MongoCollection 创建一个类型,然后将其传递给模拟对象,但是它会报错,因为模拟对象是 Type 类型。

我还尝试将模拟对象强制转换为 Document,如下所示:

val mockCollection: MongoCollection<Document> = mock(MongoCollection::class.java) as MongoCollection<Document>

但是在代码实现内部访问 MongoCollection 时会抛出 NullpointerException 异常。

我已经尝试过以下两个库:

它们都出现了相同的错误。

我尝试在 Java 中编写相同的测试,泛型的强制转换在其中是有效的。

MongoCollection<Document> mockCollection = (MongoCollection<Document>) mock(MongoCollection.class);

有人有在 Kotlin 中模拟泛型类的经验吗?


<details>
<summary>英文:</summary>

I am trying to mock MongoCollection&lt;Document&gt; class in kotlin

class MyTest {

val mockCollection: MongoCollection<Document> = mock(MongoCollection<Document>::class.java)

}

it given an error **only classes are allowed on the left side of a class literal**

on researching a bit I found this
https://stackoverflow.com/questions/46401104/only-classes-are-allowed-on-the-left-hand-side-of-a-class-literal
I tried create a type for **MongoCollection&lt;Document&gt;** and then passing it to the mock but it gives an error as the mock is of **Type**.

I also tried casting the mock to Document as below

val mockCollection: MongoCollection<Document> = mock(MongoCollection::class.java) as MongoCollection<Document>

but that gives an **NullpointerException** exception during access of MongoCollection inside implementation of code.
 
I have tried both the 
* Mockito kotlin library -https://mvnrepository.com/artifact/com.nhaarman.mockitokotlin2/mockito-kotlin/2.2.0
* Java mockito core - https://mvnrepository.com/artifact/org.mockito/mockito-core/3.5.13

and both of them have the same error?

I tired writing the same test in java and casting of generics work in it.

MongoCollection<Document> mockCollection = (MongoCollection<Document>) mock(MongoCollection.class);

 


Does anyone have any experience of mocking generic classes in Kotlin?


</details>


# 答案1
**得分**: 1

我为您编写了以下示例:

```kotlin
interface Mosi<T> {
    fun mos(): T
}

然后下面的三个代码片段都适用于测试,测试都通过了:

class ExampleUnitTest {

    val mosi = Mockito.mock(Mosi::class.java)
    @Test
    fun test() {
        `when`(mosi.mos()).thenReturn("Mosi")
        assertEquals(mosi.mos(), "Mosi")
    }
}
class ExampleUnitTest {

    val mosi = Mockito.mock(Mosi::class.java) as Mosi<String>
    @Test
    fun test() {
        `when`(mosi.mos()).thenReturn("Mosi")
        assertEquals(mosi.mos(), "Mosi")
    }
}
class ExampleUnitTest {

    val mosi = mock<Mosi<String>> {
        on { mos() }
            .thenReturn("Mosi")
    }
    @Test
    fun test() {
        assertEquals(mosi.mos(), "Mosi")
    }
}

如果出现了 NPE(空指针异常),问题可能出在其他地方,也许提供更多的代码可以帮助解决问题!

英文:

I wrote the following example for you:

interface Mosi&lt;T&gt; {
    fun mos(): T
}

then all three code snippets below work for testing and test are passed:

class ExampleUnitTest {

    val mosi = Mockito.mock(Mosi::class.java)
    @Test
    fun test() {
        `when`(mosi.mos()).thenReturn(&quot;Mosi&quot;)
        assertEquals(mosi.mos(), &quot;Mosi&quot;)
    }
}
class ExampleUnitTest {

    val mosi = Mockito.mock(Mosi::class.java) as Mosi&lt;String&gt;
    @Test
    fun test() {
        `when`(mosi.mos()).thenReturn(&quot;Mosi&quot;)
        assertEquals(mosi.mos(), &quot;Mosi&quot;)
    }
}
class ExampleUnitTest {

    val mosi = mock&lt;Mosi&lt;String&gt;&gt; {
        on { mos() }
            .thenReturn(&quot;Mosi&quot;)
    }
    @Test
    fun test() {
        assertEquals(mosi.mos(), &quot;Mosi&quot;)
    }
}

if you get NPE the problem is somewhere else, maybe providing more code can help!

答案2

得分: 0

解决方案
---
创建一个类似于 Mockito 的扩展:

    inline fun <reified T: Any> mock() = Mockito.mock(T::class.java)

用法
---
像这样使用扩展函数:

    val mockCollection = mock<MongoCollection<Document>>()

    val mockCollection: MongoCollection<Document> = mock() // 我更喜欢这个

我总是使用这个而不是原始的 `mock()` 函数,因为可以推断出 Java 类。节省了一些输入。

题外话
---

甚至用于模拟 Lambdas 也非常好!

    val callback: () -> Unit = mock()

    someClass.doSomething(callback)

    verify(callback).invoke()
英文:

Solution

Create an extension on Mockito like this:

inline fun &lt;reified T: Any&gt; mock() = Mockito.mock(T::class.java)

Usage

Use the extension function like this:

val mockCollection = mock&lt;MongoCollection&lt;Document&gt;&gt;()

val mockCollection: MongoCollection&lt;Document&gt; = mock() // I prefer this one

I always use this instead of the original mock() function since the java class can be inferred. Saves some typing.

Off-topic

It's even great for mocking Lambdas!

val callback: () -&gt; Unit = mock()

someClass.doSomething(callback)

verify(callback).invoke()

huangapple
  • 本文由 发表于 2020年10月16日 22:50:27
  • 转载请务必保留本文链接:https://go.coder-hub.com/64391483.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定