Spring Boot单元测试时如何校验EntityManager创建的查询且不影响原数据库
核心结论
纯mock EntityManager的方式确实无法校验查询本身的正确性,你可以通过两种方案解决问题,适配不同的测试场景。
方案1:用内存数据库做查询集成测试(最推荐)
这个是Spring Boot生态下校验JPA查询的标准方案,完全满足不访问原数据库、测试数据完全可控的要求:
- 引入H2这类内存数据库依赖,Spring Boot测试环境会自动配置内存数据源,不会连接你的生产/开发数据库
- 测试前通过
@Sql注解或者spring.jpa.hibernate.ddl-auto配置自动建表,插入固定的测试数据,测试数据完全由你定义,不会出现波动 - 测试时注入真实的EntityManager,直接执行业务代码中的查询,对比返回结果是否符合预期,既能校验JPQL语法合法性、参数绑定正确性,也能校验关联查询、过滤逻辑是否符合需求,测试结束后内存数据库自动销毁,无任何残留
测试示例代码:
@DataJpaTest // 自动配置JPA相关组件、内存数据库 @AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.ANY) // 强制使用内存数据库 public class ReservationQueryTest { @Autowired private EntityManager em; @Test public void testEditFeedbackQuery() { // 预先插入测试数据 Reservation testReservation = new Reservation(); testReservation.setId(1L); User testUser = new User(); testUser.setId(2L); testReservation.setReservation_done_by(testUser); em.persist(testReservation); em.flush(); // 执行你的业务查询 String queryStr = "select r from Reservation r left join fetch r.driverAssignment where r.id=:id and r.reservation_done_by.id=:user_id "; List<Reservation> result = em.createQuery(queryStr, Reservation.class) .setParameter("id", 1L) .setParameter("user_id", 2L) .getResultList(); // 校验结果 Assertions.assertEquals(1, result.size()); Assertions.assertEquals(1L, result.get(0).getId()); } }
方案2:Mockito参数校验(仅校验查询字符串、参数绑定,不执行查询)
如果不需要校验查询的实际执行结果,只想确认代码传入的JPQL、参数符合预期,可以修改你的mock逻辑,不用宽松的anyString()匹配,改用精确匹配+参数校验:
// 精确匹配JPQL内容,只要代码里的JPQL写错就会匹配失败,测试报错 Mockito.when(em.createQuery(eq("select r from Reservation r left join fetch r.driverAssignment where r.id=:id and r.reservation_done_by.id=:user_id "))) .thenReturn(query); Mockito.when(query.getResultList()).thenReturn(sampleReservations.getReservationsForUserReservations()); // 执行业务方法后校验参数是否正确绑定 Mockito.verify(query).setParameter("id", reservation_id); Mockito.verify(query).setParameter("user_id", user_id);
这个方案只能做静态校验,无法发现JPQL语法错误、关联逻辑错误等问题,适合和业务逻辑单元测试搭配使用。
测试分层建议
你可以把两类测试分开:
- 业务逻辑单元测试:mock EntityManager/Repository,只校验查询拿到结果后的业务逻辑是否正确,比如空结果是否返回正确的错误响应、拿到数据后是否正确更新feedback、是否调用了save方法
- 持久层查询测试:用内存数据库单独校验所有JPQL的正确性,两类测试各司其职
内容的提问来源于stack exchange,提问作者Prasad Darshana
相关产品推荐
相关产品推荐

