In this tutorial, we will discuss how to Mock Objects and Stub Methods with Mockito, and we will see various ways to stub with common scenarios and examples.
Mocking and stubbing are the basis of writing clean and simple unit tests. So, let’s begin!
Visit our Mockito introductory level tutorial if you are a beginner and have just begun exploring the framework.
What is a Mock Object?
A mock object is the intimation of a programmed module/method with some pre-defined behaviour or values. This mock object is used as a substitution for a real object. This is a widely used concept in the world of testing, where we mimic the behaviour of one object to test the behaviour of another service or object.
How to Stub Method?
A mock object alone is nothing until we abstract its behaviour by stubbing the method. The term stub method means that we define what should happen whenever an interaction is expected to be done on the mocked object. Stubbing is declared on the mock object.
Consider an example, we have a Library Management System where we have a class that returns the list of available books category-wise in the library after fetching data from the database. The assumption is class reads data via LibraryRepository class that actually persists data on some Relational Database. In this situation, whenever we want to test utility class behaviour that actually divides the data (fetched from the database) based on their category, we would completely remove the dependency on the Database layer and would introduce a mock object of LibraryRepository. Therefore we can control the behaviour of what data is returned from the database. This would involve two steps:
- Creating a Mock object using static methods or annotations.
- To stub method – to control the behaviour or answers to the calls applied on the mocked objects.
Stay put! We’ll look at the examples of stubbing practically in the next section.
Setting Up Mockito
We will be using JUnit as a unit testing framework. Since Mockito isn’t tied to JUnit, you can still follow along even if you use a different framework.
Let’s assume you have a Maven Spring Boot Project. Refer to this article for more details. To begin working with the Mockito framework, all we need to do is to include the following dependency to our Maven project:
<dependency> <groupId>org.mockito</groupId> <artifactId>mockito-core</artifactId> <version>4.7.0</version> <scope>test</scope> </dependency>
Different Ways to Stub Methods
To actually direct what should happen to a mocked object, we need to stub the method. Mockito offers two ways to stubbing methods:
- The when-then approach
- The do-when approach
The when-then Approach
The When-Then method is the most commonly used approach to stub mock objects with better readability. It follows stubbing directives as mentioned below:
when(something).thenReturn(somethingElse)
Mockito when() method:
This method enables stubbing. It is used whenever we want to return specific values from a certain method. It is ideal to use in a scenario where we have some condition associated with it, for example, When this happens, Then do this.
Different methods used with the when-then approach
Now, we will discuss some common scenarios of mocking objects and stubbing methods using the when-then approach.
Stubbing consecutive calls
We often encounter use-case where we want to return different behaviour on the same method call. For instance, if we want to check some logic based on the BMI result returned by the BmiService, we must return numerous distinct return values to test the flow completely.
Here is a small test snippet to demonstrate the usage:
@Test public void testCalculateBMI() { when(bmiService.calculateBMI(1.754,50)).thenReturn("Obese"); String result1 = bmiCalculator.calculateBMI(1.754,50); when(bmiService.calculateBMI(1.754,30)).thenReturn("Under weight"); String result2 = bmiCalculator.calculateBMI(1.754,30); assertEquals("Obese",result1); assertEquals("Under weight",result2); }
This shows that the first stubbing works whenever the first real method gets executed, and subsequently, for the next real method call, the last stubbing wins. Please note if any other real method call is made, the result of the last stubbing will work.
The most common use-case of stubbing consecutive calls is mocking iterators.
Stubbing with callbacks
As per the official documentation of Mockito, stubbing with callbacks is not preferred. Though, they have introduced an interface Answer to handle such a scenario if needed. This is useful when we want to perform certain actions while executing stubs.
The following is an example of using the Answer interface within the stubbing:
@Test public void testCalculateResult() { Student expectedStudentObj = new Student(); expectedStudentObj.setId(1); expectedStudentObj.setName("Reham Muzzamil"); when(studentService.getStudentById(anyString())).thenAnswer( new Answer() { public Object answer(InvocationOnMock invocation) { Object[] args = invocation.getArguments(); Object mock = invocation.getMock(); System.out.println("called with arguments: " + Arrays.toString(args)); return expectedStudentObj; } }); Student student = studentController.getStudentById("S-01"); verify(studentService).getStudentById(anyString()); assertEquals(student.getName(),expectedStudentObj.getName()); }
The do-when Approach
In most cases, do-when works the same way as the when-then approach. But, it has an advantage because it also works with the methods having void type. It follows the semantics: “Do XYZ when ABC happens”.
Different methods used with the do-when approach
Next, we will discuss common scenarios of mocking objects and stubbing methods using the do-when approach.
Stubbing void methods
Suppose we have a method that performs some operations but does not return anything. Hence its return type is void. To test such a method, we would explicitly tell Mockito that this method does nothing by using the following method:
doNothing().when(mock).methodToStub()
It is important to note that there is no option available to stub void methods in the when-then approach. We will illustrate the problem by using an intermediary service that is only responsible for some computations and is not responsible for returning any data. The following snippet demonstrates the usage of the above-mentioned stub method:
An example of a code snippet is shown below:
@Test public void testDataProcessingService() { Mockito.doNothing().when(dataProcessorService).processResults(); List<String> results = new ArrayList<>(); when(dataProcessorService.getResults()).thenReturn(results); List<String> data = dataViewer.getProcessedData(); verify(dataProcessorService).processResults(); assertNotNull(data); }
Stubbing void methods with exceptions
Try-catch blocks are frequently used to ensure we can gracefully handle application or service crashes. Mockito also offers us specific methods to throw exceptions in stubbed method calls for testing purposes. To stub a void method with the exception, use doThrow() method.
The following code snippet demonstrates the usage of the above-mentioned method:
@Test(expected = IllegalArgumentException.class) public void testServiceThrowsException() { doThrow(IllegalArgumentException.class).when(bmiService).calculateBMI(0.0, 0); bmiCalculator.calculateBMI(0.0, 0); verify(bmiService).calculateBMI(anyFloat(),anyInt()); }
Differences Between The Two Approaches
- The when-then approach is more readable and user-friendly,
- Also, the when-then offers return type validation hence encouraging type safety,
- For stubbing void methods, the do-when approach must be used as there is no method support in the when-then.
Conclusion
This blog post covered an introduction to mock objects and stubbing methods. In the later part of the article, various scenarios and examples are discussed using when-then and do-when directives of Mockito to stub method calls.
Discover the power of Mockito in the realm of testing! Explore our in-depth tutorials on the Testing Java Code page, where you’ll learn how to leverage Mockito to create robust test cases, ensuring comprehensive coverage and accurate verification of your Java code.
Happy Learning!