如何在JUnit中Mock Hibernate的setFirstResult()与setMaxResults()方法
如何Mock Hibernate的setFirstResult()和setMaxResults()来测试分页功能?
我写了一个用Hibernate实现分页的业务方法,但是在写JUnit测试时没法正确Mock setFirstResult() 和 setMaxResults() 方法,导致测试总是返回全量数据,测试用例失败。以下是我的代码:
业务方法
public List<Issuance> getIssuances(int after, int limit) { CriteriaBuilder builder = db.session().getCriteriaBuilder(); CriteriaQuery<Issuance> criteriaQuery = builder.createQuery(Issuance.class); Root<Issuance> txnRoot = criteriaQuery.from(Issuance.class); Path<Long> idPath = txnRoot.get("id"); criteriaQuery.select(txnRoot); criteriaQuery.orderBy(builder.desc(idPath)); return db.session().createQuery(criteriaQuery).setFirstResult(after).setMaxResults(limit).getResultList(); }
现有JUnit测试方法
@Test public void getIssuances(){ Issuance issuance1 = new Issuance(); issuance1.setId(1l); Issuance issuance2 = new Issuance(); issuance2.setId(2l); Issuance issuance3 = new Issuance(); issuance3.setId(3l); List<Issuance> expectedIssuances = Arrays.asList(issuance1,issuance2,issuance3); CriteriaQuery<Issuance> criteriaQuery = mock(CriteriaQuery.class); CriteriaBuilder criteriaBuilder = mock(CriteriaBuilder.class); when(dbSession.getCriteriaBuilder()).thenReturn(criteriaBuilder); Root<Issuance> txnRoot = mock(Root.class); when(criteriaBuilder.createQuery(eq(Issuance.class))).thenReturn(criteriaQuery); when(criteriaQuery.from(eq(Issuance.class))).thenReturn(txnRoot); Query<Issuance> query = mock(Query.class); when(query.setFirstResult(eq(0))).thenReturn(query); when(query.setMaxResults(eq(1))).thenReturn(query); when(query.getResultList()).thenReturn(expectedIssuances); when(dbSession.createQuery(eq(criteriaQuery))).thenReturn(query); List<Issuance> result = unit.getIssuances(0,1); assertNotEquals(expectedIssuances.size(), result.size());// 测试用例失败,因为两者数量相等 }
问题根源
你的测试失败主要有两个核心原因:
- 你Mock的
query.getResultList()直接返回了全部3个对象,但业务方法明确调用了setMaxResults(1),测试时应该返回符合分页规则的子集,而非全量数据。 - 没有验证
setFirstResult()和setMaxResults()是否被正确调用,无法确保分页逻辑实际执行了。
修正后的测试代码
@Test public void getIssuances_shouldReturnPaginatedResults(){ // 1. 准备测试数据 Issuance issuance1 = new Issuance(); issuance1.setId(1l); Issuance issuance2 = new Issuance(); issuance2.setId(2l); Issuance issuance3 = new Issuance(); issuance3.setId(3l); // 对应after=0, limit=1的预期分页结果(业务方法按id倒序,所以取id最大的那条) List<Issuance> paginatedExpected = Arrays.asList(issuance3); // 2. Mock所有依赖节点 CriteriaQuery<Issuance> criteriaQuery = mock(CriteriaQuery.class); CriteriaBuilder criteriaBuilder = mock(CriteriaBuilder.class); when(dbSession.getCriteriaBuilder()).thenReturn(criteriaBuilder); when(criteriaBuilder.createQuery(eq(Issuance.class))).thenReturn(criteriaQuery); Root<Issuance> txnRoot = mock(Root.class); when(criteriaQuery.from(eq(Issuance.class))).thenReturn(txnRoot); // 补充Mock txnRoot.get("id")的调用 Path<Long> idPath = mock(Path.class); when(txnRoot.get("id")).thenReturn(idPath); // Mock criteriaQuery.orderBy()这个void方法 doNothing().when(criteriaQuery).orderBy(any(Order.class)); Query<Issuance> query = mock(Query.class); // 链式调用的Mock:确保setFirstResult和setMaxResults返回自身,维持调用链 when(query.setFirstResult(eq(0))).thenReturn(query); when(query.setMaxResults(eq(1))).thenReturn(query); // 返回分页后的结果,而非全量数据 when(query.getResultList()).thenReturn(paginatedExpected); when(dbSession.createQuery(eq(criteriaQuery))).thenReturn(query); // 3. 执行测试方法 List<Issuance> result = unit.getIssuances(0,1); // 4. 断言与验证 assertEquals(paginatedExpected.size(), result.size()); assertEquals(paginatedExpected.get(0).getId(), result.get(0).getId()); // 验证分页方法是否被传入正确参数调用 verify(query).setFirstResult(0); verify(query).setMaxResults(1); }
关键修改说明
- 返回分页子集:将
getResultList()的Mock返回改为对应分页参数的结果,这样断言时结果大小才会符合预期。 - 补充缺失Mock:业务方法中调用的
txnRoot.get("id")和criteriaQuery.orderBy(...)在原测试中未Mock,可能引发空指针异常,需要补充Mock逻辑。 - 添加调用验证:用
verify()确认setFirstResult()和setMaxResults()被传入正确参数调用,确保分页逻辑确实执行,而非直接返回全量数据。 - 优化测试命名:把测试方法名改为更具描述性的
getIssuances_shouldReturnPaginatedResults,让测试意图更清晰。
内容的提问来源于stack exchange,提问作者Shoaib
相关产品推荐
相关产品推荐

