The Purpose of this project is an introduction to less common features of Mockito framework and test templates in IntelliJ IDEA.
I will start with fundamental things of mocking/stubbing in order to lay the ground for more advanced things. They will help us highlights problems we encounter on daily bases when working with Mockito and embraces solutions :-)
Agenda
- Default behaviour of not stubbed method
- SmartNulls
- Mockito global configuration
- Deep stubbing
- Templates in intelliJ
When no stubbed, methods of mocks return zeros, falseys, empty collections or nulls - depends what is the return type of used method. It can lead to encountering NullPointerException.
java.lang.NullPointerException
at domain.InvoiceService.issueInvoice(ItemService.java:10)
at domain.a_missing_mock_for_organisation.ItemServiceTest.shouldIssueAnInvoiceWithSuccess(ItemServiceTest.java:24)
Code example here
Above stack trace informes us only about the line number where NullPointerException occurred. It isn’t obvious at first glance that the source of the problem was default value returned from unstubbed method that developer has forgotten to stub. What if we could get a more readable stack trace that help us track the bug right away. It turns out that Mockito has such feature - SmartNull.
SmartNull produces much more descriptive exception message then NPE. It points exactly what unstubbed method on mock was called that lead to NPE.
org.mockito.exceptions.verification.SmartNullPointerException:
You have a NullPointerException here:
-> at domain.InvoiceService.issueInvoice(ItemService.java:10)
because this method call was *not* stubbed correctly:
-> at domain.InvoiceService.issueInvoice(ItemService.java:10)
order.getOrganisation();
at domain.InvoiceService.issueInvoice(ItemService.java:10)
at domain.b_smart_null.InvoiceServiceTest.shouldIssueAnInvoiceWithSuccess(ItemServiceTest.java:28)
Code example here
To make mock returns SmartNull instead of null override default answer with RETURNS_SMART_NULLS flag
mock(Item.class, RETURNS_SMART_NULLS);
or
@Mock(answer = Answers.RETURNS_SMART_NULLS)
Item order;
We need to change default answer for every mock that we want to return smart nulls. Because of this ceremony not many people opt to use this feature.
It turns out that with a little work we can change default answer for all mock to RETURNS_SMART_NULLS with no additional repetitive work.
Mockito provides a global configuration that we can use to change some default behavior. For instance, we can change default answer for unstubbed mock to RETURNS_SMART_NULLS.
In order to do this, we need to create class org.mockito.configuration.MockitoConfiguration (in test folder) that extends DefaultMockitoConfiguration.
public class MockitoConfiguration extends DefaultMockitoConfiguration{
public Answer<Object> getDefaultAnswer() {
return new ReturnsSmartNulls();
}
}
Code example here
Sometimes when working with a badly design legacy system we encounter a situation where in order to tests something our mock has to return another mock to return something meaningful.
Example of ‘deep stubbing’
Item order = mock(Item.class, RETURNS_SMART_NULLS);
Organisation orderItem = mock(Organisation.class);
when(order.getOrganisation()).thenReturn(orderItem);
when(orderItem.getOrganisationId()).thenReturn(0l);
We can shorten deep stubbing creation with RETURNS_DEEP_STUBS
Item order = mock(Item.class, RETURNS_DEEP_STUBS);
when(order.getOrganisation().getOrganisationId()).thenReturn(0l);
More information here
IntelliJ provides templates for test methods and test classes. They are very helpfull in getting ride of repetive task such as:
- add static import for Mockito and AssertJ
- embeding comments for 'given-when-then' inside test method
In order to edit template for test method go to File | Settings | Editor | File and Code Templates. Choose 'code' tab and edit JUnit4 Test Method with following values.
@org.junit.Test
public void should${NAME}() {
//given
${BODY}
//when
//then
}
To use template press Cltr + Insert inside test class and choose Test Method.
In order to edit template for test class go to File | Settings | Editor | File and Code Templates.
import static org.assertj.core.api.Assertions.*;
import static org.mockito.BDDMockito.*;
#parse("File Header.java")
public class ${NAME} {
${BODY}
}
To use template press Ctrl + Shift + T inside class and pick Create New Test ...