Java 如何用Mockito和jUnit模拟持久化和实体

Java 如何用Mockito和jUnit模拟持久化和实体,java,junit,mockito,hamcrest,Java,Junit,Mockito,Hamcrest,我试图找到一种使用Mockito测试实体的方法 这是一种简单的测试方法: @Mock private EntityManager em; @Test public void persistArticleWithValidArticleSetsArticleId() { Article article = new Article(); em.persist(article); assertThat(article.getId(), is(not(0L))); } 如何最

我试图找到一种使用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是由应用程序容器生成的

为此,您可以使用Mockito
答案

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));
doAnswer(新答案(){
@凌驾
公共对象应答(调用锁调用){
Article=(Article)invocation.getArguments()[0];
第条setId(1L);
返回null;
}
}).when(em).persist(any(Article.class));
这告诉Mockito,当调用
persist
方法时,第一个参数应该调用其
setId
方法


但是如果你这样做,我不明白测试的目的是什么。您实际上只是在测试Mockito
答案
机制是否有效,而不是
文章
实体管理器
的代码是否正确工作。

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

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((InvocationOnMock invocation) -> {
        Article article = (Article) invocation.getArguments()[0];
        article.setId(1L);
        return null;
    }).when(em).persist(any(Article.class));

一些额外的信息;在测试范围之外,EntityManager由应用程序容器(glassfish)生成。您只需测试Article类的setId()和getId()方法。我认为没有必要嘲笑EntityManager并告诉它将id设置为article。当单元测试EntityManager本身时,当您想检查id是否已生成并设置为要持久化的实体时,此测试将更合适。这不是使用模拟库的最佳情况。仔细看看您的测试——您在这里测试的是检查模拟库是否工作,而不是测试您的代码。如果您需要模拟/存根受测类的内部组件的行为,并且您知道该组件在特定情况下的行为,那么mock非常有用。我很感兴趣地看到您将新id存储在
Answer
对象的字段中。除非我想在一些地方重复使用
答案,否则我不会想到这样做。但是你为什么要删除你的?我认为它和我的完全不同,在这里两者都值得拥有@JBNizetI删除了它,因为我基本上和你的一样,但后来才来。我取消删除了它,因为你认为它增加了一些东西。在这种特定情况下,存储id是无用的。事实上,我借用了Answer类确实在顶级类中使用的代码,可以用于任何类型的实体,使其可重用(和重用)。感谢您的回答。这项测试的原因与实践更为相关。我正在寻找一种模拟void方法的方法,该方法在调用时运行代码行;作为将来的参考。@Patrick-那么我建议你研究一下与
答案
类相关的Mockito文档。这是Mockito API的一个非常丰富的领域,Mockito有许多内置方法,用于为各种常用操作创建
Answer
对象。@Brice也许您可以将其作为一个答案发布,附带一个代码片段,只是为了可见性的原因,值得注意的是,使用此解决方案,您可以将
AssignIdToArticleAnswer
作为测试类的一个内部类,然后使用相同的
doAnswer
调用多个测试方法。如果适合的话,您甚至可以传入不同的ID。这使得这个解决方案比我的解决方案更通用,如果你做的不是一次性测试的话+1.想法确实是一样的,但我更喜欢这种方法,因为它的可重用性和测试方法本身更好的可读性。没什么大不了的,但对于自定义答案,我会创建返回答案的静态工厂方法。
   doAnswer((InvocationOnMock invocation) -> {
        Article article = (Article) invocation.getArguments()[0];
        article.setId(1L);
        return null;
    }).when(em).persist(any(Article.class));