我有一个包含以下三个方法的类:
void add(Service... objs)
void add(Collection<Service> objs)
void add(Stream<Service> objs)
正如您所期望的,这些都支持添加零个或多个对象,这些对象可以单独指定,也可以作为数组、集合或流的一部分指定。前两个变体根据它们的参数创建一个流,并将它们传递给实际执行添加操作的第三个变体。
在测试使用此类的对象时,我使用 Spring 的 @MockBean 注释创建了一个 Mockito 模拟对象来表示此类的实例。我可以在调试器中看到被测试的对象包含模拟对象,并且我期望的调用(带有 Service 类型的单个参数)正在发送给模拟。因为应该调用的方法是第一个变体(可变参数),并且我知道可变参数参数有点棘手,所以我编写了测试来检查是否使用正确的参数调用了模拟,如下所示:
ArgumentCaptor<Service> captor = ArgumentCaptor.forClass(Service.class);
verify(theMock).add(captor.capture());
assertThat(captor.getAllValues()).containsExactly(expectedService);
但是,当我运行此代码时,断言失败,因为 captor.getAllValues() 返回的列表不包含服务,而是包含流:失败消息显示:
java.lang.AssertionError:
Expecting:
<[java.util.stream.ReferencePipeline$Head@2cfe272f]>
to contain exactly (and in same order):
<[com.xxx.data.Service@37c5]>
but some elements were not found:
<[com.xxx.data.Service@37c5]>
and others were not expected:
<[java.util.stream.ReferencePipeline$Head@2cfe272f]>
当我在调试器中运行代码时,我可以看到从被测对象到add(Service...)
的调用调用真正的实现;这会调用 add(Stream<Service>)
这就是被模拟拦截的调用。这解释了为什么我看到失败,但我不明白为什么模拟无法拦截原始调用,或者我可以做些什么来让它这样做。
最佳答案
更新您的 ArgumentCaptor 以接受 Service[]
ArgumentCaptor<Service[]> serviceCaptor = ArgumentCaptor.forClass(Service[].class);
并断言
Service[] actualServices = serviceCaptor.getAllValues();
assertEquals(actualServices.length, 1);
assertEquals(actualServices[0], service);
最佳实践是在 Junit 中使用 ErrorCollector 来断言多个
在 Testng 中断言和 SoftAssect 并在断言后调用 softAssert.assertAll()
关于java - Mockito 模拟在未告知时调用实际实现,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/61662001/