Core Java

Mock Nested Method Calls Using Mockito

Mockito is a powerful Java-based framework that simplifies unit testing by allowing developers to mock objects and their behaviors. Nested method calls, often encountered in complex systems, can complicate testing due to multiple dependencies. Let us delve into understanding how Mockito can be used to mock nested method calls for more efficient unit testing.

1. Overview

Nested method calls occur when a method of one object returns another object, and methods are subsequently invoked on the returned object. Consider the following classes:

class Service {
    public Repository getRepository() {
        return new Repository();
    }
}

class Repository {
    public String fetchData() {
        return "Data";
    }
}

Testing code like service.getRepository().fetchData() is challenging without mocking because it involves dependencies across multiple layers. Using Mockito, we can simulate these interactions without relying on actual implementations.

2. Initializing Objects

Before mocking, we need to initialize the objects. Mockito provides two main ways to do this: using annotations or manual instantiation. Below is an example of initialization using annotations:

import org.mockito.Mock;
import org.mockito.MockitoAnnotations;

public class TestClass {
    @Mock
    private Service service;

    @Mock
    private Repository repository;

    @Before
    public void setup() {
        MockitoAnnotations.openMocks(this);
    }
}

Here, the @Mock annotation creates mock instances for the Service and Repository classes. The setup method ensures that these mock objects are activated before each test by calling MockitoAnnotations.openMocks(this).

3. Mocking Objects

Mocking allows us to define the behavior of methods in the objects. To mock nested calls like service.getRepository().fetchData(), we first mock the intermediate method to return a mocked object, and then mock the subsequent method:

import static org.mockito.Mockito.*;
import org.junit.Test;
import static org.junit.Assert.assertEquals;

@Test
public void testNestedCall() {
    when(service.getRepository()).thenReturn(repository);
    when(repository.fetchData()).thenReturn("Mocked Data");

    String result = service.getRepository().fetchData();

    assertEquals("Mocked Data", result);
}

3.1 Code Explanation

Firstly, the test method uses the @Test annotation, indicating that it is a test method. Inside the method, mocking is achieved using the when(...).thenReturn(...) construct provided by the Mockito framework.

The first mocked interaction is when(service.getRepository()).thenReturn(repository);, which specifies that when the getRepository() method of the service object is called, it should return a mocked repository object. The second mocked interaction is when(repository.fetchData()).thenReturn("Mocked Data");, which specifies that when the fetchData() method of the repository object is invoked, it should return the string “Mocked Data.”

In the next step, the test invokes service.getRepository().fetchData() to simulate a nested call. This chain of method calls utilizes the mocked behaviors defined earlier, ensuring that the call to fetchData() on the mocked repository object returns “Mocked Data.”

Finally, the test uses the assertion assertEquals("Mocked Data", result); to validate that the result of the nested method call is “Mocked Data.” If the value returned does not match the expected value, the test will fail.

4. Using Deep Stubs

Mockito provides a convenient feature called deep stubs, which automatically creates mock objects for methods returning other objects. This simplifies mocking for nested calls:

@Mock(answer = Answers.RETURNS_DEEP_STUBS)
private Service service;

@Test
public void testDeepStub() {
    when(service.getRepository().fetchData()).thenReturn("Deep Stubbed Data");

    String result = service.getRepository().fetchData();

    assertEquals("Deep Stubbed Data", result);
}

4.1 Code Explanation

The first line @Mock(answer = Answers.RETURNS_DEEP_STUBS) is an annotation used to create a mock object for the Service class. The answer = Answers.RETURNS_DEEP_STUBS part specifies that Mockito should automatically return mock objects for any method calls on nested objects, allowing deep stubbing. This means that if a method of an object within the service object is called, it will return another mock object, and the process can continue recursively.

The second part of the code is the test method testDeepStub. In this method, the test uses when(service.getRepository().fetchData()).thenReturn("Deep Stubbed Data"); to define a mock behavior. This line specifies that when the getRepository() method of the service object is called, it will return a mock repository object, and subsequently, when the fetchData() method is invoked on this repository, it will return the string “Deep Stubbed Data.”

The next line, String result = service.getRepository().fetchData();, calls the getRepository() and fetchData() methods on the service object. Due to the deep stubbing, this will return the mocked value “Deep Stubbed Data” from the fetchData() method of the repository object.

Finally, the assertEquals("Deep Stubbed Data", result); line asserts that the result returned from the deep-stubbed call is equal to the expected value “Deep Stubbed Data.” If the returned value matches the expected value, the test passes; otherwise, it fails.

4.2 Code Output

The code gives the following output:

Deep Stubbed Data

5. Conclusion

Mocking nested method calls using Mockito provides a powerful way to isolate and test components in a layered architecture. By combining manual stubbing and deep stubs, you can effectively handle complex scenarios with minimal effort. While deep stubs simplify mocking, always review your code design to avoid over-reliance on mocks, as it may indicate a need for refactoring.

Yatin Batra

An experience full-stack engineer well versed with Core Java, Spring/Springboot, MVC, Security, AOP, Frontend (Angular & React), and cloud technologies (such as AWS, GCP, Jenkins, Docker, K8).
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

0 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
Back to top button