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);
}
因此,总而言之,在确定是否包括验证或互动后,您可能需要考虑:
>被测代码的作用:
>可能需要存根以防止执行暂停
>可以使用验证充分证明除委托外无所事事的方法
>转换模拟响应的方法可能需要先进行存根,然后对转换后的响应进行断言
>等
>您希望测试用例的具体程度:
>有些测试路径需要验证以提供完整的覆盖范围
>存根验证可能是过分的;查看您的代码覆盖率编号,确定其他验证调用是在增加好处还是在测试用例中增加混乱