如何在内存数据库中使用 H2 测试 EntityManager 查询

Posted

技术标签:

【中文标题】如何在内存数据库中使用 H2 测试 EntityManager 查询【英文标题】:How to test EntityManager query with H2 in memory DB 【发布时间】:2022-01-23 18:58:24 【问题描述】:

我有一个 Spring Boot 项目,我想测试一些查询。我想插入一组预定义的数据并执行存储库查询以检查结果是否是所需的。

为此,我使用的是内存 H2 DB,但问题(我认为)不存在,与 DB 相关的一切都正常。主要问题是我无法正确模拟存储库中的EntityManager 字段,并且查询始终为空。

我的仓库是这样的:

@Repository
public class MyRepositoryImpl implements MyRepository 

    @PersistenceContext
    private EntityManager entityManager;
    
    @Override
    public Result runQuery() 
        
        TypedQuery<Result> query = entityManager.createQuery(
                "SELECT ...", Result.class);
        return query.setParameter("...", "...") // here 'query' is always null
                .setMaxResults(1)
                .getResultStream()
                .findFirst()
                .orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND, "Entity not found"));
    


在测试之外执行时效果很好,但是尝试运行此测试文件会引发错误:

@RunWith(SpringRunner.class)
public class MyRepositoryTest 

    @Mock
    EntityManager entityManager;

    @InjectMocks
    MyRepositoryImpl repository;

    @Test
    public void it_should_works() 
        Result r = repository.runQuery();
        assertNull(r);
    

存储库被模拟并且不为空,我可以调用该方法。但是在仓库内部,由于query字段为空,尝试执行时会抛出NullPointerException

我在互联网上搜索了很多方法来测试界面内的JPARepository@Query,但不是EntityManager 查询。

我还找到了一些模拟查询结果的方法,比如when(runQuery()).thenReturn(result),但我不希望这样,我在内存数据库中有数据,所以我想执行查询并获取结果。

所以,现在,我认为的主要问题是如何在存储库类中正确地模拟 EntityManager 对象。

提前致谢。

编辑:

我已经关注 this link 并且就像另一个 SO 问题:这只是为了模拟 JpaRepository

我用过这段代码:

@Test
public void it_should_works() 
    Result r = repository.findAll();
    assertNotNull(r);

而且效果很好,但是使用我自己的查询失败并出现错误:

org.springframework.orm.jpa.JpaSystemException: could not advance using next(); nested exception is org.hibernate.exception.GenericJDBCException: could not advance using next()

    ...

Caused by: org.h2.jdbc.JdbcSQLNonTransientException: El objeto ya está cerrado
The object is already closed [90007-200]

所以问题是:它与我的数据库有关吗?为什么使用 JpaRepository 方法它可以工作,但我自己的查询却不行?

编辑:

解决了将@Transactional 添加到存储库的问题。

【问题讨论】:

【参考方案1】:

由于您使用 h2 内存数据库来运行测试,并且您希望在测试中实际使用该数据库,因此您不应该真正模拟任何东西。

您的模拟不起作用,因为 MyRepositoryImpl 通常由 Spring 初始化,并且该过程比插入 EntityManager 复杂得多。

我认为你想做的更像是这里描述的https://www.baeldung.com/spring-testing-separate-data-source

因此,您将拥有一个覆盖数据源属性的 src/test/resources/application.properties 文件。然后,您只需像往常一样将您的存储库 @Autowired 到您的测试类中。

【讨论】:

以上是关于如何在内存数据库中使用 H2 测试 EntityManager 查询的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 H2 内存数据库编写单元测试

使用 JPA 和 JUnit 测试时如何一致地擦除内存数据库中的 H2 [重复]

如何使用 H2 嵌入式数据库创建 H2 内存数据库

如何关闭 h2 内存数据库?

内存数据库中的 H2:使用 JDBC 设置时区? Java 单元测试

在内存数据库中使用 H2 进行 Spring Boot 集成测试