Mockito isNotNull传递null
提前感谢您的帮助- 我是mockito的新手,但我花了最后一天的时间查看示例和文档,但还没有找到解决问题的方法,所以希望这不是一个太愚蠢的问题 我想验证deleteLogs()是否对每个标记为删除的路径调用deleteLog(Path)NUM_LOGS_以_DELETE多次。我不关心mock中的路径是什么(因为我不想去文件系统、集群等进行测试),所以我验证deleteLog是否调用NUM_LOGS_to_DELETE times,并使用任何非空路径作为参数。但是,当我单步执行时,deleteLog会被传递一个null参数——这会导致一个NullPointerException(基于我继承的代码的行为) 也许我做错了什么,但是验证和使用isNotNull似乎很简单……下面是我的代码:Mockito isNotNull传递null,mockito,Mockito,提前感谢您的帮助- 我是mockito的新手,但我花了最后一天的时间查看示例和文档,但还没有找到解决问题的方法,所以希望这不是一个太愚蠢的问题 我想验证deleteLogs()是否对每个标记为删除的路径调用deleteLog(Path)NUM_LOGS_以_DELETE多次。我不关心mock中的路径是什么(因为我不想去文件系统、集群等进行测试),所以我验证deleteLog是否调用NUM_LOGS_to_DELETE times,并使用任何非空路径作为参数。但是,当我单步执行时,deleteLo
MonitoringController mockController=mock(MonitoringController.class);
//调用我要验证其行为的函数
mockController.deleteLogs();
//验证mockController调用deleteLog的次数是否正确
验证(mockController,Mockito.times(NUM_LOGS_TO_DELETE)).deleteLog(isNotNull(Path.class));
再次感谢我从未对参数使用isNotNull,因此我无法真正说出您的代码出了什么问题-我总是使用ArgumentCaptor。基本上,您可以告诉它要查找什么类型的参数,它会捕获它们,然后在调用之后,您可以断言您要查找的值。请尝试以下代码:
ArgumentCaptor<Path> pathCaptor = ArgumentCaptor.forClass(Path.class);
verify(mockController, Mockito.times(NUM_LOGS_TO_DELETE)).deleteLog(pathCaptor.capture());
for (Path path : pathCaptor.getAllValues()) {
assertNotNull(path);
}
ArgumentCaptor-pathCaptor=ArgumentCaptor.forClass(Path.class);
验证(mockController,Mockito.times(NUM_LOGS_TO_DELETE)).deleteLog(pathCaptor.capture());
for(路径:pathCaptor.getAllValues()){
assertNotNull(路径);
}
事实证明,isNotNull是一个返回null的方法,这是经过深思熟虑的,因此,我们或多或少地希望所有匹配器返回伪值,如null
或0,并将其期望值记录在Mockito框架内的堆栈中
意外的是,MonitoringController.deleteLog
实际上是在调用代码,而不是调用Mockito的验证代码。通常发生这种情况是因为deleteLog
是final
:Mockito通过子类(实际上是动态代理)工作,并且因为final
禁止子类化,编译器基本上跳过虚拟方法查找,直接将调用内联到实现,而不是Mockito的mock。再次检查您试图存根或验证的方法是否不是final
,因为您指望它们在测试中不会表现为final
在测试中直接调用mock上的方法几乎是不正确的;如果这是一个MonitoringControllerTest,您应该使用一个真正的MonitoringController并模拟它的依赖项。我希望您的
mockController.deleteLogs()
只是为了代替您的实际测试代码,在测试代码中,您可以使用依赖于MonitoringController并与之交互的其他组件
大多数测试根本不需要模拟。假设你有这个班:
class MonitoringController {
private List<Log> logs = new ArrayList<>();
public void deleteLogs() {
logs.clear();
}
public int getLogCount() {
return logs.size();
}
}
但您的监控控制器也可能是这样的:
class MonitoringController {
private final LogRepository logRepository;
public MonitoringController(LogRepository logRepository) {
// By passing in your dependency, you have made the creator of your class
// responsible. This is called "Inversion-of-Control" (IoC), and is a key
// tenet of dependency injection.
this.logRepository = logRepository;
}
public void deleteLogs() {
logRepository.delete(RecordMatcher.ALL);
}
public int getLogCount() {
return logRepository.count(RecordMatcher.ALL);
}
}
突然间,测试代码可能就不那么容易了,因为它没有保持自己的状态。要使用与上面相同的测试,您需要一个工作日志存储库。您可以编写一个FakeLogRepository,将内容保存在内存中,这是一个很好的策略,或者您可以使用Mockito为您制作一个mock:
@Test public void deleteLogsShouldCallRepositoryDelete() {
LogRepository mockLogRepository = Mockito.mock(LogRepository.class);
MonitoringController controllerUnderTest =
new MonitoringController(mockLogRepository);
controllerUnderTest.deleteLogs();
// Now you can check that your REAL MonitoringController calls
// the right method on your MOCK dependency.
Mockito.verify(mockLogRepository).delete(Mockito.eq(RecordMatcher.ALL));
}
这显示了Mockito的一些优点和局限性:
- 您不再需要实现来保持状态。您甚至不需要
就可以存在getLogCount
- 您还可以跳过创建日志,因为您测试的是交互,而不是状态
- 您与MonitoringController的实现有着更紧密的联系:您不能简单地测试它是否遵守其总合同
- Mockito可以存根单个交互,但要使它们保持一致是很困难的。如果希望
返回2,直到调用LogRepository.count
,然后返回0,这将很难用Mockito表示。这就是为什么编写假实现来表示有状态对象并将Mockito mock留给无状态服务接口是有意义的delete
@Test public void deleteLogsShouldCallRepositoryDelete() {
LogRepository mockLogRepository = Mockito.mock(LogRepository.class);
MonitoringController controllerUnderTest =
new MonitoringController(mockLogRepository);
controllerUnderTest.deleteLogs();
// Now you can check that your REAL MonitoringController calls
// the right method on your MOCK dependency.
Mockito.verify(mockLogRepository).delete(Mockito.eq(RecordMatcher.ALL));
}