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

如何为Spring Data JPA的@Query自定义查询方法编写JUnit测试用例

如何为Spring Data JPA的@Query自定义查询编写JUnit测试用例

其实给@Query注解的方法写测试,思路和你熟悉的派生查询测试是一致的,核心都是准备测试数据→调用目标方法→验证结果符合预期,只是要特别注意@Query里的查询逻辑对应的条件和返回值类型。先纠正一个小问题:你的@Query语句用了select new map(...),但方法返回类型是Set<Investigator>,这会导致类型不匹配——new map返回的是Map<String, Object>集合,不是实体类。我会分别针对两种正确的写法来写测试:


场景1:@Query返回实体集合(修正后的方法)

如果你的需求是返回nInstId=60的Investigator实体,方法应该改成这样:

@Repository 
public interface InvestigatorRepository extends JpaRepository<Investigator, Integer> { 
    @Query("select invest from Investigator invest where invest.nInstId=60") 
    Set<Investigator> findInvestigatorsByInstId60();
}

对应的JUnit测试用例步骤:

1. 编写测试类,依赖注入Repository

用Spring Boot提供的@DataJpaTest注解,它会自动配置内存数据库(比如H2)、Spring Data JPA环境,且默认开启事务,测试结束后自动回滚数据,不会污染测试环境:

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
import static org.assertj.core.api.Assertions.assertThat;
import java.util.Set;
import java.util.stream.Collectors;

@DataJpaTest
class InvestigatorRepositoryTest {

    @Autowired
    private InvestigatorRepository investigatorRepository;

    // 测试数据准备
    @BeforeEach
    void setUpTestData() {
        // 插入2条符合条件(nInstId=60)的记录
        Investigator zhangSan = new Investigator();
        zhangSan.setnInstId(60);
        zhangSan.setsInvestigatorName("张三");
        investigatorRepository.save(zhangSan);

        Investigator liSi = new Investigator();
        liSi.setnInstId(60);
        liSi.setsInvestigatorName("李四");
        investigatorRepository.save(liSi);

        // 插入1条不符合条件的记录,验证不会被查询出来
        Investigator wangWu = new Investigator();
        wangWu.setnInstId(50);
        wangWu.setsInvestigatorName("王五");
        investigatorRepository.save(wangWu);
    }

    // 测试目标方法
    @Test
    void findInvestigatorsByInstId60_shouldReturnOnlyInstId60Records() {
        // 调用自定义查询方法
        Set<Investigator> result = investigatorRepository.findInvestigatorsByInstId60();

        // 断言结果数量正确
        assertThat(result).hasSize(2);

        // 断言返回的记录都是nInstId=60的
        result.forEach(investigator -> assertThat(investigator.getnInstId()).isEqualTo(60));

        // 断言姓名符合预期
        var names = result.stream()
                .map(Investigator::getsInvestigatorName)
                .collect(Collectors.toSet());
        assertThat(names).containsExactlyInAnyOrder("张三", "李四");
        assertThat(names).doesNotContain("王五");
    }
}

场景2:@Query返回Map集合(原方法的正确写法)

如果你的需求是只返回sInvestigatorName字段的Map集合,方法返回类型要改成Set<Map<String, Object>>:

@Repository 
public interface InvestigatorRepository extends JpaRepository<Investigator, Integer> { 
    @Query("select new map(invest.sInvestigatorName as sInvestigatorName) from Investigator invest where invest.nInstId=60") 
    Set<Map<String, Object>> findInvestigatorNamesByInstId60();
}

对应的测试方法只需要调整断言逻辑:

@Test
void findInvestigatorNamesByInstId60_shouldReturnCorrectNames() {
    Set<Map<String, Object>> result = investigatorRepository.findInvestigatorNamesByInstId60();

    assertThat(result).hasSize(2);

    // 提取Map中的sInvestigatorName字段进行验证
    var names = result.stream()
            .map(map -> (String) map.get("sInvestigatorName"))
            .collect(Collectors.toSet());
    assertThat(names).containsExactlyInAnyOrder("张三", "李四");
    assertThat(names).doesNotContain("王五");
}

几个关键注意点:

  • @DataJpaTest会自动配置测试所需的JPA环境,不需要额外配置数据库连接;
  • 测试用的内存数据库数据是临时的,每次测试前都会初始化,不用担心数据污染;
  • 用AssertJ的断言(assertThat开头)比JUnit原生断言更灵活、可读性更高,Spring Boot测试已经默认集成;
  • 如果你的查询涉及关联实体、复杂条件,只需要在setUpTestData里准备对应的关联数据即可,测试逻辑和单表查询一致。

内容的提问来源于stack exchange,提问作者SpringUser

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.26 10:46:25