【问题标题】:How to mock persisting and Entity with Mockito and jUnit如何使用 Mockito 和 jUnit 模拟持久化和实体
【发布时间】:2014-11-15 12:35:09
【问题描述】:

我正在尝试寻找一种方法来使用 Mockito 测试我的实体;

这是简单的测试方法:

@Mock
private EntityManager em;

@Test
public void persistArticleWithValidArticleSetsArticleId() {
    Article article = new Article();
    em.persist(article);
    assertThat(article.getId(), is(not(0L)));
}

如何最好地模拟 EntityManager 将 Id 从 0L 更改为即 1L 的行为?可能在可读性方面的障碍最少。

编辑: 一些额外的信息;在测试范围之外,EntityManager 由应用程序容器生成

【问题讨论】:

  • 一些额外的信息;在测试范围之外,EntityManager 由应用程序容器 (glassfish) 生成。
  • 您可以简单地测试 Article 类的 setId() 和 getId() 方法。我认为没有必要模拟 EntityManager 并告诉它将 id 设置为文章。当您要检查 id 是否生成并设置为您传递的实体以进行持久化时,此测试将更适合对 EntityManager 本身进行单元测试。
  • 使用模拟库并不是最好的情况。仔细看看你的测试——你在这里测试的是检查模拟库是否有效,而不是测试你的代码。如果您需要模拟/存根被测类的内部组件的行为并且您知道该组件在特定情况下的行为方式,则 Mocks 很有用。

标签: java junit mockito hamcrest


【解决方案1】:

您可以为此使用 Mockito Answer。

doAnswer(new Answer<Object>(){
     @Override
     public Object answer(InvocationOnMock invocation){
        Article article = (Article) invocation.getArguments()[0];
        article.setId(1L);
        return null;
     }
  }).when(em).persist(any(Article.class));

这告诉 Mockito,当调用 persist 方法时,第一个参数应该调用其 setId 方法。

但如果你这样做,我不明白测试的目的是什么。您实际上只是在测试 Mockito Answer 机制是否正常工作,而不是 Article 或 EntityManager 的代码是否正常工作。

【讨论】:

  • 或多或少。我有兴趣看到您将新 ID 存储在 Answer 对象的字段中。我不会想到这样做,除非我想在几个地方重用Answer。但是你为什么删除你的?我认为它与我的足够不同,值得在这里拥有两者。 @JBNizet
  • 我删了是因为我和你的基本一样,但是后来才来的。我取消删除它,因为你认为它增加了一些东西。在那种特定情况下,存储 id 是没有用的。事实上,我借用了我实际使用的代码,其中 Answer 类确实在顶级类中,并且可以用于任何类型的实体,使其可重用(和重用)。
  • 感谢您的回答。这个测试的原因更多是与实践相关的。我正在寻找一种模拟 void 方法的方法,该方法在调用时运行代码行;供将来参考。
  • @Patrick - 那么我建议您研究与Answer 类相关的 Mockito 文档。这是 Mockito API 相当丰富的领域,Mockito 有很多内置方法可以为各种常用操作创建Answer 对象。
  • @Brice 也许您可以将其作为答案发布,使用代码 sn-p,只是出于可见性原因,而不是将其埋在我和 JB 答案下的 cmets 中。
【解决方案2】:
public class AssignIdToArticleAnswer implements Answer<Void> {

    private final Long id;

    public AssignIdToArticleAnswer(Long id) {
        this.id = id;
    }

    @Override
    public Void answer(InvocationOnMock invocation) throws Throwable {
        Article article = (Article) invocation.getArguments()[0];
        article.setId(id);
        return null;
    }
}

然后

doAnswer(new AssignIdToArticleAnswer(1L)).when(em).persist(any(Article.class));

【讨论】:

  • 值得注意的是,使用此解决方案,您可以将AssignIdToArticleAnswer 设为您的测试类的内部类,然后在您的几个测试方法中使用相同的doAnswer 调用。如果适合您,您甚至可以传入不同的 ID。如果您做的不仅仅是一次性测试,这使得这个解决方案比我的解决方案更通用。 +1。
  • 这个想法确实是一样的,但我更喜欢这种方法,因为它的可重用性和测试方法本身的更好的可读性。
  • 没什么大不了的,但对于自定义答案,我会创建返回答案的静态工厂方法。
【解决方案3】:

与上述类似的答案,但使用 lambdas

   doAnswer((InvocationOnMock invocation) -> {
        Article article = (Article) invocation.getArguments()[0];
        article.setId(1L);
        return null;
    }).when(em).persist(any(Article.class));

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-11-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多