You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.29 13:57:02