我正在尝试为以下服务方法编写单元测试:
public CommandDTO update(UUID uuid, QuantityRequest request) {
Quantity quantity = quantityRepository.findByUuid(uuid)
.orElseThrow(() -> new EntityNotFoundException(QUANTITY));
Quantity updated = saveQuantity(quantity, request);
return CommandDTO.builder().uuid(updated.getUuid()).build();
}
private Quantity saveQuantity(Quantity quantity, QuantityRequest request) {
//map fields (code omitted for brevity)
return quantityRepository.save(quantity);
}
我使用
ArgumentCaptor
,以便捕获我的服务方法调用的私有方法中的 quantity
参数:quantityRepository.save(quantity)
。
@Test
public void test() {
Quantity quantity = new Quantity();
QuantityRequest request = new QuantityRequest();
request.setQuantity(100);
when(quantityRepository.findByUuid(uuid)).thenReturn(Optional.of(quantity));
// It seems to be meaningless this stubbing. because I already stb it in verify method below
when(quantityRepository.save(any())).thenReturn(quantity);
quantityService.update(uuid, request);
verify(quantityRepository).save(quantityCaptor.capture());
Quantity captured = quantityCaptor.getValue();
// assertions...
}
测试正在工作,但是如果我删除
when(quantityRepository.save(any())).thenReturn(quantity);
行,它会抛出“空指针异常错误”,因为在这种情况下,更新方法中的 updated
参数为空。那么,我是否必须在 when()
方法中使用提到的存根?我认为我不需要它,因为验证已经通过 verify(quantityRepository).save(quantityCaptor.capture())
执行该任务。这是真的吗?
不,你需要这里的存根。您无法删除
when(save)
调用,因为您的测试取决于 save
的返回值。但是,您质疑是否需要对任何给定的事物进行存根和验证,这是正确的。
你是对的,验证你存根的东西通常是多余的,文档
verify
告诉你这一点:
虽然可以验证存根调用,但通常它只是多余的。假设您已存根
。如果您的代码关心foo.bar()
返回什么,那么其他东西就会中断(通常在foo.bar()
执行之前)。如果您的代码不关心verify()
返回什么,那么它不应该被存根。foo.bar()
Mockito 的原作者 Szczepan Faber 在“询问与讲述”中引用了 Aaron Jensen 的话:
如果您正在验证,则不需要存根,除非该方法返回对测试(或代码)流程至关重要的内容,在这种情况下,您实际上不需要验证,因为流程会已验证。
一般来说,如果您对某些内容进行了存根,那么您可以在最后进行测试断言,并且不需要测试该方法是否被调用 - 断言将会失败。如果您验证某个方法被调用,但没有人关心结果,或者结果直接传递回调用者,那么您可能不需要存根,因为 Mockito 的默认返回值(如
0
和 null
)应该可以工作很好。
在这里,这些都不是真的:默认的
null
值会导致 NPE,并且您需要 verify
因为这是从 ArgumentCaptor 中获取值的唯一受支持的方法。这意味着您调用 verify
,但不是为了实际验证,而是为了从 ArgumentCaptor 中获取值。部分 verify
调用是多余的,但没有其他实用方法可以到达必要的 ArgumentCaptor 部分,因此您的代码对于 when
和 verify
都很好。
问题出在以下几行:
Quantity updated = saveQuantity(quantity, request);
return CommandDTO.builder().uuid(updated.getUuid()).build();
本质上与:
相同 Quantity updated = quantityRepository.save(quantity)
return CommandDTO.builder().uuid(updated.getUuid()).build();
存根是必要的,因为当您调用
save
时,您期望 updated.getUuid()
方法返回一些内容。
如果没有存根,updated
为空,并且您的调用结果为 NullPointerException
。