java-Mockito验证交互与验证结果

Mockito有关验证方法的JavaDocs链接到有关询问和告知的this interesting article.我特别迷失在那儿,“存根的交互被隐式验证”.

让我们举个例子:

想象一个有这个班

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();
    }
}

现在,让我们看看如何检查此方法:

>对于给定的参数,正确构造了HQL查询.
>参数已正确绑定到查询对象.
>结果就是我所期望的.

首先,我将模拟EntityManager对象,因为我不想访问真实的数据库.

@InjectMocks
private FooDao dao;

@Mock
private EntityManager entityManager;

现在,我可以添加如下内容:

@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?
}

所以,现在的问题是要检查什么.我可以只添加一个assertEquals(stubData,actual),测试就会成功.

我还可以添加:

Mockito.verify(entityManager).createQuery(expectedSql, Foo.class);
Mockito.verify(mockQuery).setParameter("name", "foobar");

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

解决方法:

您必须存根与EntityManager的交互,因为否则,当findFooByName()调用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();
}

更加具体

以下测试不是特定于TypedQuery创建方式的特定测试,而是验证被创建的方式以及被测试方法返回其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);
}

最具体

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

That the result is what I expect.

That the parameters are correctly bound to the query object.

That the HQL query is constructed properly for the parameters given.

@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);
}

因此,总而言之,在确定是否包括验证或互动后,您可能需要考虑:

>被测代码的作用:

>可能需要存根以防止执行暂停
>可以使用验证充分证明除委托外无所事事的方法
>转换模拟响应的方法可能需要先进行存根,然后对转换后的响应进行断言
>等

>您希望测试用例的具体程度:

>有些测试路径需要验证以提供完整的覆盖范围
>存根验证可能是过分的;查看您的代码覆盖率编号,确定其他验证调用是在增加好处还是在测试用例中增加混乱

上一篇:android-如何使用Mockito模拟DisplayMetrics


下一篇:Mockito测试Java 8 Lambda Consumer API