首页 > 解决方案 > 使用 createQuery()、参数和 executeUpdate() 模拟 Hibernate entityManager

问题描述

我是 Java 单元测试的新手;我有一个使用 Hibernate 与 MySQL 数据库交互的应用程序。

我有许多使用createQuery()方法构建的查询,也带有参数,如下所示:

return this.entityManager.createQuery("from MyEntity m where param  = :param", MyEntity.class)
    .setParameter("param", param)
    .getSingleResult();

我想避免模拟entityManager对象上的所有后续调用,因为有时我使用超过 5 个参数进行查询,并且模拟每个调用似乎并不那么方便。

相同的概念可以应用于Builder 对象

编辑 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;

哪个按预期工作。我可以模拟所有调用链。

  1. 引用Mockito.RETURNS_DEEP_STUBS 的 Javadoc

警告:常规干净代码很少需要此功能!把它留给遗留代码。模拟一个模拟返回一个模拟,返回一个模拟,(...),返回一些有意义的暗示违反德墨忒耳定律或模拟一个值对象(一个众所周知的反模式)。

  1. 如果上一点还不够,那么下一点,之后的几行,显然设置了一个很大的限制:

当无法模拟链中包含的任何返回类型的方法时(例如:是原始类或最终类),此功能将不起作用。这是因为 java 类型系统。

第二点意味着如果我尝试以这种方式模拟executeUpdate()返回 an 的方法,int它会引发异常。

when(entityManager.createQuery(anyString())
                .setParameter(eq("param"), anyString())
                .executeUpdate())
.thenReturn(1);

这样我就无法测试与entityManager.

问题

  1. 我应该如何模拟电话entityManager?在我看来,我必须一个一个地模拟每种方法。
  2. 用错了Answers.RETURNS_DEEP_STUBS吗?如果没有,我该如何处理第二个例子?

标签: javahibernatejunitmockitojunit5

解决方案


不要模拟 JPA API,只需使用适当的测试数据编写集成测试并针对真实数据执行真实查询以查看一切是否正常。像testcontainers这样的项目使上手变得非常容易。


推荐阅读