使用`verify`或使用`when`隐式检查函数调用参数?

时间:2017-01-20 17:32:16

标签: java unit-testing testing mockito

使用Mockito检查正确参数到位的最佳方法是什么?

考虑下一个单元测试:

@Test
public void getProjectByIdTest() {
    Long projectId = 1L;
    ProjectEntity expectedProject = mock(ProjectEntity.class);
    when(projectRepository.findOne(anyLong())).thenReturn(expectedProject);

    assertThat(projectService.getById(projectId), is(expectedProject));
    verify(projectRepository).findOne(projectId);
}

我们检查projectService明确将verify的论点传递到正确的地方{/ 1}}。

现在检查一下这个单元测试:

@Test
public void getProjectByIdTest() {
    Long projectId = 1L;
    ProjectEntity expectedProject = mock(ProjectEntity.class);
    when(projectRepository.findOne(projectId)).thenReturn(expectedProject);

    assertThat(projectService.getById(projectId), is(expectedProject));
}

它还会检查projectService将其参数传递到正确位置,但隐含地使用when(因此如果projectService实际将一些随机数传递给projectRepository.findOne(),{ {1}}将失败,因为mock将返回错误的值。)

那应该怎么做呢?在我看来,没有assertThat这个测试就会失去一些清晰度;但另一方面它更短。

3 个答案:

答案 0 :(得分:1)

你想测试什么?你应该尝试测试你的测试单元(这里,你的测试方法)是否正常工作,并且只验证重要的交互那份总合同。

@Test
public void getProjectByIdTest_withVerify() {
    Long projectId = 1L;
    ProjectEntity expectedProject = mock(ProjectEntity.class);

    // Your simulated dependency returns expectedProject for every ID?
    when(projectRepository.findOne(anyLong())).thenReturn(expectedProject);

    assertThat(projectService.getById(projectId), is(expectedProject));

    // Is it a requirement of your test that this method must be called?
    // Maybe your system someday calls a "findAll" method, or caches values.
    verify(projectRepository).findOne(projectId);
}

比较

@Test
public void getProjectByIdTest() {
    Long projectId = 1L;
    ProjectEntity expectedProject = mock(ProjectEntity.class);

    // You return the expectedProject when asked for. Everything else returns null.
    when(projectRepository.findOne(projectId)).thenReturn(expectedProject);

    // You check that the return value is correct, which implies the call succeeded.    
    assertThat(projectService.getById(projectId), is(expectedProject));
}

verify交互肯定有它们的位置,特别是对于ArgumentCaptors和特定方法调用是合同一部分的交互,例如服务器或RPC调用或void方法调用。

对于一个字面权威的观点,请阅读Mockito的创始人Szczepan Faber撰写的文章"Is there a difference between asking and telling?"。它提供了许多细节和洞察力,可以从期望中推断出什么是值得验证的。

答案 1 :(得分:0)

小言。您的模拟行为记录在两个示例之间不对称:

when(projectRepository.findOne(anyLong())).thenReturn(expectedProject);

when(projectRepository.findOne(projectId)).thenReturn(expectedProject);

在两种情况下都应该使用when(projectRepository.findOne(projectId))来查看你的断言。

  

那应该怎么做呢?在我看来,没有那个验证   这个测试失去了一些清晰度;但另一方面它更短。

就我个人想要模拟的方法没有返回类型而言,我个人认为verify()是一个降级解决方案。
在某些情况下,它至少允许检查依赖关系是否被调用,并且它具有预期的参数。

如果模拟依赖项的方法返回某些内容,就像在您的情况下那样,从单元测试的角度来看,第二种解决方案似乎更自然。

当你写这样的东西时:

when(projectRepository.findOne(anyLong())).thenReturn(expectedProject);
assertThat(projectService.getById(projectId), is(expectedProject));
verify(projectRepository).findOne(projectId);

您给人的印象是您想要模拟依赖关系存储库,但您还需要检查存储库上的每个被调用方法。

首先它是多余的,因为如果 这个断言是正确的:assertThat(projectService.getById(projectId), is(expectedProject));,这意味着使用期望的参数调用了存储库并返回了预期的项目。为什么要使用verify(projectRepository).findOne(projectId);再次查看?

其次,单元测试的目标不是检查依赖项上的每个方法是否被调用。它在断言方面没有带来很多东西,你强烈地将单元测试与依赖的api结合在一起,你会让你测试更复杂。

答案 2 :(得分:-1)

因正确原因而失败并且后,UnitTest的第三个最重要的事情是是可读的

测试的读者应该很容易理解测试失败的原因。因此,将模拟配置为尽可能宽容,并检查验证中的行为。