Java 使用“verify”显式检查函数调用参数,或使用“when”隐式检查函数调用参数?
使用Java 使用“verify”显式检查函数调用参数,或使用“when”隐式检查函数调用参数?,java,unit-testing,testing,mockito,Java,Unit Testing,Testing,Mockito,使用Mockito检查正确参数是否位于正确位置的最佳方法是什么 考虑下一个单元测试: @Test public void getProjectByIdTest() { Long projectId = 1L; ProjectEntity expectedProject = mock(ProjectEntity.class); when(projectRepository.findOne(anyLong())).thenReturn(expectedProject);
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);
}
@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
是否使用verify
显式地将其参数传递到正确的位置
现在检查此单元测试:
@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);
}
@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
是否将其参数传递到了正确的位置,但在时隐式地传递了(因此,如果projectService
实际将一些随机数传递到projectRepository.findOne()
,断言将失败,因为mock将返回错误的值)
那么这应该怎么做呢?在我看来,如果没有verify
,这个测试就会失去一些清晰性;但是从另一方面来说,它的长度更短。UnitTest的第三个最重要的事情是可读性,这是在由于正确的原因失败并且速度很快之后
测试的读者应该很容易理解测试失败的原因。因此,将您的模拟配置为尽可能宽容,并检查验证中的行为。UnitTest由于正确的原因失败且速度快后,第三个最重要的事情是可读性
测试的读者应该很容易理解测试失败的原因。因此,将您的模拟配置为尽可能宽容,并检查验证中的行为。请注意。您的mockito行为记录在两个示例之间并不对称:
when(projectRepository.findOne(anyLong())).thenReturn(expectedProject);
及
在这两种情况下,您都应该使用when(projectRepository.findOne(projectd))
来查看您如何进行断言
那么这应该怎么做呢?在我看来,没有这一点
这个测试失去了一些清晰度;但从另一方面看,它较短
个人认为,当我想模拟的方法没有返回类型时,将代码> Valuy()<代码>作为一个降级的解决方案。
在某些情况下,它至少允许检查是否调用了依赖项以及依赖项所具有的预期参数。
如果模拟依赖项的方法返回了一些东西,就像在您的案例中一样,那么从单元测试的角度来看,第二种解决方案似乎更自然
当你这样写的时候:
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(projectd)再次检查它代码>?
其次,单元测试的目标不是检查依赖项上的每个方法是否被调用。它不会带来很多断言方面的东西,您将单元测试与依赖项的api紧密结合,从而使测试变得更加复杂 少说话。您的mockito行为记录在两个示例之间并不对称:
when(projectRepository.findOne(anyLong())).thenReturn(expectedProject);
及
在这两种情况下,您都应该使用when(projectRepository.findOne(projectd))
来查看您如何进行断言
那么这应该怎么做呢?在我看来,没有这一点
这个测试失去了一些清晰度;但从另一方面看,它较短
个人认为,当我想模拟的方法没有返回类型时,将代码> Valuy()<代码>作为一个降级的解决方案。
在某些情况下,它至少允许检查是否调用了依赖项以及依赖项所具有的预期参数。
如果模拟依赖项的方法返回了一些东西,就像在您的案例中一样,那么从单元测试的角度来看,第二种解决方案似乎更自然
当你这样写的时候:
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(projectd)再次检查它代码>?
其次,单元测试的目标不是检查依赖项上的每个方法是否被调用。它不会带来很多断言方面的东西,您将单元测试与依赖项的api紧密结合,从而使测试变得更加复杂 你想测试什么?您应该尝试测试您的测试单元(这里是您的测试方法)是否工作,并且只验证对该总合同很重要的交互
@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
交互肯定有自己的位置,特别是对于ArgumentCaptor和特定方法调用是合同一部分的交互,例如服务器或RPC调用或void方法调用
要获得真正权威的观点,请阅读Mockito创始人Szczepan Faber的文章。它提供了很多细节和见解,说明了从期望中可以推断出什么,哪些是值得验证的。您想测试什么?您应该尝试测试您的测试单元(这里是您的测试方法)是否工作,并且只验证对该总合同很重要的交互
@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
交互肯定有自己的位置,特别是对于ArgumentCaptor和特定方法调用是合同一部分的交互,例如服务器或RPC调用或void方法调用
要获得真正权威的观点,请阅读Mockito创始人Szczepan Faber的文章。它提供了许多细节和见解,说明了从期望中可以推断出什么与哪些值得验证。谢谢,我喜欢你的推理!我把它编码成不对称的