Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/json/14.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Mockito isNotNull传递null_Mockito - Fatal编程技术网

Mockito isNotNull传递null

Mockito isNotNull传递null,mockito,Mockito,提前感谢您的帮助- 我是mockito的新手,但我花了最后一天的时间查看示例和文档,但还没有找到解决问题的方法,所以希望这不是一个太愚蠢的问题 我想验证deleteLogs()是否对每个标记为删除的路径调用deleteLog(Path)NUM_LOGS_以_DELETE多次。我不关心mock中的路径是什么(因为我不想去文件系统、集群等进行测试),所以我验证deleteLog是否调用NUM_LOGS_to_DELETE times,并使用任何非空路径作为参数。但是,当我单步执行时,deleteLo

提前感谢您的帮助- 我是mockito的新手,但我花了最后一天的时间查看示例和文档,但还没有找到解决问题的方法,所以希望这不是一个太愚蠢的问题

我想验证deleteLogs()是否对每个标记为删除的路径调用deleteLog(Path)NUM_LOGS_以_DELETE多次。我不关心mock中的路径是什么(因为我不想去文件系统、集群等进行测试),所以我验证deleteLog是否调用NUM_LOGS_to_DELETE times,并使用任何非空路径作为参数。但是,当我单步执行时,deleteLog会被传递一个null参数——这会导致一个NullPointerException(基于我继承的代码的行为)

也许我做错了什么,但是验证和使用isNotNull似乎很简单……下面是我的代码:

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可以存根单个交互,但要使它们保持一致是很困难的。如果希望
    LogRepository.count
    返回2,直到调用
    delete
    ,然后返回0,这将很难用Mockito表示。这就是为什么编写假实现来表示有状态对象并将Mockito mock留给无状态服务接口是有意义的

非常感谢您的回复-我想您已经强调了我对嘲讽和嘲讽的工作方式缺乏理解的几个方面。我想我的主要挣扎是不知道我要莫基托做什么。顺便说一句,这个方法不是最终的,但我相信我做的模拟是错误的。我再看看,不客气!别担心,很好地使用Mockito需要一段时间才能弄清楚。我的简短版本是,有时,为了测试一个组件,您有时必须测试它如何与其他系统交互,因此您使用真实的组件并使用Mockito来模拟其他系统。我强烈推荐Martin Fowler的一篇文章,这篇文章应该能让您了解基于模拟的测试与基于状态的测试的区别。编辑最后一条注释为时已晚—mockController.deleteLogs()是MonitoringController类中的代码(因此我想mockito实际调用该方法并不奇怪?)。所以为了在我的MonitoringControllerTest类中测试MonitoringController,我应该创建一个实际的MonitoringController对象?如果这是案例类成员,我会嘲笑什么?我也会读那篇文章,谢谢!对答案进行了大量编辑,以反映类的设计如何影响是否在类的测试中使用Mockito。希望这有帮助,祝你好运!非常感谢——福勒的文章和你的回复澄清了我对嘲弄的许多误解——这并不像我想的那么简单。我的队员
@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));
}