使用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
这个测试就会失去一些清晰度;但另一方面它更短。
答案 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的第三个最重要的事情是是可读的。
测试的读者应该很容易理解测试失败的原因。因此,将模拟配置为尽可能宽容,并检查验证中的行为。