使用 createQuery()、参数和 executeUpdate() 模拟 Hibernate entityManager
Mock Hibernate entityManager with createQuery(), parameters and executeUpdate()
我是 java 单元测试的新手;我有一个使用 Hibernate 与 MySQL 数据库交互的应用程序。
我有许多使用 createQuery()
方法构建的查询,也有参数,如下所示:
return this.entityManager.createQuery("from MyEntity m where param = :param", MyEntity.class)
.setParameter("param", param)
.getSingleResult();
我想避免模拟对 entityManager
对象的所有后续调用,因为有时我会使用超过 5 个参数进行查询,并且模拟每个调用似乎不太方便。
同样的概念也适用于 Builder objects。
编辑 1
我添加一个我使用的具体示例(鉴于这不是管理异常的好方法,但不幸的是它通常很安静):
public class MyService {
private EntityManager entityManager;
public MyEntity find(String field ) {
try{
return this.entityManager.createQuery("from MyEntity c where c.field = :field ", MyEntity .class)
.setParameter("field ", field )
.getSingleResult();
} catch (NoResultException e) {
return null;
} catch (NonUniqueResultException e) {
logger.error("find", e);
return null;
}
}
}
在这个例子中,鉴于 entityManager
上的调用行为,我有不同的分支要测试。然后我必须模拟那个调用的答案来测试这个方法的所有行。
我发现了什么
我发现的是以下内容:
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
private EntityManager entityManager;
按预期工作。我可以模拟所有的调用链。 但是
WARNING: This feature should rarely be required for regular clean code! Leave it for legacy code. Mocking a mock to return a mock, to return a mock, (...), to return something meaningful hints at violation of Law of Demeter or mocking a value object (a well known anti-pattern).
- 如果上一点不够,下一点,后面的几行,明确设置一个很大的限制:
This feature will not work when any return type of methods included in the chain cannot be mocked (for example: is a primitive or a final class). This is because of java type system.
第二点意味着,如果我尝试以这种方式模拟 executeUpdate()
方法 returns 和 int
,它会引发异常。
when(entityManager.createQuery(anyString())
.setParameter(eq("param"), anyString())
.executeUpdate())
.thenReturn(1);
这样我就无法测试与 entityManager
的交互。
问题
- 我应该如何模拟
entityManager
上的调用?对我来说似乎不可能,我必须一个一个地模拟每个方法。
- 用错了
Answers.RETURNS_DEEP_STUBS
?如果没有,我该如何处理第二个例子?
不要模拟 JPA API,只需使用适当的测试数据编写集成测试并对真实数据执行真实查询以查看是否一切正常。 testcontainers 等项目让入门变得非常容易。
我是 java 单元测试的新手;我有一个使用 Hibernate 与 MySQL 数据库交互的应用程序。
我有许多使用 createQuery()
方法构建的查询,也有参数,如下所示:
return this.entityManager.createQuery("from MyEntity m where param = :param", MyEntity.class)
.setParameter("param", param)
.getSingleResult();
我想避免模拟对 entityManager
对象的所有后续调用,因为有时我会使用超过 5 个参数进行查询,并且模拟每个调用似乎不太方便。
同样的概念也适用于 Builder objects。
编辑 1
我添加一个我使用的具体示例(鉴于这不是管理异常的好方法,但不幸的是它通常很安静):
public class MyService {
private EntityManager entityManager;
public MyEntity find(String field ) {
try{
return this.entityManager.createQuery("from MyEntity c where c.field = :field ", MyEntity .class)
.setParameter("field ", field )
.getSingleResult();
} catch (NoResultException e) {
return null;
} catch (NonUniqueResultException e) {
logger.error("find", e);
return null;
}
}
}
在这个例子中,鉴于 entityManager
上的调用行为,我有不同的分支要测试。然后我必须模拟那个调用的答案来测试这个方法的所有行。
我发现了什么
我发现的是以下内容:
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
private EntityManager entityManager;
按预期工作。我可以模拟所有的调用链。 但是
WARNING: This feature should rarely be required for regular clean code! Leave it for legacy code. Mocking a mock to return a mock, to return a mock, (...), to return something meaningful hints at violation of Law of Demeter or mocking a value object (a well known anti-pattern).
- 如果上一点不够,下一点,后面的几行,明确设置一个很大的限制:
This feature will not work when any return type of methods included in the chain cannot be mocked (for example: is a primitive or a final class). This is because of java type system.
第二点意味着,如果我尝试以这种方式模拟 executeUpdate()
方法 returns 和 int
,它会引发异常。
when(entityManager.createQuery(anyString())
.setParameter(eq("param"), anyString())
.executeUpdate())
.thenReturn(1);
这样我就无法测试与 entityManager
的交互。
问题
- 我应该如何模拟
entityManager
上的调用?对我来说似乎不可能,我必须一个一个地模拟每个方法。 - 用错了
Answers.RETURNS_DEEP_STUBS
?如果没有,我该如何处理第二个例子?
不要模拟 JPA API,只需使用适当的测试数据编写集成测试并对真实数据执行真实查询以查看是否一切正常。 testcontainers 等项目让入门变得非常容易。