java - Mockito 模拟在未告知时调用实际实现

标签 java spring unit-testing mockito

我有一个包含以下三个方法的类:

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/

相关文章:

spring - 如何关闭 ThreadPoolTask​​Executor?好办法

unit-testing - 单元测试中的随机数据?

javascript - Angular 2 和服务单元测试

python - Autospec 模拟未正确保留签名?

java - 错误 java.lang.RuntimeException : Unable to start activity

java - 如何验证使用 Spring SFTP 下载的文件的校验和

java - 基于配置文件 Spring Boot 的 Autowiring 实现

如果发生故障,Spring Kafka自动提交偏移

java - java中如何使URL有效?

java - 如何使用 Mockito 模拟在外部方法中创建的对象?