如何 JUnit 测试我的模拟方法是用我想要的参数调用的(不仅仅是 id,还有深度相等匹配器)?
How to JUnit test that my mocked method is called with the parameter i want (not just id, but deep equal matcher)?
这是我要测试的方法的一部分:
protected void myMethod(MyObject o) {
o.setComment("comment");
MyObjectDAO.updateMyObject(o);
}
这是我的 JUnit 的一部分:
@Mock
private MyObjectDAO myObjectDaoMock;
@Test
public void testMyMethod() {
MyObject o = new MyObject();
// Run Test
x.myMethod(o);
// Control
assertEquals("comment", o.getComment());
verify(myObjectDaoMock).updateMyObject(o);
}
测试是绿色的;我们现在测试是否设置了myObject的comment属性,用myObject调用update方法。但是不是更新方法是否被调用更新的属性(在setComment方法之后)。当我们改变 myMethod 中两行的位置时(首先更新对象,然后设置其属性),我们的测试仍然是绿色的。
我认为 ArgumentCaptor 在这里很有用。所以我做了这个:
@Mock
private MyObjectDAO myObjectDaoMock;
@Test
public void testMyMethod() {
MyObject o = new MyObject();
// Run Test
x.myMethod(o);
// Control
ArgumentCaptor<MyObject> argumentCaptor = ArgumentCaptor.forClass(MyObject.class);
verify(myObjectDaoMock).updateMyObject(argumentCaptor.capture());
MyObject oActual = argumentCaptor.getValue();
assertEquals("comment", oActual.getComment());
}
...希望 ArgumentCaptor 将捕获调用更新方法的对象状态,因此我可以确定使用更新的评论属性调用更新方法。测试再次为绿色。 但是还是测试的不够巧妙。当我们再次改变myMethod中两行的位置时(先更新对象,再设置它的属性),我们的测试还是绿的。
据我了解,ArgumentCaptor 并没有为自己创建另一个属性(argumentCaptor.getValue()),它是对原始对象的引用。因此,由于 java 与 值引用 一起工作,对于 JUnit,只要 objectIds 相同,无论我是在之前还是之后更新对象都没有任何区别。
我如何实际测试 updateObject 方法是用 myObject 的更新值调用的?
编辑: 另一个想法是(感谢@Crazyjavahacking)也模拟 myObject(虽然我不想要那个) ,并用 2 个模拟定义调用顺序:
InOrder inOrder = inOrder(o, myObjectDAOMock);
inOrder.verify(o).setComment("comment");
inOrder.verify(myObjectDAOMock).updateMyObject(o);
这仍然没有测试是否使用更新后的值调用了该方法。它只是测试订单。当我们更改代码时:
o.setComment("comment");
o.setComment("comment2");
MyObjectDAO.updateMyObject(o);
.. 我们的测试仍然 运行 绿色,因为根据我们的订单定义,订单是正确的。但是该方法并没有像我们想要的那样用更新后的值 "comment" 调用。
它的行为有所不同,因为您混合了 2 个概念:
- 嘲讽
- 断言
做你需要做的唯一方法是同时模拟 MyObject
和 MyObjectDAO
。在这种情况下,您可以验证是否在 MyObject
上调用了 setter 以及调用顺序。在 Mockit 中使用
InOrder inOrder = inOrder(o, myObjectDAOMock);
inOrder.verify(o).setComment("...");
// 1.) inOrder.verify(o, times(0)).setComment(anyString());
inOrder.verify(myObjectDAOMock).updateMyObject(o);
// 2.) assertEquals(o.getComment(), "comment");
// 3.) verifyNoMoreInteractions(o);
施工。
一些注意事项:
- 这将保证不再有对
setComment()
的其他调用
- 如果我们将
@Spy
用于 MyObject
而不是 @Mock
,我们可以验证 MyObject
实例 [=34= 中注释的最终值是多少]
- 这将保证不会对传递的实例执行其他调用
这是我要测试的方法的一部分:
protected void myMethod(MyObject o) {
o.setComment("comment");
MyObjectDAO.updateMyObject(o);
}
这是我的 JUnit 的一部分:
@Mock
private MyObjectDAO myObjectDaoMock;
@Test
public void testMyMethod() {
MyObject o = new MyObject();
// Run Test
x.myMethod(o);
// Control
assertEquals("comment", o.getComment());
verify(myObjectDaoMock).updateMyObject(o);
}
测试是绿色的;我们现在测试是否设置了myObject的comment属性,用myObject调用update方法。但是不是更新方法是否被调用更新的属性(在setComment方法之后)。当我们改变 myMethod 中两行的位置时(首先更新对象,然后设置其属性),我们的测试仍然是绿色的。
我认为 ArgumentCaptor 在这里很有用。所以我做了这个:
@Mock
private MyObjectDAO myObjectDaoMock;
@Test
public void testMyMethod() {
MyObject o = new MyObject();
// Run Test
x.myMethod(o);
// Control
ArgumentCaptor<MyObject> argumentCaptor = ArgumentCaptor.forClass(MyObject.class);
verify(myObjectDaoMock).updateMyObject(argumentCaptor.capture());
MyObject oActual = argumentCaptor.getValue();
assertEquals("comment", oActual.getComment());
}
...希望 ArgumentCaptor 将捕获调用更新方法的对象状态,因此我可以确定使用更新的评论属性调用更新方法。测试再次为绿色。 但是还是测试的不够巧妙。当我们再次改变myMethod中两行的位置时(先更新对象,再设置它的属性),我们的测试还是绿的。
据我了解,ArgumentCaptor 并没有为自己创建另一个属性(argumentCaptor.getValue()),它是对原始对象的引用。因此,由于 java 与 值引用 一起工作,对于 JUnit,只要 objectIds 相同,无论我是在之前还是之后更新对象都没有任何区别。
我如何实际测试 updateObject 方法是用 myObject 的更新值调用的?
编辑: 另一个想法是(感谢@Crazyjavahacking)也模拟 myObject(虽然我不想要那个) ,并用 2 个模拟定义调用顺序:
InOrder inOrder = inOrder(o, myObjectDAOMock);
inOrder.verify(o).setComment("comment");
inOrder.verify(myObjectDAOMock).updateMyObject(o);
这仍然没有测试是否使用更新后的值调用了该方法。它只是测试订单。当我们更改代码时:
o.setComment("comment");
o.setComment("comment2");
MyObjectDAO.updateMyObject(o);
.. 我们的测试仍然 运行 绿色,因为根据我们的订单定义,订单是正确的。但是该方法并没有像我们想要的那样用更新后的值 "comment" 调用。
它的行为有所不同,因为您混合了 2 个概念:
- 嘲讽
- 断言
做你需要做的唯一方法是同时模拟 MyObject
和 MyObjectDAO
。在这种情况下,您可以验证是否在 MyObject
上调用了 setter 以及调用顺序。在 Mockit 中使用
InOrder inOrder = inOrder(o, myObjectDAOMock);
inOrder.verify(o).setComment("...");
// 1.) inOrder.verify(o, times(0)).setComment(anyString());
inOrder.verify(myObjectDAOMock).updateMyObject(o);
// 2.) assertEquals(o.getComment(), "comment");
// 3.) verifyNoMoreInteractions(o);
施工。
一些注意事项:
- 这将保证不再有对
setComment()
的其他调用
- 如果我们将
@Spy
用于MyObject
而不是@Mock
,我们可以验证MyObject
实例 [=34= 中注释的最终值是多少] - 这将保证不会对传递的实例执行其他调用