【问题标题】:Mockito - Kotlin test throws Null Pointer Exception when trying to capture Pageable argumentMockito - Kotlin 测试在尝试捕获 Pageable 参数时抛出空指针异常
【发布时间】:2021-05-02 00:19:17
【问题描述】:

我使用 Mockito 为我的控制器中的方法编写了一个非常简单的测试

@Test
fun `get items based on category ID`() {
    val pageable: Pageable = PageRequest.of(5, 50)
    controller.get(10, pageable)

    val captor = ArgumentCaptor.forClass(Int::class.java)
    val pageableCaptor = ArgumentCaptor.forClass(Pageable::class.java)
    Mockito.verify(itemService).getItemsBasedOnCategoryID(captor.capture(), pageableCaptor.capture())
    assertEquals(captor.value, 10)
    assertEquals(pageableCaptor.value.pageSize, 50)
    assertEquals(pageableCaptor.value.pageNumber, 5)
}

但我得到了这个例外

pageableCaptor.capture() must not be null
java.lang.NullPointerException: pageableCaptor.capture() must not be null
    at com.practice.ItemControllerTest.get items based on category ID(ItemControllerTest.kt:41)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)

我无法理解,因为当我使用类似代码直接在服务层上测试该方法时,它通过了测试。 我有这个测试的解决方法,但我只是想了解为什么这不起作用。我非常感谢您对此提供帮助。

如果您希望我添加任何其他信息,请随时告诉我。

【问题讨论】:

    标签: unit-testing kotlin mockito pageable


    【解决方案1】:

    问题在于getItemsBasedOnCategoryIDpageable 参数不可为空,而ArgumentCaptor.capture 的返回类型是platform type,Kotlin 编译器认为它可能为空(实际上capture() 返回 null,这就是 Mockito 的工作方式)。在这种情况下,编译器会在使用该类型时生成空检查。您可以在测试的反编译代码中看到它:

    @Test
    public final void get_items_based_on_category_ID {
          ...
          Object var10002 = pageableCaptor.capture();
          Intrinsics.checkNotNullExpressionValue(var10002, "pageableCaptor.capture()"); <<- NPE
          var10000.getItemsBasedOnCategoryID(var4, (Pageable)var10002);
          ...
    }
    

    诀窍是以某种方式欺骗编译器以防止它生成空检查。

    选项 1:使用 mockito-kotlin 库。它为此类问题提供了解决方法,并提供了几个额外的工具。这可能是您最好的选择,因为您可能会面临下一个问题,例如当使用 Mockito 的 any() 参数匹配器时(同样的故事,空值与非空值不匹配)

    选项 2:DIY:

    1. 首先,将 ArgumentCapture 的类型参数显式声明为不可为空:
    val pageableCaptor: ArgumentCaptor<Pageable> = ArgumentCaptor.forClass(Pageable::class.java)
    

    如果没有显式声明,pageableCaptor 的类型是ArgumentCaptor&lt;Pageable!&gt;!,即平台类型。

    1. 那么您将需要一个辅助函数:
    @Suppress("UNCHECKED_CAST")
    private fun <T> capture(captor: ArgumentCaptor<T>): T = captor.capture()
    

    这似乎是一个无操作函数,但关键是它不再返回平台类型:如果 ArgumentCaptor 的类型参数不可为空,那么函数返回值的类型也是如此。

    1. 最后用这个函数代替ArgumentCaptor.capture()
    Mockito.verify(itemService).getItemsBasedOnCategoryID(captor.capture(), capture(pageableCaptor))
    

    现在 Kotlin 编译器认为 capture(pageableCaptor) 永远不会返回 null,因此它不会生成任何 null 检查。

    【讨论】:

    • 太棒了!那成功了。非常感谢答案和详细解释。
    猜你喜欢
    • 1970-01-01
    • 2019-02-15
    • 2020-11-13
    • 1970-01-01
    • 2020-03-13
    • 1970-01-01
    • 2017-11-17
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多