如何使用Mockito测试基于QueryDSL实现的JPA查询方法
使用Mockito测试QueryDSL方法的方案
前置准备
首先调整待测类的依赖注入方式,将JPAQueryFactory、PredicateBuilder、分页排序参数等通过构造方法注入,避免直接在方法内部获取依赖,方便Mock注入。如果不方便修改原有代码,也可以通过@InjectMocks+反射的方式注入Mock对象。
待测类调整示例:
public class YourQueryService { private final JPAQueryFactory queryFactory; private final PredicateBuilder predicateBuilder; private final OrderSpecifier<?> order; private final int rowCount; private final int pageId; // 构造注入依赖,测试时直接传入Mock对象 public YourQueryService(JPAQueryFactory queryFactory, PredicateBuilder predicateBuilder, OrderSpecifier<?> order, int rowCount, int pageId) { this.queryFactory = queryFactory; this.predicateBuilder = predicateBuilder; this.order = order; this.rowCount = rowCount; this.pageId = pageId; } public List<EntityDAO> getObject() { QEntity entity = QEntity.entity; JPAQuery<EntityDAO> query = queryFactory .select(Projections.bean(EntityDAO.class, entity.propertyA, entity.propertyB.count().as("count"))) .from(entity) .where(predicateBuilder.build()) .groupBy(entity.propertyA) .orderBy(order) .limit(rowCount) .offset(pageId*rowCount); return query.fetch(); } }
单元测试代码示例(JUnit 5 + Mockito)
核心技巧是使用Mockito的Answers.RETURNS_SELF策略Mock JPAQuery对象,自动处理QueryDSL链式调用的返回值,不需要逐个给from、where、groupBy等方法打桩。
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.Answers; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; import com.querydsl.core.types.OrderSpecifier; import com.querydsl.jpa.impl.JPAQuery; import com.querydsl.jpa.impl.JPAQueryFactory; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.*; import static org.junit.jupiter.api.Assertions.*; @ExtendWith(MockitoExtension.class) public class YourQueryServiceTest { @Mock private JPAQueryFactory queryFactory; // 自动处理链式调用返回自身 @Mock(answer = Answers.RETURNS_SELF) private JPAQuery<EntityDAO> query; @Mock private PredicateBuilder predicateBuilder; @Mock private OrderSpecifier<?> order; @Test public void getObject_ShouldReturnExpectedResult() { // 构造测试参数和预期结果 int testRowCount = 10; int testPageId = 1; List<EntityDAO> expectedList = List.of(new EntityDAO()); QEntity entity = QEntity.entity; // 打桩Mock行为 when(queryFactory.select(any())).thenReturn(query); when(predicateBuilder.build()).thenReturn(null); // 可替换为自定义测试Predicate when(query.fetch()).thenReturn(expectedList); // 初始化待测类 YourQueryService service = new YourQueryService(queryFactory, predicateBuilder, order, testRowCount, testPageId); // 调用待测方法 List<EntityDAO> actualList = service.getObject(); // 断言返回结果符合预期 assertEquals(expectedList, actualList); // 验证查询逻辑正确性,确认各个查询条件的调用符合预期 verify(queryFactory).select(Projections.bean(EntityDAO.class, entity.propertyA, entity.propertyB.count().as("count"))); verify(query).from(entity); verify(query).where(any()); verify(query).groupBy(entity.propertyA); verify(query).orderBy(order); verify(query).limit(testRowCount); verify(query).offset(testPageId * testRowCount); verify(query).fetch(); } }
注意事项
- 如果使用JUnit 4,只需把类上的
@ExtendWith(MockitoExtension.class)替换为@RunWith(MockitoJUnitRunner.class)即可,其余逻辑一致 - 需要验证Predicate条件时,可自定义构造Predicate实例,把
when(predicateBuilder.build()).thenReturn(null)替换为返回自定义实例,同时把verify(query).where(any())替换为verify(query).where(eq(自定义Predicate)) - 若原有方法的JPAQueryFactory是通过私有方法获取,可以用PowerMock来Mock私有方法的返回值,或者通过反射修改待测类的queryFactory字段值
内容的提问来源于stack exchange,提问作者avi
相关产品推荐
相关产品推荐

