Mockito.when().thenReturn()无法使用Kotlin和SpringTest处理@MockBean对



我在测试中使用@MockBean时遇到了一些问题。

这是我的测试课程:

@SpringBootTest
@AutoConfigureMockMvc
@Import(Config::class)
class ControllerTest {
@Autowired
private lateinit var mockMvc: MockMvc
@MockBean
private lateinit var kafkaPublisher: KafkaPublisher
@Test
fun valid payload(){
Mockito.when(kafkaPublisher.sendEventListToKafka(anyList())).thenReturn(Unit)
mockMvc.perform(
post("/publish")
.contentType(MediaType.APPLICATION_JSON)
.content(getPayload()))
.andExpect(status().isOk)
.andExpect(content().string("Published 1 event(s) successfully"))
}
}

其中getPayload((返回将使用Jackson转换为控制器对象的有效负载字符串。

这是我正在测试的控制器:

@Controller
class BumblebeeEventController {
@Autowired
private lateinit var kafkaPublisher: KafkaPublisher
@PostMapping("/publish")
fun eventIntake(@RequestBody @Valid payload: EventList) : ResponseEntity<String>{
kafkaPublisher.sendEventListToKafka(payload)
return ResponseEntity("Published ${payload.events.size} event(s) successfully",HttpStatus.OK)
}
}

我目前收到Mockito.when.then的投诉返回用法:

org.mockito.exceptions.misusing.MissingMethodInvocationException: 
when() requires an argument which has to be 'a method call on a mock'.
For example:
when(mock.getArticles()).thenReturn(articles);
Also, this error might show up because:
1. you stub either of: final/private/equals()/hashCode() methods.
Those methods *cannot* be stubbed/verified.
Mocking methods declared on non-public parent classes is not supported.
2. inside when() you don't call method on mock but on some other object.

at com.ControllerTest.valid payload(ControllerTest.kt:39)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:566)
at org.junit.platform.commons.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:688)
at org.junit.jupiter.engine.execution.MethodInvocation.proceed(MethodInvocation.java:60)
at org.junit.jupiter.engine.execution.InvocationInterceptorChain$ValidatingInvocation.proceed(InvocationInterceptorChain.java:131)
at org.junit.jupiter.engine.extension.TimeoutExtension.intercept(TimeoutExtension.java:149)
at org.junit.jupiter.engine.extension.TimeoutExtension.interceptTestableMethod(TimeoutExtension.java:140)
at org.junit.jupiter.engine.extension.TimeoutExtension.interceptTestMethod(TimeoutExtension.java:84)
at org.junit.jupiter.engine.execution.ExecutableInvoker$ReflectiveInterceptorCall.lambda$ofVoidMethod$0(ExecutableInvoker.java:115)
at org.junit.jupiter.engine.execution.ExecutableInvoker.lambda$invoke$0(ExecutableInvoker.java:105)
at org.junit.jupiter.engine.execution.InvocationInterceptorChain$InterceptedInvocation.proceed(InvocationInterceptorChain.java:106)
at org.junit.jupiter.engine.execution.InvocationInterceptorChain.proceed(InvocationInterceptorChain.java:64)
at org.junit.jupiter.engine.execution.InvocationInterceptorChain.chainAndInvoke(InvocationInterceptorChain.java:45)
at org.junit.jupiter.engine.execution.InvocationInterceptorChain.invoke(InvocationInterceptorChain.java:37)
at org.junit.jupiter.engine.execution.ExecutableInvoker.invoke(ExecutableInvoker.java:104)
at org.junit.jupiter.engine.execution.ExecutableInvoker.invoke(ExecutableInvoker.java:98)
at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.lambda$invokeTestMethod$6(TestMethodTestDescriptor.java:210)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.invokeTestMethod(TestMethodTestDescriptor.java:206)
at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:131)
at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:65)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$5(NodeTestTask.java:139)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$7(NodeTestTask.java:129)
at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:127)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:126)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:84)
at java.base/java.util.ArrayList.forEach(ArrayList.java:1541)
at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:38)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$5(NodeTestTask.java:143)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$7(NodeTestTask.java:129)
at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:127)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:126)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:84)
at java.base/java.util.ArrayList.forEach(ArrayList.java:1541)
at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:38)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$5(NodeTestTask.java:143)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$7(NodeTestTask.java:129)
at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:127)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:126)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:84)
at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.submit(SameThreadHierarchicalTestExecutorService.java:32)
at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor.execute(HierarchicalTestExecutor.java:57)
at org.junit.platform.engine.support.hierarchical.HierarchicalTestEngine.execute(HierarchicalTestEngine.java:51)
at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:108)
at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:88)
at org.junit.platform.launcher.core.EngineExecutionOrchestrator.lambda$execute$0(EngineExecutionOrchestrator.java:54)
at org.junit.platform.launcher.core.EngineExecutionOrchestrator.withInterceptedStreams(EngineExecutionOrchestrator.java:67)
at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:52)
at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:96)
at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:75)
at com.intellij.junit5.JUnit5IdeaTestRunner.startRunnerWithArgs(JUnit5IdeaTestRunner.java:71)
at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:33)
at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:221)
at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:54)

最后是我的KafkaPublisher:

open class KafkaPublisher @Autowired constructor(
private val kafkaTemplate: KafkaTemplate<String, EventAvro>
)  {
fun sendEventListToKafka(eventList: List<Event>){
eventList.forEach {
sendMessageToKafka(UUID.randomUUID().toString(), EventMapper.mapToEventAvro(it))
}
}
fun sendMessageToKafka(uuid: String, message: EventAvro) {
kafkaTemplate.send(kafkaTopic, uuid, message).get(3L, TimeUnit.SECONDS)
LOGGER.info("Successfully published into kafka topic ${kafkaTopic}, message: $message")
}

我有两个问题:

  1. 如果我取出Mockito.when.then返回MockBean Kafka Publisher试图将事件实际发布到Kafka。我想,如果它被嘲笑,它只会有一个最小的实现——例如,返回null,没有任何逻辑
  2. 为什么Mockito不使用这个KafkaPublisher类,认为这可能与它是最终版本有关,所以我把它打开了。不确定它还能是什么。甚至不喜欢用一个真实的对象来代替anyList((,而且any((也不起作用

提前感谢

问题1:模拟单元方法

您正在尝试存根返回Unit的方法。

Mockito.when(kafkaPublisher.sendEventListToKafka(anyList())).thenReturn(Unit)

这是不正确的。要截断void方法,您需要使用doReturn((|doThrow((|doAnswer((|doNothing((| doCallRealMethod((方法族

还要注意,对于void方法,默认操作是什么都不做。因此,正确的方法是完全消除所讨论的存根。

问题2:模拟最终方法

在Kotlin中,默认情况下,所有类和方法都是final。您打开了KafkaPublisher类,但没有打开sendEventListToKafka方法。

Mockito在其默认配置中无法模拟final方法。它可以配置为使用内联mock maker。这就是调用sendEventListToKafka的原始实现的原因。

然而,Kotlin通过Kotlin弹簧插件提供了与弹簧的良好集成

由于支持元注释,用@Configuration、@Controller、@RestController、@Service或@Repository注释的类将自动打开,因为这些注释是用@Component进行元注释的。

如果用@Service注释KafkaPublisher(而不是依赖@Configuration类来生成它(,它将打开类及其方法(我的IDE甚至将open关键字标记为冗余(

最新更新