Mockito - Kotlin 测试在尝试捕获可分页参数时抛出空指针异常
Mockito - Kotlin test throws Null Pointer Exception when trying to capture Pageable argument
我已经使用 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)
我无法理解,因为当我使用类似的代码直接在服务层上测试该方法时,它通过了测试。
我有一个解决此测试的方法,但我只是想了解为什么这不起作用。非常感谢您对此提供帮助。
如果您希望我添加任何其他信息,请随时告诉我。
问题是 getItemsBasedOnCategoryID
的 pageable
参数是不可空的,而 ArgumentCaptor.capture
的 return 类型是 platform type ,它被 Kotlin 编译器认为可能为空(实际上 capture() returns 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()
参数匹配器时(同样的故事,null- 与非 null 不匹配)
选项 2:DIY:
- 首先,明确声明 ArgumentCapture 的类型参数不可为空:
val pageableCaptor: ArgumentCaptor<Pageable> = ArgumentCaptor.forClass(Pageable::class.java)
没有显式声明,pageableCaptor
的类型是ArgumentCaptor<Pageable!>!
,即平台类型。
- 那么你将需要一个辅助函数:
@Suppress("UNCHECKED_CAST")
private fun <T> capture(captor: ArgumentCaptor<T>): T = captor.capture()
它似乎是一个无操作函数,但重点是它不再是 return 平台类型:如果 ArgumentCaptor 的类型参数不可为空,那么函数的类型也是return 值。
- 最后使用这个函数代替
ArgumentCaptor.capture()
:
Mockito.verify(itemService).getItemsBasedOnCategoryID(captor.capture(), capture(pageableCaptor))
现在 Kotlin 编译器认为 capture(pageableCaptor)
永远不会 return 为 null,因此它不会生成任何 null 检查。
我已经使用 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)
我无法理解,因为当我使用类似的代码直接在服务层上测试该方法时,它通过了测试。 我有一个解决此测试的方法,但我只是想了解为什么这不起作用。非常感谢您对此提供帮助。
如果您希望我添加任何其他信息,请随时告诉我。
问题是 getItemsBasedOnCategoryID
的 pageable
参数是不可空的,而 ArgumentCaptor.capture
的 return 类型是 platform type ,它被 Kotlin 编译器认为可能为空(实际上 capture() returns 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()
参数匹配器时(同样的故事,null- 与非 null 不匹配)
选项 2:DIY:
- 首先,明确声明 ArgumentCapture 的类型参数不可为空:
val pageableCaptor: ArgumentCaptor<Pageable> = ArgumentCaptor.forClass(Pageable::class.java)
没有显式声明,pageableCaptor
的类型是ArgumentCaptor<Pageable!>!
,即平台类型。
- 那么你将需要一个辅助函数:
@Suppress("UNCHECKED_CAST")
private fun <T> capture(captor: ArgumentCaptor<T>): T = captor.capture()
它似乎是一个无操作函数,但重点是它不再是 return 平台类型:如果 ArgumentCaptor 的类型参数不可为空,那么函数的类型也是return 值。
- 最后使用这个函数代替
ArgumentCaptor.capture()
:
Mockito.verify(itemService).getItemsBasedOnCategoryID(captor.capture(), capture(pageableCaptor))
现在 Kotlin 编译器认为 capture(pageableCaptor)
永远不会 return 为 null,因此它不会生成任何 null 检查。