Java Mockito验证交互与验证结果

Java Mockito验证交互与验证结果,java,unit-testing,mockito,Java,Unit Testing,Mockito,Mockito在verify方法上的JavaDocs链接到关于询问和告知。我有点迷茫,特别是“存根交互是隐式验证的” 让我们举一个例子: 想象一个学生上这门课 class FooDao { private EntityManager entityManager; public FooDao(EntityManager entityManager) { this.entityManager = entityManager; } public Li

Mockito在
verify
方法上的JavaDocs链接到关于询问和告知。我有点迷茫,特别是“存根交互是隐式验证的”

让我们举一个例子:

想象一个学生上这门课

class FooDao {
    private EntityManager entityManager;

    public FooDao(EntityManager entityManager) {
        this.entityManager = entityManager;
    }

    public List<Foo> findFooByName(String name, String otherParam) {
        String hql = "SELECT f FROM Foo f WHERE 1 = 1 ";
        if(name != null) {
            hql += " AND f.name = :name";
        }
        if(otherParam != null) {
            hql += " AND f.other = :otherParam";
        }
        TypedQuery<Foo> query = this.entityManager.createQuery(hql, Foo.class);

        if(name != null) {
            query.setParameter("name", name);
        }
        if(otherParam != null) {
            query.setParameter("otherParam", otherParam);
        }
        return query.getResultList();
    }
}
现在,我可以添加如下内容:

@Mock
private TypedQuery<Foo> mockQuery;

@Test
public void testFindFooByName() {
    List<Foo> stubData = stubData(); // Method definition omitted for brevity 
    // Return the custom mockedQuery with the entityManager
    Mockito.when(mockQuery.getResultList()).thenReturn(stubData);
    Mockito.when(entityManager.createQuery(Mockito.anyString(), 
                    Mockito.eq(Foo.class)))
            .thenReturn(mockQuery);
    List<Foo> actual = dao.findFooByName("foobar", null);

    // Now what should I check?
}

第一次验证将确保HQL查询已正确构造,第二次验证将确保参数已正确绑定到查询。这些验证是必需的还是仅仅断言结果就足够了?

您必须存根与
entityManager
的交互,否则当
FindFoodByName()
调用
setParameter()
getQueryList()时,您的测试将导致NPE被抛出

关于是存根还是验证
query.getResultList()
调用的选择取决于您希望测试的具体程度

最不具体的

下面的测试并不具体说明如何创建
TypedQuery
,而是让自己确信它是以某种方式创建的,并且调用了它的
getResultList()
方法

@Test
public void testFindFooByName() {
    List<Foo> stubData = stubData();

    Mockito.when(entityManager.createQuery(Mockito.anyString(), Mockito.eq(Foo.class))).thenReturn(mockQuery);

    dao.findFooByName("foobar", null);

    Mockito.verify(mockQuery).getResultList();
}
最具体的

以下测试证明了以下所有内容(来自您的OP):

结果正是我所期望的

参数是否正确绑定到查询对象

为给定的参数正确构造HQL查询

@测试
public void testFindFooByName(){
String name=“foobar”;
字符串otherParam=“otherParam”;
String expectedHql=“从Foo f中选择f,其中1=1,f.name=:name,f.other=:otherParam”;
List stubData=stubData();
Mockito.when(mockQuery.getResultList()).thenReturn(stubData);
Mockito.when(entityManager.createQuery(Mockito.eq(expectedHql)、Mockito.eq(Foo.class)),然后返回(mockQuery);
List-actual=dao.findFooByName(name,otherParam);
Assert.assertSame(stubData,实际);
验证(mockQuery).setParameter(“名称”,名称);
验证(mockQuery).setParameter(“otherParam”,otherParam);
}
总之,在决定是否包括验证或存根交互或两者时,您可能需要考虑:

  • 被测代码的功能:
    • 可能需要存根以防止暂停执行
    • 一个只做委托的方法可以通过验证得到充分证明
    • 从mock转换响应的方法可能需要stubing,然后针对转换后的响应进行断言
  • 您希望测试用例的具体程度:
    • 一些测试路径将需要验证以提供完全覆盖
    • 存根+验证可能有点过头了;查看您的代码覆盖率数字,确定额外的验证调用是在增加好处,还是只是在给您的测试用例增加混乱

这是一个意见问题,我想谢谢你详细的回答!那么,是否有任何标准或模式来编写此类测试,或者必须逐案分析?我认为这里没有标准模式,尽管“存根交互被隐式验证”肯定是一个良好的开端。这是你能感觉到的东西。
Mockito.verify(entityManager).createQuery(expectedSql, Foo.class);
Mockito.verify(mockQuery).setParameter("name", "foobar");
@Test
public void testFindFooByName() {
    List<Foo> stubData = stubData();

    Mockito.when(entityManager.createQuery(Mockito.anyString(), Mockito.eq(Foo.class))).thenReturn(mockQuery);

    dao.findFooByName("foobar", null);

    Mockito.verify(mockQuery).getResultList();
}
@Test
public void testFindFooByName() {
    List<Foo> stubData = stubData();

    Mockito.when(mockQuery.getResultList()).thenReturn(stubData);
    Mockito.when(entityManager.createQuery(Mockito.anyString(), Mockito.eq(Foo.class))).thenReturn(mockQuery);

    List<Foo> actual = dao.findFooByName("foobar", null);

    Assert.assertSame(stubData, actual);
}
@Test
public void testFindFooByName() {
    String name = "foobar";
    String otherParam = "otherParam";

    String expectedHql = "SELECT f FROM Foo f WHERE 1 = 1 AND f.name = :name AND f.other = :otherParam";

    List<Foo> stubData = stubData();

    Mockito.when(mockQuery.getResultList()).thenReturn(stubData);
    Mockito.when(entityManager.createQuery(Mockito.eq(expectedHql), Mockito.eq(Foo.class))).thenReturn(mockQuery);

    List<Foo> actual = dao.findFooByName(name, otherParam);

    Assert.assertSame(stubData, actual);

    Mockito.verify(mockQuery).setParameter("name", name);
    Mockito.verify(mockQuery).setParameter("otherParam", otherParam);
}