I was recently introduced to Mockito by my colleagues at Sentia, where I currently work as a Java software developer. As a green developer we all know that, being told to use new tools in fast paced projects can sometimes be a bit overwhelming.
Therefore, as part of my personal development at Sentia, I decided to write this blog post about Mockito. Hopefully it will function as a floatation device as you are being thrown into the deep end, like I once was. This post is based on JUnit 5 and will focus mainly on the basics of Mockito. More advanced features will be discussed in my upcoming post. So, stay tuned for more.
Why Mockito
Prior to testing, it is essential to define our System Under Test. In Unit testing, the set of units we are interested in testing at a certain time is called the System Under Test (SUT). Of course, in any non-trivial SUT class we are likely to find dependencies on other classes that fall outside the SUT.
Some of these dependencies are part of the same source root, others part of the core or third-party libraries. They may be very trustworthy and deterministic, but may just as well depend on very non-deterministic and unpredictable external resources like an I/O device or network.
The latter case is very bad for testing. In a unit test we would like the world outside the SUT to behave exactly the way we want to.
In order to achieve that, instead of running our tests against real objects, we would like to replace them with mock objects (test doubles), set them to behave a certain way so we can focus solely on assessing the SUT.
This is where Mockito comes in handy. It allows us to isolate the SUT’s actual logic from external resources by providing a powerful API for mocking, setting expectations on the mock objects, and verifying method’s post-conditions.
In software development it is good practice to add automated unit tests to run and notify us in case code breaks the system, so that faulty code can quickly be identified and fixed. This is not only beneficial in the development stage but further down the road in code management as well. However, there is little gain in writing unit tests when automated test suites take a long time to run. In order to prevent the development workflow from blocking, we need a single JUnit test to take at max a few milliseconds to execute.
Here Mockito proves to be useful too. By replacing external dependencies with lightning fast mock objects, we eliminate those typical real-world delays, allowing our tests to run without blocking.
What is Mockito
Mockito not only tastes great but is also the most popular open source unit mocking framework for Java. It allows mock object creation, stubbing, and verification.
At the time of writing (June 2020), Mockito v3.3.12 is the latest version.
You can check out the Mockito source at https://github.com/mockito/mockito and visit https://site.mockito.org to learn more about Mockito and download the jar file.
Stubs vs mocks
Before we delve into the Mockito specifics let us point out the difference between mocks and stubs. Since these two notions are somewhat similar, many beginners easily confuse mock objects with common testing notion of using stubs.
In Martin Fowler’s article stubs and mocks are described as follows:
- Stubs provide canned answers to calls made during the test, usually not responding at all to anything outside what’s programmed in for the test. Stubs may also record information about calls, such as an email gateway stub that remembers the messages it ‘sent’, or maybe only how many messages it ‘sent’.
- Mocks are what we are talking about here: objects pre-programmed with expectations which form a specification of the calls they are expected to receive.
The purpose of both is to eliminate testing all the dependencies of a class or function so your tests are more focused and simpler in what they are trying to prove.
The biggest distinction is that a stub is already written with predetermined behavior. A mock is not setup in a predetermined way, it is something that as part of your test needs to be set expectations on. Mocks in a way are determined at runtime since the code that sets expectations, must run before they do anything.
Furthermore, replacing a real object with a mock object is called mocking. And we speak of stubbing when we set certain expectations on a mock, so it behaves in that specific way at runtime.
Armed with this knowledge let us see how things are done in Mockito.
How to
Generally speaking, using Mockito involves three different steps: (Garcia, 2017, p. 320)
- Mocking objects
- Stubbing methods (setting expectations)
- Verification
Our focus will be on testing the following code example.
public class ProductServiceImpl implements ProductService {
@Autowired private ProductDao productDao;
@Override
public boolean updateProduct(Product product) {
if (productDao.getById(product.getId()) == null) {
return false;
}
return productDao.update(product);
}
}
1. Mocking objects
Notice that within our product service, the productDao object interacts with classes residing outside our SUT. Therefore we will mock productDao. This can be done in two ways. The first approach is to use the static method mock( ) defined in the org.mockito.Mockito class. Here we will use the static import feature of Java.
import static org.mockito.Mockito.mock;
public class ProductServiceTest {
ProductDao productDao = mock(ProductDao.class);
}
The second way is to annotate the class member variable with the @Mock annotation. To create mocks using the @Mock annotation, we either need to initialize the mocks before test execution using MockitoAnnotations.initMocks(this), or use the JUnit 5 extension mechanism instead.
See following code snippet where we have used the latter case:
import org.mockito.Mock;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.junit.jupiter.MockitoExtension;
@ExtendWith(MockitoExtension.class)
public class ProductServiceTest {
@Mock
ProductDao productDao;
}
For those using JUnit 4, this code will give errors. Instead replace the class level annotation with @RunWith(MockitoJUnitRunner.class).
Both cases achieve the same result. Using the annotation is usually considered cleaner, as there is no need for boilerplate assignments that all look the same. However, since the @Mock annotation can only be declared outside of methods and has a class scope, it can sometimes be handy to use the mock( ) variant instead. Namely when you only want to mock an object inside a particular method and have it behave as a real object elsewhere.
In addition to regular mocks, Mockito also allows us to identify the object where mocks are going to be injected. This is typically used for the unit we want to test. This can be accomplished with the @InjectMocks annotation.
2. Stubbing methods
Before we explain how to stub methods in Mockito, let us first explain what method stubbing really is.
Stubbing a method means setting up an expectation on a method invocation or simulating the behavior of the method.
Mock objects are basically proxy objects, and they imitate the behavior of real objects.
We can stub a method on a mock object to redefine the behavior of the method. In other words, we can return a specific value or throw a specific exception when the method is called on the mocked object.
Stubbing is quite powerful because it allows us to partition the control flow of our program and test each unit (partition) in isolation.
If we don’t stub a method of a mock object, by default it will return either null, a primitive value, or an empty collection, as appropriate. For example 0 for an int and false for a boolean.
The static Mockito.when( ) method identifies a method that needs to be stubbed, and the thenReturn( ) method returns a specific value.
Let’s say we want to stub the method getById(String id) to test the case where the product we want to update exist, then we will do this as follows:
when(productDao.getById(anyString())).thenReturn(product);
Here we tell Mockito to return a (mocked) product object in case the method getById is called on the mocked object productDao, and we tell it that any String as parameter will do.
Mockito provides various other stubbing methods, see section Overview of Mockito API below for more.
3. Verification
In the last phase we verify if the expectations of our tests have actually been met. Mockito offers a powerful API to carry out different types of verifications. Using Mockito, we can ensure that a mock method, with required arguments, is being called or not. On top of that, we can also verify the invocation order with a mock, or capture and verify the argument passed to a stubbed method. Furthermore, the verification capabilities of Mockito can be complemented with Standard JUnit assertions.
See section Overview of Mockito API for more on verification.
In our complete example below we use the static verify( ) method, and in one case enhance our verification with the never( ) option, to ensure whether the stubbed method update(Product product) is invoked or is never invoked. Depending on what getById returns.
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.Matchers.anyString;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.junit.jupiter.MockitoExtension;
import org.mockito.InjectMocks;
import org.mockito.Mock;
@ExtendWith(MockitoExtension.class)
public class ProductServiceTest {
@InjectMocks private ProductService productService = new ProductServiceImpl();
@Mock ProductDao productDao;
@Mock Product product;
@Test
public void updateProductWhenProductExisting() {
when(productDao.getById(anyString())).thenReturn(product);
boolean result = productService.updateProduct(product);
assertTrue(result);
verify(productDao).update(product);
}
@Test
public void updateProductWhenNonExisting() {
when(productDao.getById(anyString())).thenReturn(null);
boolean result = productService.updateProduct(product);
assertFalse(result);
verify(productDao, never()).update(product);
}
}
Just for fun I have hidden an easter egg here. That is, one of the lines of code in our example is abundant. Can you find it?
Overview of Mockito API
The following table summarizes the Mockito APIs grouped by the aforementioned steps: (Garcia, 2017, pp. 322-323)
Mockito API | Description | Step |
---|---|---|
@Mock | This annotation identifies a mock object to be created by Mockito | Mocking objects |
@InjectMocks | This annotation identifies the object in which the mocks are going to be injected. This is used typically to the unit we want to test | Mocking objects |
@Spy | In addition to mocks, Mockito allows us to create spy objects, which is a partial mock implementation. Spy objects use the real implementation in non-stubbed methods | Mocking objects |
Mockito.when(x).thenReturn(y) Mockito.doReturn(y).when(x) | These methods allow us to specify the value (y) that should be returned by the stubbed method (x) of a given mock object. | Stubbing methods |
Mockito.when(x).thenThrow(e) Mockito.doThrow(e).when(x) | These methods allow us to specify the exception (e) that should be thrown when calling a stubbed method (x) of a given mock object. | Stubbing methods |
Mockito.when(x).thenAnswer(a) Mockito.doAnswer(a).when(x) | Unlike returning a hardcoded value, a dynamic user-defined logic (Answer a) is executed when a given method (x) of the mock is invoked. | Stubbing methods |
Mockito.when(x).thenCallRealMethod() Mockito.doCallRealMethod().when(x) | This method allows us the real implementation of a method instead the mocked one. | Stubbing methods |
Mockito.doNothing().when(x) | When using a spy, the default behavior is calling the real methods of the object. In order to avoid the execution of a void method x, this method is used. | Stubbing methods |
BDDMockito.given(x).willReturn(y) BDDMockito.given(x).willThrow(e) BDDMockito.given(x).willAnswer(a) BDDMockito.given(x).willCallRealMethod() | Behaviour-driven development is a test methodology in which tests are specified in terms of scenarios and implemented as given (initial context), when (event occurs), and then (ensure some outcomes). Mockito supports this type of tests through the class BDDMockito. The behavior of the stubbed methods (x) is equivalent to Mockito.when(x). | Stubbing methods |
Mockito.verify() | This method verifies the invocation of mock objects. This verification can be optionally enhanced using the following methods: times(n): The stubbed method is invoked exactly n times. never(): The stubbed method is never called. atLeastOnce(): The stubbed method is invoked at least once. atLeast(n): The stubbed method is called at least n times. atMost(n): The stubbed method is called at the most n times. only(): A mock fails if any other method is called on the mock object. timeout(m): This method is called in m milliseconds at the most. | Verification |
Mockito.verifyZeroInteractions() Mockito.verifyNoMoreInteractions() | These two methods verify that a stubbed method has no interactions. Internally, they use the same implementation. | Verification |
@Captor | This annotation allows us to define an ArgumentChaptor object, aimed to verify the arguments passed to a stubbed method. | Verification |
Mockito.inOrder | It facilitates verifying whether interactions with a mock were performed in a given order. | Verification |
Limitations
When using Mockito, keep in mind that it does have limitations. As is the case with any great tool. Mockito cannot mock or spy on Java constructs such as final classes and methods, static methods, enums, private methods, the equals( ) and hashCode( ) methods, primitive types, and anonymous classes.
However, it is debatable whether these limitations could actually force us to improve our code in some way or another. It could make us rethink our design and push us towards restructuring our classes and methods. Also remember that, not everything needs to be mocked or substituted for your test to remain a unit test.
Alternatively, PowerMockito, a PowerMock’s extension API to supports Mockito, offers us the functionality to overcome these limitations.
Summary
In this blog post we explained some basic topics of Mockito. First, we explained where Mockito comes in handy and where you can get it. Then we talked a bit about the three steps involved in using Mockito. (Mocking objects, stubbing methods, and verification) We gave an example to help solidify it. In my upcoming post I will dive more into some advanced features and discuss some of the topics mentioned in the Overview API table.
References:
Garcia, B (2017), “Mastering Software Testing with JUnit 5”, Birmingham - Mumbai, Packt Publishing