Reputation: 1347
I'm new on Unit Testing in java; I've an application that uses Hibernate to interact with a MySQL database.
I have many queries built with createQuery()
method, also with parameters, like the following one:
return this.entityManager.createQuery("from MyEntity m where param = :param", MyEntity.class)
.setParameter("param", param)
.getSingleResult();
I would like to avoid to mock all the subsequent calls on the entityManager
object, because sometimes I've query with more than 5 parameters and seems not that handy to mock each of those calls.
The same concept can be applied on Builder objects.
Edit 1
I add a concrete example of what I use (given that it's not a good way to manage exception, but unluckly is quiet usual):
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;
}
}
}
In this example, given the behavior of the call on entityManager
I have different branches to be tested. Then I have to mock the answer of that call to test all the lines of this method.
What I found was the following:
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
private EntityManager entityManager;
Which works as expected. I can mock all the calls' chain. BUT
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.
The second point means that if I try to mock in this way the method executeUpdate()
, which returns an int
, it raise an exception.
when(entityManager.createQuery(anyString())
.setParameter(eq("param"), anyString())
.executeUpdate())
.thenReturn(1);
and in that way I can't test the interactions with the entityManager
.
entityManager
? It seems impossible to me that I have to mock each method one by one.Answers.RETURNS_DEEP_STUBS
? If not, how can I handle the second example?Upvotes: 2
Views: 2363
Reputation: 16400
Don't mock the JPA API, just write integration tests with proper test data and execute the real queries against real data to see if everything works. Projects like testcontainers make it very easy to get started.
Upvotes: 4