Have you ever felt that your code was difficult to test because of your classes, for example, depends on something external? A typical issue is that you have some class managing database operations and during your tests, you don’t want to query the database as it is usually considered out of scope from unit tests. Well, no worries Mockito has got you covered.



Before we begin, I want to emphasize that if you can avoid using mocks while testing your code, then that’s great and you probably should. But the reality is that most code is difficult to test without mocking and if you feel like you have to use it, then you definitely should. The point of this blog is not to convince you to mock everything. I simply want to show you how you can use mocking.

Prerequisites

I will code an example project throughout the post and you are very welcome of following along. I have provided you with a skeleton Spring boot project that you are welcome to clone in order to participate. The skeleton implements a couple of components such as repositories and services.

Java 8

Maven

Skeleton project

The project implements a basic REST service which lets you save, retrieve and delete users, with a basic in-memory database.

Mockito

Mockito is a powerful Java mocking framework where you set up ”fake” objects of classes (called mocks) of your choosing. With these mocks you are then able to configure how they should act and what they should return when a method is called.

package org.thecuriousdev.demo.skeleton.controller; import org.junit.Before; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.thecuriousdev.demo.skeleton.db.PersonRepository; public class PersonControllerTest { @Mock private PersonRepository personRepository; private PersonController personController; @Before public void setup() { MockitoAnnotations.initMocks(this); personController = new PersonController(personRepository); } }

We start by creating a PersonControllerTest class where we annotate our PersonRepository object with @Mock . We then set up a @Before method which runs before each test where we initialize our mocks and create a PersonController class with that mock.

Next import the following static classes.

import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.mockito.Matchers.any; import static org.mockito.Mockito.when;

Add the following test case where we use Mockito.when to instruct the mock what it should respond when called upon the provided method.

@Test public void retrievePersonWithMockTest() { Person person = new Person("viktor", 24, "tacos"); when(personRepository.findById(any())).thenReturn(Optional.of(person)); ResponseEntity response = personController.getPerson("viktor"); assertNotNull(response); assertEquals(response.getStatusCodeValue(), 200); assertEquals(person, response.getBody()); }

We have now successfully mocked the PersonRepository. We instructed our mock to return a user that we created when findById(String) is called with anything. We can now verify that our controller method handles the data correctly returned from the repository.

Important to note is that methods who returns void does not run when called on a mock object, but you can instruct the mock to perform some action then. But we will later in this post show some interesting stuff that can be done on void methods always.

Spy

Mockito also supports stubbing only specific methods but keep an actual real object of the class. This is very useful when you, for example, have a class picking up something from a database and then processing it with some function(s). Stubbing the actual method that retrieves something from the database (instead of mocking the whole object) lets us test and verify that the processing function(s) works as expected. This is referred to as a spy.

package org.thecuriousdev.demo.skeleton.controller; import org.junit.Before; import org.thecuriousdev.demo.skeleton.db.PersonRepository; import java.util.Optional; import static org.mockito.Mockito.spy; public class PersonControllerSpyTest { private PersonController personController; private PersonRepository personRepository; @Before public void setup() { personRepository = spy(new PersonRepository()); personController = new PersonController(personRepository); } }

Here we initialized a “normal” PersonRepository but wrapped a Mockito.spy around it and created a PersonController class with it.

A spy enables us to perform various verifications on methods of the class that was wrapped. Additionally, it makes it possible to stub specific methods but leaves the other ones as they were.

import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.mockito.Matchers.any; import static org.mockito.Mockito.doReturn;

Add the static imports.

@Test public void retrievePersonWithSpyTest() { Person person = new Person("nastya", 24, "sushi"); doReturn(Optional.of(person)).when(personRepository).findById(any()); ResponseEntity response = personController.getPerson("nastya"); assertNotNull(response); assertEquals(response.getStatusCodeValue(), 200); assertEquals(person, response.getBody()); }

Almost similar to the mock object we here instruct our PersonRepository to perform a stub on findById(String) . Do notice, the difference between the parentheses though. When stubbing methods on a spy the right parenthesis comes before the method call.

What about static methods? These are quite normal in utility classes, can we mock these? Sadly not with only Mockito, but let me then introduce PowerMock.

PowerMock

A very large benefit of adding PowerMock to your project is that it enables you to perform stubbing on static methods.

I do however want to advise you to only use PowerMock when you really have to. PowerMock uses a lot of sorcery on a bytecode level in order to do what it does which may cause some difficult bugs to debug in your tests. I have never run into this personally though and it is definitely better to use PowerMock to test otherwise untestable code, rather than leaving it not tested. But once again, stick to Mockito only when possible.

In order to use PowerMock, you will need to add two extra dependencies to your project in your pom.xml file.

<dependency> <groupId>org.powermock</groupId> <artifactId>powermock-module-junit4</artifactId> <version><1.6.4</version> <scope>test</scope> </dependency> <dependency> <groupId>org.powermock</groupId> <artifactId>powermock-api-mockito</artifactId> <version><1.6.4</version> <scope>test</scope> </dependency>

