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
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
Citing from the Javadoc of Mockito.RETURNS_DEEP_STUBS:
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).
If the previous point wasn't enough, the next one, some lines after, clearly set a big limitation:
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.
Questions
How should I mock the calls on entityManager? It seems impossible to me that I have to mock each method one by one.
Is wrong to use Answers.RETURNS_DEEP_STUBS? If not, how can I handle the second example?
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.
Related
I'm writing some Unit Tests for my Service class, specifically, an update method that does exactly that, update an Entity with the given data from a request.
The problem is, I'm using ModelMapper to map the request data to the entity and when the test goes through the mapping statement it doesn't actually call the modelMapper but the mock .... which does nothing because it's a mock.
How should I mock its behavior?
public EntityNode updateEntity(String code, EntityDTO request) {
String message = "Entity with code: %s not found";
EntityNode entity = repository.findByCode(code)
.orElseThrow(() -> new EntityNotFoundException(String.format(message, code)));
modelMapper.map(request, entity);
return repository.save(entity);
}
I've thought about using an ArgumentCaptor but I'm not really sure if it suits my needs or if it's really what I need to do what I want.
This is my unfinished test method. After writing all of this I think I should stub ModelMappers.map() somehow and also return the result of calling the ModelMapper stub map() method when calling repository.save(entity).
#Test
void givenValidEntity_whenUpdateEntity_shouldUpdateProperties() {
//given
String code = "TEST";
Entity expected = new Entity();
expected.setName("Old");
EntityDTO request = new EntityDTO();
request.setName("New")
given(repository.findByCode(code)).willReturn(expected);
//when
Entity updatedEntity = service.updateEntity(code, request);
//then
assertEquals(request.getName(), updatedEntity.getName());
}
Does this make any sense?
Thanks
What does the changing?
By looking at the current code it seems like the modelMapper does the changing. This would mean that changing unit test should be in modelMapper's own unit test.
What does the EntityNode updateEntity(String code, EntityDTO request) do?
It fetches an entity from a repository, takes your entity, passes it through modelMapper and saves the result via repository. So while testing this function you should test only that those things happen with correct arguments, not what the modelMapper itself does.
If you want to test your function + modelMapper then this is more of an integration test not a unit test.
Additional notes
Ideally modelMapper would not mutate anything. Instead it would take the two arguments and return the result as a new object. This way it would be more easily testable and mockable (you could avoid using argument verification while testing updateEntity function).
You could extract the mapping to another class that returns the mapped entity, so you could mock that returned value.
I want to pass in suitable object into the verify method, not just any().
Is there a way to do it?
I cannot just take and copy Lambda method and pass the results into the verify. That doesn't work because Lambdas cannot be tested directly.
My Unit test which is obviously not even close to testing anything:
#Test
public void testRunTrigger() {
campaignTrigger.updateCampaignStatus();
verify(jdbcTemplate).update(any(PreparedStatementCreator.class));
assertEquals("UPDATE campaign SET state = 'FINISHED' WHERE state IN ('PAUSED','CREATED','RUNNING') AND campaign_end < ? ", campaignTrigger.UPDATE_CAMPAIGN_SQL);
}
And this is the class I'm testing :
#Component
#Slf4j
public class CampaignTrigger {
final String UPDATE_CAMPAIGN_SQL = String.format("UPDATE campaign SET state = '%s' " +
" WHERE state IN (%s) AND campaign_end < ? ", FINISHED,
Stream.of(PAUSED, CREATED, RUNNING)
.map(CampaignState::name)
.collect(Collectors.joining("','", "'", "'")));
#Autowired
private JdbcTemplate jdbcTemplate;
#Scheduled(cron = "${lotto.triggers.campaign}")
#Timed
void updateCampaignStatus() {
jdbcTemplate.update(con -> {
PreparedStatement callableStatement = con.prepareStatement(UPDATE_CAMPAIGN_SQL);
callableStatement.setTimestamp(1, Timestamp.valueOf(LocalDateTime.now()));
log.debug("Updating campaigns statuses.");
return callableStatement;
});
}
Any advice, or theoretical knowledge that this is not the way to do it I would highly appreciate.
You shouldn't mock the code which you don't control. Mock only the code for which you have your tests, because when mocking you are assuming that you know (i.e. you define) how the mocked class works.
Here, you have no idea how jdbcTemplate works and whether calling it with some lambda actually does what you think it does.
Testing your code with code that you don't control is the point of integration tests. I.e. you should test your CampaignTrigger together with a real database (or in-memory one) and without mocking jdbcTemplate.
You could try your luck with capturing the object that is used for that call, see here. That allows to write code like this:
ArgumentCaptor<Person> argument = ArgumentCaptor.forClass(Person.class);
verify(mock).doSomething(argument.capture());
assertEquals("John", argument.getValue().getName());
Giving you full access to the object that was passed to your method call! And note that mockito recently introduced a #Captor annotation that makes things even easier to use.
Edit; given the comments by #Morfic: what he states is absolutely reasonable.
This answer is giving the "immediate" hint how you could solve that specific problem.
Beyond: the reasonable approach is always always always to slice that "unit under test" ... to be as small as possible!
Your class/method(s) should serve exactly one responsibility; and then you makes sure that the implementation can be tested with most simple means possible.
So: if the question is: "should I use argument captors or should I better rework my production code" - then rework your production code.
I am new to JUnit, and do not know which methods should have tests and which should not. Take the following example:
public List<Site> getSites(String user)
{
SiteDao dao = new SiteDaoImpl();
List<Site> siteList = new ArrayList<Site>();
ServiceRequest rq = new ServiceRequest();
rq.setUser(user);
try
{
ServiceResponse response = siteDAO.getReponse(rq);
List<String> siteNums = response.getSiteNums();
if (siteNums != null && !siteNums.isEmpty())
{
List<DbModelSite> siteInfo = dao.getSiteInfo(siteNums);
if (siteInfo != null && !siteInfo.isEmpty())
{
siteList = SiteMapper.mapSites(siteInfo);
}
}
}
catch (Exception e)
{
e.printStackTrace();
}
return siteList;
}
public static List<Site> mapSites(List<DbModelSite> siteInfo)
{
List<Site> siteList = null;
if (siteInfo != null && !siteInfo.isEmpty())
{
siteList = new ArrayList<Site>();
for (DbModelSite temp : siteInfo)
{
Site currSite = mapSite(temp);
siteList.add(currSite);
}
}
return siteList;
}
public static Site mapSite(DbModelSite site)
{
Site mappedSite = null;
if (site != null)
{
mappedSite = new Site();
mappedSite.setSiteNum(site.getSiteNum());
mappedSite.setSpace(site.getSpace());
mappedSite.setIndicator("Y");
}
return mappedSite;
}
It is pretty trivial to come up with a unit test for both the mapSites() and mapSite()methods, but where I am having trouble is with the getSites() method. Does it make sense to unit test this method? If so, how would I go about doing so? It seems that this would require quite a bit of mocking, and as I am very new to JUnit, I have not been able to figure out how to mock all of these objects.
So my question is really two fold:
How do you determine if a method needs to be unit tested?
How does one unit test a complex method which requires a large amount of mocking?
Yes, it makes sense to test that method.
The first thing to be able to test it, would be to use dependency injection. If the method creates its own SiteDao instance using new, there is no way you can tell the method to use another, mock instance of SiteDao.
So, read on dependency injection, and use it. Basically, it boils down to
public class MyService {
private SiteDao siteDao;
public MyService(SiteDao siteDao) {
this.siteDao = siteDao;
}
// use the siteDao passed when constructing the object, instead of constructing it
}
That way, when testing your service, you can do
SiteDao mockSiteDao = mock(SiteDao.class);
SiteService service = new SiteService(mockSiteDao);
Here's one pice of advice that is not directly related to your question, but will make your code much simpler, and thus easier to test, too:
Never return null from a method returning a collection. Return an empty collection to signal "no element".
In general, don't accept null as a valid method argument value, especially if the argument is a collection.
Corollary of 1 and 2: by following these principles, you never need to check for null or emptyness of a collection. Just use it directly.
This will reduce the number of if (siteNums != null && !siteNums.isEmpty()) cluttering your code, and you'll have way fewer branches to test, too.
Note that all sane libraries (the JDK methods, JPA, etc.) follow these principles. A JPA query will never return a null list for example.
Also, don't swallow an exception by just printing its stack trace and returning an empty list, as if nothing bad happened. Let the exception propagate so that you can notice and fix the bug.
Just imagine that this method is a method returning the number of cancer tumors found by a medical analysis system. Would you really like the system to tell you that you're in perfect health, whereas the system was in fact unable to do its job due to an exception? I would really prefer the system to say "I'm out of order, use another machine to be sure".
The idea of unit testing is to ensure that each "unit" (which is usually a method) can be tested in isolation so you can test that for given input you receive an expected output, so to answer your questions:
I should say all public methods should be unit tested
If you are doing too much in your method that you need to mock lots then you probably want to break the functionality out into another class
Going back to your example there are a couple of things to be wary of if you want to unit test:
new - anytime you use this keyword in a method you will find it difficult to mock that object. In some cases (like ServiceRequest) it's fine but in others such as SiteDao you'll have problems.
Static methods - same thing, with SiteMapper.mapSites(siteInfo) you will find it difficult to mock
You can use libraries such as PowerMock to mock new, private and static methods but I personally try to avoid that.
Super class
public class BasicDao {
public Object createQuery() {
return new Object();
}
}
Implementation
public class MyDAO implements BasicDao {
public Object getMyData() {
Object obj = createQuery();
// more code...
return ...;
}
}
I need to test getMyData() method and I want to mock/suppress createQuery() method cos it will fail on test env.
Thanks!
Don't do tests this way. This is called partial mocking and this is wrong, it means that the code that have been written is not using good Object Oriented design. One should favor Composition OVER Inheritance.
Nevertheless DAO are object that represents the boundary of your system, i.e.
one side is Object Oriented
one side is Relational
In this case you should write integration tests,
insert data in a DB, actual instance or in-memory instance (h2, hsqldb, ...)
prepare your data, there's plenty possibilities here (DbUnit, DbSetup, ...)
configure and run your DAO tests on this database
It may require more setup and more time to run but it is better in the long run.
more robust mapping testing
easier to change the technical implementation, without changing tests
run against a database, you can detect configuration issues/challenges sooner, and make sure they don't break 3 years later
Also answer of #user3386493 is technically correct but I'd suggest to use this kind of stubbing with spies (otherwise actual method code is executed) :
MyDAO daoSpy = Mockito.spy(new MyDAO());
doReturn(new Object()).when(daoSpy).createQuery();
You can do that by using Mockito.
#Test
public void testGetMyData() {
MyDAO dao = new MyDAO();
MyDAO daoSpy = Mockito.spy(dao);
Mockito.when(daoSpy.createQuery()).thenReturn(new Object());
}
I have a class which takes a message with payload String.
The payload is then split and used to create an Entity which is passed to DAOInterface to persist.
How can you test the call daoInterface.insert(entity) has been made?
To Mock the DAOInterface and then verify the call to DAO requires the entity in the test class i.e.
verify(daoInterface).insert(entity);
Is this bad design i.e. creating the entity at this stage? Should the Sting[] split be passed to the DAOImplementaion and initialized there. Example problem,
public class ServiceClass {
#AutoWire
private DAOInterface daoInterface;
public void serviceMessage(Message<String> message) {
MessageHeaders mh = new MessageHeaders(message.getHeaders());
String[] split = ((String) mh.get("payload")).split("_");
code omitted
...
String type = mh.get("WhatType");
Entity entity = new Entity(split[0], split[1], split[2]);
if (type.equals("one"))
{
daoInterface.insert(entity); //How to test?
}
else
{
if (type.equals("two"))
{
doaInterface.modify(entity); //How to test?
}
}
}
}
You can verify with Mockito Matchers.
If you only care that the method is called with some Entity, you can verify that with
verify(daoInterface).insert(any(Entity.class));
If you care about which Entity, and the Entity class has an equals method, you can make an entity that should be equal to the one created and verify with
verify(daoInterface).insert(eq(expectedEntity);
If it's more complex than either of these cases, you can also write your own argument matchers.
The easiest thing you can do is injecting another collaborator to the service which will transform payload to Entity. This way you can keep control on object creation (Inversion of Control). Something like the example below injected to the ServiceClass should do the job:
interface PayloadTransformer {
public Entity transform(String payload);
}
This way your code will be easy to test and you split responsibilities which is usually a good thing. Have a look on Single Responsibility principle
Pushing transformation logic down to dao is almost never a good idea.
BTW. you can write else if without additional brackets and indentations. It's more readable like:
if (a) {
// do something
} else if (b) {
// do something
} else {
// do something
}
The last advice ServiceClass is really poor name for class. The word class is redundant here. Just name it Service, EntityService, MessageService or something which fits your case well.
I wouldn't name field with suffix *Interface as well. Underneath is some implementation injected, I assume. Better name it entityDao or just dao. It's up to you though :)
If you use a test framework like PowerMock, you can invoke private constructors and private methods in your test. This makes it easy to inject mock objects like a mock DAOInterface so you can retrieve it later and test it's been called.
For example, in PowerMock, to call a private constructor:
public class ServiceClass{
#Autowire
private final DAOInterface dao;
public ServiceClass() {
}
private ServiceClass(DAOInterface dao) {
this.dao = dao;
}
}
You simply do:
PrivateConstructorInstantiationDemo instance = WhiteBox.invokeConstructor(
PrivateConstructorInstantiationDemo.class,
new MockDAOInterface() );
So if you're using a dependency inject framework like above, this dovetails nicely. You don't normally have the dependency injection working during test, since it usually requires booting a large chunk of code with a lot of configuration.
By adding a single private constructor, you avoid breaking encapsulation, but you can still inject your mock object into the code during test with a test framework like PowerMock. This is considered best practice.
You could break encapsulation and add publicly accessible methods or ctors to the SeviceClass, but if you don't need them for your design it's not good practice to add them only for test. That's why people put such effort into bypassing encapsulation in frameworks like Mockito and PowerMock. It's not just a dodge around private code, it's because you want to keep the encapsulation while still being able to test.
EDIT:
If you're not familiar with making mock objects, you should do some Google searches on the subject. It's very common and a good skill to have. With the above code, you could make your own mock object. But making mocks is so common that most test frameworks will do this for you.
For example, in PowerMock, I just looked at their page on making mocks here. You can make a mock like this
DAOInteface myMock = createMock(DAOInterface.class);
You can then ask the mock to verify that methods are called:
expect(myMock.someMethod());
Now the mock 'expects' that method to be called, and if it isn't, it'll generate an error for your test. Pretty sweet actually.
You can also return values from a call:
expect(myMock.insert()).andReturn("Test succeeded");
so your code would then see the value "Test succeeded" when it called that method. I don't see that 'insert' does return a value, that's just an example.