如何在内存数据库中使用 H2 测试 EntityManager 查询
How to test EntityManager query with H2 in memory DB
我有一个 Spring 引导项目,我想测试一些查询。我想插入一组预定义的数据并执行存储库查询以检查结果是否是所需的。
为此,我使用内存中的 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)
但我不想那样,我的数据在内存数据库中,所以我想执行查询并得到结果。
所以,现在,我认为主要的问题是如何在存储库 class.
中正确模拟 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
添加到存储库的问题。
由于您正在使用 h2 内存数据库进行 运行 测试,并且您想在测试中实际使用该数据库,因此您不应该真正模拟任何东西。
您的模拟不起作用,因为 MyRepositoryImpl 通常由 Spring 初始化,并且该过程比插入 EntityManager 复杂得多。
我觉得你想做的更像是这里描述的https://www.baeldung.com/spring-testing-separate-data-source
因此您将有一个 src/test/resources/application.properties 文件来覆盖数据源属性。然后你就像平常一样将你的存储库@Autowired到你的测试class。
我有一个 Spring 引导项目,我想测试一些查询。我想插入一组预定义的数据并执行存储库查询以检查结果是否是所需的。
为此,我使用内存中的 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)
但我不想那样,我的数据在内存数据库中,所以我想执行查询并得到结果。
所以,现在,我认为主要的问题是如何在存储库 class.
中正确模拟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
添加到存储库的问题。
由于您正在使用 h2 内存数据库进行 运行 测试,并且您想在测试中实际使用该数据库,因此您不应该真正模拟任何东西。
您的模拟不起作用,因为 MyRepositoryImpl 通常由 Spring 初始化,并且该过程比插入 EntityManager 复杂得多。
我觉得你想做的更像是这里描述的https://www.baeldung.com/spring-testing-separate-data-source
因此您将有一个 src/test/resources/application.properties 文件来覆盖数据源属性。然后你就像平常一样将你的存储库@Autowired到你的测试class。