Von diesem Artikel ist auch eine deutschsprachige Version verfügbar.
There was this nasty thought coming to my mind when starting to write this blog post: Does the world really need yet another article on JUnit , Mockito and PowerMock ? Well, in fact there is already quite some information available out there. On the other hand every new article sure is touching slightly different aspects of the topic and is therefore hopefully still useful for some readers. But enough with the philosophical part of this blog post. Let’s start with some nice unit testing and (power-)mocking :-).
What are Mocks and why do we need them
This paragraph is intentionally kept short and you can safely skip it in case you already know the concepts behind mocking.
In unit testing we want to test methods of one class in isolation. But classes are not isolated. They are using services and methods from other classes. Those are often referred to as collaborators. This leads to two major problems:
- External services might simply not work in a unit testing environment as they require database access or are using some other external systems.
- Testing should be focused on the implementation of one class. If external classes are used directly their behaviour is influencing those tests. That is usually not wanted.
This is when mocks are entering the stage and thus Mockito and PowerMock. Both tools are “hiding away” the collaborators in the class under test replacing them with mock objects. The division of work between the two is that Mockito is kind of good for all the standard cases while PowerMock is needed for the harder cases. That includes for example mocking static and private methods.
More information can be found from the following article which is kind of THE article on the subject matter 🙂 http://martinfowler.com/articles/mocksArentStubs.html .
Mocking with Mockito
Let’s jump into a first example right away. In the dummy application that is used here to have some examples there is a class ItemService that is using an ItemRepository to fetch data from the database. Obviously that is a good candidate to do some mocking. In the method under test an item is fetched by its id and then some logic is applied. We only want to test that logic.
1public class ItemServiceTest {
2
3 @Mock
4 private ItemRepository itemRepository;
5
6 @InjectMocks
7 private ItemService itemService;
8
9 @Before
10 public void setUp() throws Exception {
11 MockitoAnnotations.initMocks(this);
12 }
13
14 @Test
15 public void getItemNameUpperCase() {
16
17 //
18 // Given
19 //
20 Item mockedItem = new Item("it1", "Item 1", "This is item 1", 2000, true);
21 when(itemRepository.findById("it1")).thenReturn(mockedItem);
22
23 //
24 // When
25 //
26 String result = itemService.getItemNameUpperCase("it1");
27
28 //
29 // Then
30 //
31 verify(itemRepository, times(1)).findById("it1");
32 assertThat(result, is("ITEM 1"));
33 }
34}
Probably in most projects some kind of dependency injection framework is used. Therefore the example is based on Spring and we are using the corresponding Mockito annotation @Mock to create the mock objects. The class under test is then annotated with the @InjectMocks annotation. This is quite straightforward together with initializing the mocks in the setup-method of the JUnit-class.
The complete example project can be found from GitHub here: https://github.com/ThomasJaspers/java-junit-sample . This does not only contain the test samples, but also the dummy application that contains the functionality to be tested. Thus it is really easy to play around with this :-).
The actual mocking happens using the “when-method-call-then-return”-syntax like when(itemRepository.findById(“it1”)).thenReturn(mockedItem). There are other possibilities like throwing an exception for example. Even if the example is a bit artificial it hopefully helps getting used to the syntax and the overall approach. When testing more complex classes/methods later on the amount of mock-objects might be a bit discouraging on first sight. Anyway, this would be of course an additional motivation to keep methods short.
Beside the mocking of method-calls it is also possible to verify that those methods have been really called. This happens in the above example in the line verify(itemRepository, times(1)).findById(“it1”) and is especially useful for testing the flow logic of the class under test.
More Mocking with Mockito
The previous paragraph was showing kind of the basics of using Mockito. Of course there are more possibilities and one rather important one is changing objects passed to mocked objects as parameters. This can be done using doAnswer as shown in the following example which is an excerpt from this JUnit test .
1@Test
2public void testPLZAddressCombinationIncludingHostValue() {
3
4 //
5 // Given
6 //
7 Customer customer = new Customer("204", "John Do", "224B Bakerstreet");
8
9 doAnswer(new Answer<Customer>() {
10 @Override
11 public Customer answer(InvocationOnMock invocation) throws Throwable {
12 Object originalArgument = (invocation.getArguments())[0];
13 Customer param = (Customer) originalArgument;
14 param.setHostValue("TestHostValue");
15 return null;
16 }
17 }).when(hostService).expand(any(Customer.class));
18
19 when(addressService.getPLZForCustomer(customer)).thenReturn(47891);
20 doNothing().when(addressService).updateExternalSystems(customer);
21
22 //
23 // When
24 //
25 String address = customerService.getPLZAddressCombinationIncludingHostValue(customer, true);
26
27 //
28 // Then
29 //
30 Mockito.verify(addressService, times(1)).updateExternalSystems(any(Customer.class));
31 assertThat(address, is("47891_224B Bakerstreet_TestHostValue"));
32}
With the concepts shown so far it should be possible to cover most of the “standard usecases”. But of course the answer to one important question is still missing: What if – for example – static methods from a collaborator are used? Probably by now it is not that hard to guess the answer to this :-).
PowerMock – Mocking the Impossible
With PowerMockito it is possible to mock all the hard cases that Mockito does not support. Most of the time this means mocking of static methods. But it is also possible to mock private methods and constructor calls. Anyway most of time the use case is mocking static methods calls. PowerMockito is using byte code manipulation and thus it comes with its own JUnit runner. Furthermore the list of classes that needs to be mocked must be given using the @PrepareForTest annotation. Let’s take a look at an example again.
1@RunWith(PowerMockRunner.class)
2@PrepareForTest({StaticService.class})
3public class ItemServiceTest {
4
5 @Mock
6 private ItemRepository itemRepository;
7
8 @InjectMocks
9 private ItemService itemService;
10
11 @Before
12 public void setUp() throws Exception {
13 MockitoAnnotations.initMocks(this);
14 }
15
16 @Test
17 public void readItemDescriptionWithoutIOException() throws IOException {
18
19 //
20 // Given
21 //
22 String fileName = "DummyName";
23
24 mockStatic(StaticService.class);
25 when(StaticService.readFile(fileName)).thenReturn("Dummy");
26
27 //
28 // When
29 //
30 String value = itemService.readItemDescription(fileName);
31
32 //
33 // Then
34 //
35 verifyStatic(times(1));
36 StaticService.readFile(fileName);
37 assertThat(value, equalTo("Dummy"));
38 }
39}
It can be nicely seen here that the tests are written in almost the same manner using PowerMock as we are used to from Mockito. Main reason for this is that PowerMock comes with a specific API for Mockito (and also for EasyMock). This can be seen from an excerpt of the Maven file where we are not only importing the PowerMock JUnit-module but also the Mockito-API:
1... 2<dependency> 3 <groupId>org.powermock</groupId> 4 <artifactId>powermock-module-junit4</artifactId> 5 <version>1.6.4</version> 6 <scope>test</scope> 7</dependency> 8 9<dependency> 10 <groupId>org.powermock</groupId> 11 <artifactId>powermock-api-mockito</artifactId> 12 <version>1.6.4</version> 13 <scope>test</scope> 14</dependency> 15...
Classes containing static methods must be mocked using the mockStatic()-method. Also verification if a method has actually been called is slightly different. But for the when-then mocking-part the syntax stays the same.
Of course you can – and probably will – use Mockito and PowerMock in the same JUnit test at some point of time. When doing so a small agreement in the team might be helpful on which methods are statically imported (e.g. Mockito-when) and which are then used fully qualified (e.g. PowerMockito.when) to avoid confusion.
One feature of PowerMock, that is quite handy at times, is the possibility to delegate to another JUnit runner using the @PowerMockRunnerDelegate annotation. This is shown in the following code snippet and the complete example can be found here .
1@RunWith(PowerMockRunner.class)
2@PowerMockRunnerDelegate(Parameterized.class)
3@PrepareForTest({StaticService.class})
4public class RateServiceTest {
5...
6}
The example is delegating to the Parametrized JUnit runner , while using PowerMock at the same time. Another use case – that might be quite likely – is delegating to SpringJUnit4ClassRunner.class.
Conclusion
Mockito is offering a very readable and easy to use interface for mocking tests in Java. As PowerMock is offering a Mockito-like API it can be used almost the same way as Mockito itself. This is really quite convenient.
Tests using mocks can be written very readable. But as always in unit testing this depends mostly on the classes under test.
Well, other than that there is not much more to say than: Just get started and happy mocking :-).
More articles
fromThomas Jaspers
Your job at codecentric?
Jobs
Agile Developer und Consultant (w/d/m)
Alle Standorte
More articles in this subject area
Discover exciting further topics and let the codecentric world inspire you.
Gemeinsam bessere Projekte umsetzen.
Wir helfen deinem Unternehmen.
Du stehst vor einer großen IT-Herausforderung? Wir sorgen für eine maßgeschneiderte Unterstützung. Informiere dich jetzt.
Hilf uns, noch besser zu werden.
Wir sind immer auf der Suche nach neuen Talenten. Auch für dich ist die passende Stelle dabei.
Blog author
Thomas Jaspers
Do you still have questions? Just send me a message.
Do you still have questions? Just send me a message.