如何 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 个概念:

  1. 嘲讽
  2. 断言

做你需要做的唯一方法是同时模拟 MyObjectMyObjectDAO。在这种情况下,您可以验证是否在 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);

施工。

一些注意事项:

  1. 这将保证不再有对 setComment()
  2. 的其他调用
  3. 如果我们将 @Spy 用于 MyObject 而不是 @Mock,我们可以验证 MyObject 实例 [=34= 中注释的最终值是多少]
  4. 这将保证不会对传递的实例执行其他调用