How to mock persisting and Entity with Mockito and jUnit
Asked Answered
M

3

6

I'm trying to find a way to test my entity using Mockito;

This is the simple test method:

@Mock
private EntityManager em;

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

How do I best mock the behaviour that the EntityManager changes the Id from 0L to i.e. 1L? Possibly with the least obstructions in readability.

Edit: Some extra information; Outside test-scope the EntityManager is produced by an application-container

Milewski answered 15/11, 2014 at 12:35 Comment(3)
Some extra information; Outside test-scope the EntityManager is produced by an application-container (glassfish).Milewski
You can simply test your setId() and getId() methods for Article class. I think there is no need to mock EntityManager and tell it to set id to article. This test will be more suitable when unit testing EntityManager itself, when you want to check if id is generated and set to entity you pass to persist.Mindimindless
It's not the best case to use mocking library. Take a closer look to your test - what you're testing here is checking if mocking library works, instead of testing your code. Mocks are useful if you need to mock/stub a behavior of internal component of your class under test and you know how this component behaves in particular situations.Squiggle
P
7
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;
    }
}

And then

doAnswer(new AssignIdToArticleAnswer(1L)).when(em).persist(any(Article.class));
Potpourri answered 15/11, 2014 at 12:50 Comment(3)
It's worth noting that with this solution, you can make AssignIdToArticleAnswer an inner class of your test class, then use the same doAnswer call in several of your test methods. You can even pass in different ids, if it suits you to do so. This makes this solution rather more versatile than my solution, if you're doing anything more than a one-off test. +1.Gamesmanship
The idea is indeed the same but I prefer this method for it's reusability and for better readability in the test-method itself.Milewski
not a big deal, but for custom answer I would create static factory method that return the answer.Inhabitancy
G
10

You could use a Mockito Answer for this.

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));

This tells Mockito that when the persist method is called, the first argument should have its setId method invoked.

But if you do this, I don't understand what the purpose of the test would be. You'd really just be testing that the Mockito Answer mechanism works, not that the code of Article or of EntityManager works correctly.

Gamesmanship answered 15/11, 2014 at 12:50 Comment(8)
I deleted it because I was basically the same as yours, but came later. I undeleted it since you think it adds something. In that specific case, storing the id is useless. In fact I borrowed code that I'm actually using where the Answer class is indeed in a top-level class, and can be used for any kind of entity, making it reusable (and reused).Potpourri
Thanks for this answer. The reason for this test is more practise-related. I am looking for a way to mock a void-method which runs lines of code when called; For future reference.Milewski
@Milewski - then I recommend you study the Mockito docs that relate to the Answer class. This is quite a rich area of the Mockito API, and Mockito has many built-in methods for creating Answer objects for various commonly used operations.Gamesmanship
Same as JB : not a big deal, but for custom answer I would create static factory method that return the answer.Inhabitancy
@Brice Maybe you could post that as an answer, with a code snippet, just for visibility reasons, instead of burying it in the comments under my and JB's answers.Gamesmanship
;) I know but I'm on a Phone so I keep stuff simple.Inhabitancy
Shouldn't you use when(em).persist(isA(Article.class)) instead?Navigation
Not really, it makes absolutely no difference, unless OP is testing a method that persists a whole lot of entities of different classes. If that were the case, he'd have mentioned it in the question. @NavigationGamesmanship
P
7
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;
    }
}

And then

doAnswer(new AssignIdToArticleAnswer(1L)).when(em).persist(any(Article.class));
Potpourri answered 15/11, 2014 at 12:50 Comment(3)
It's worth noting that with this solution, you can make AssignIdToArticleAnswer an inner class of your test class, then use the same doAnswer call in several of your test methods. You can even pass in different ids, if it suits you to do so. This makes this solution rather more versatile than my solution, if you're doing anything more than a one-off test. +1.Gamesmanship
The idea is indeed the same but I prefer this method for it's reusability and for better readability in the test-method itself.Milewski
not a big deal, but for custom answer I would create static factory method that return the answer.Inhabitancy
H
4

similar answer as above, but with lambdas

   doAnswer((InvocationOnMock invocation) -> {
        Article article = (Article) invocation.getArguments()[0];
        article.setId(1L);
        return null;
    }).when(em).persist(any(Article.class));
Hollingshead answered 15/9, 2015 at 8:20 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.