如何为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
相关产品推荐
相关产品推荐