package org.thecuriousdev.demo.skeleton.controller; import org.junit.Before; import org.junit.runner.RunWith; import org.powermock.core.classloader.annotations.PrepareForTest; import org.powermock.modules.junit4.PowerMockRunner; import org.thecuriousdev.demo.skeleton.db.SimulatedDatabase; import org.thecuriousdev.demo.skeleton.db.PersonRepository; import static org.powermock.api.mockito.PowerMockito.mockStatic; @RunWith(PowerMockRunner.class) @PrepareForTest(SimulatedDatabase.class) public class PersonControllerStaticMockTest { private PersonController personController; @Before public void setup() { personController = new PersonController(new PersonRepository()); mockStatic(SimulatedDatabase.class); } }

The first thing we need to do is to instruct PowerMock to mock the entire class SimulatedDatabase which holds static methods. We do that by calling the mockStatic method. Like explained earlier you can also set up a spy instead and just stub one of the methods. Now we can move on and create our tests. Notice how we do not mock the PersonRepository anymore because we simply do not need it since with PowerMock we can mock the static method that it uses.

import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.mockito.Matchers.any; import static org.powermock.api.mockito.PowerMockito.when;

Import static methods once again. But notice how we use the method when, from the PowerMock API instead.

@Test public void retrievePersonWithStaticMockTest() { Person person = new Person("jimmy", 24, "pizza"); when(SimulatedDatabase.getPerson(any())).thenReturn(person); ResponseEntity response = personController.getPerson("jimmy"); assertNotNull(response); assertEquals(response.getStatusCodeValue(), 200); assertEquals(person, response.getBody()); }

After that, we can test exactly as we did with a normal mock, and now we only mocked the actual static simulated database, so in this test, we were also able to verify that the PersonRepository behaves as expected.

In combination with Mockito, you shouldn’t have much trouble testing your code. Now instead let’s have a look at a couple of extra verification methods.

Verification

ArgumentCaptor

Mockito and PowerMock supports verification that methods were called or not, and in combination with an ArgumentCaptor you can do some really neat checks. Let’s go back and add an extra test with PowerMock.

import static org.powermock.api.mockito.PowerMockito.verifyStatic; import static org.mockito.ArgumentCaptor.forClass; import static org.mockito.Mockito.spy;

Add those static imports before continuing.

@RunWith(PowerMockRunner.class) @PrepareForTest(SimulatedDatabase.class) public class PersonControllerStaticMockTest { private PersonController personController; private PersonRepository personRepository; @Before public void setup() { personRepository = spy(new PersonRepository()); personController = new PersonController(personRepository); mockStatic(SimulatedDatabase.class); } ... }

We will also update our setup() method to create a spy on PersonRepository so that we can do some verification on that layer.

@Test public void retrievePersonWithStaticMockAndVerificationTest() { Person person = new Person("joakim", 48, "steak"); ResponseEntity<Person> response = personController.savePerson(person); ArgumentCaptor<Person> personCaptor = forClass(Person.class); verifyStatic(); SimulatedDatabase.savePerson(personCaptor.capture()); Person capturedPerson = personCaptor.getValue(); assertNotNull(capturedPerson); assertEquals(person, capturedPerson); assertNotNull(response); assertEquals(204, response.getStatusCodeValue()); }

With the ArgumentCaptor we have now captured the Person object that was passed into the savePerson(Person) method. Which makes it possible to verify that the method was called and that the expected data was passed as the argument.

Times

Let’s improve our first test what we wrote; retrievePersonWithMockTest().

import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.ArgumentCaptor.forClass;

Add the static imports.

@Test public void retrievePersonWithMockTest() { Person person = new Person("viktor", 24, "tacos"); when(personRepository.findById(any())).thenReturn(Optional.of(person)); ResponseEntity<Person> response = personController.getPerson("viktor"); ArgumentCaptor<Person> personNameCaptor = forClass(String.class); verify(personRepository, times(1)).findById(personNameCaptor.capture()); String name = personNameCaptor.getValue(); assertNotNull(name); assertEquals("viktor", name); assertNotNull(response); assertEquals(response.getStatusCodeValue(), 200); assertEquals(person, response.getBody()); }

With Mockito.times(int) we are now able to verify that the method was indeed actually called and how many times. If we want to verify that some method is not run we can, of course, ask for times(0). Mockito.times(int) also works very nice in combination with the ArgumentCaptor.

There are also more similar verifications methods available.

Mockito.atleastOnce()

Mockito.atLeast(int)

Mockito.atMost(int)

Final words

There are actually tons of more available features of Mockito and PowerMock what I haven’t gone through but the post would become way too long if I would choose to do that. We have, however, gone through the most vital stuff and you should have enough knowledge to start exploring with these two powerful libraries on your own. I do recommend that you read through their documentation and have a look at yourself what other features that they offer.

The code from this blog can be found here.

If you enjoyed the post, please help me out by giving it a like and sharing it on social media!

Like this: Like Loading...