Course – LS – All

Get started with Spring and Spring Boot, through the Learn Spring course:

>> CHECK OUT THE COURSE

1. Overview

In this tutorial, we’ll learn how to mock private fields with Mockito. Mockito is a popular mocking framework often used with JUnit for creating mock objects in Java. It doesn’t inherently support mocking private fields.

However, we can use different approaches to mock private fields with Mockito. Let’s look at a few of them.

2. Project Setup

Let’s start by creating the classes that we’ll use in our examples. We’ll create a class with a private field and a test class to test it.

2.1. Source Classes

Firstly, we’ll create a simple class with a private field:

public class MockService {
    private final Person person = new Person("John Doe");
    
    public String getName() {
        return person.getName();
    }
}

The MockService class has a private field person of type Person. It also has a method getName() that returns the name of the person. As we can see, no setter method exists for the person field. So, we cannot set the field directly or change the value of the field.

Next, we’ll create the Person class:

public class Person {
    private final String name;
    
    public Person(String name) {
        this.name = name;
    }
    
    public String getName() {
        return name;
    }
}

The Person class has a private field name and a getter method for the field.

2.2. Test Class

Next, we’ll create a test class to test the MockService class:

public class MockServiceUnitTest {
    private Person mockedPerson;

    @BeforeEach
    public void setUp(){
        mockedPerson = mock(Person.class);
    }
}

We create an instance of the Person class and mock it using Mockito. In the next sections, we’ll look at ways to use this mocked instance to replace the private field of the MockService class.

3. Enable Mocking With Java Reflection API

One of the ways to set the private field is to use the Java Reflection API. This is a good method because it doesn’t require any additional dependencies. We can first make the field accessible and then set the value of the field to the mocked instance.

Let’s look at the code to do this:

@Test
void givenNameChangedWithReflection_whenGetName_thenReturnName() throws Exception {
    Class<?> mockServiceClass = Class.forName("com.baeldung.mockprivate.MockService");
    MockService mockService = (MockService) mockServiceClass.getDeclaredConstructor().newInstance();
    Field field = mockServiceClass.getDeclaredField("person");
    field.setAccessible(true);
    field.set(mockService, mockedPerson);

    when(mockedPerson.getName()).thenReturn("Jane Doe");

    Assertions.assertEquals("Jane Doe", mockService.getName());
}

We use the Class.forName() method to get the class object of the MockService class. Then, we create an instance of the MockService class using the getDeclaredConstructor() method.

Next, we use the getDeclaredField() method to get the person field of the MockService class. We make the field accessible using the setAccessible() method and set the value of the field to the mocked instance using the set() method.

Finally, we can mock the getName() method of the Person class and test the getName() method of the MockService class to return the mocked value.

4. Enable Mocking With JUnit 5

Similar to Java Reflection API, JUnit 5 also provides utility methods to set private fields. We can use the ReflectionUtils class of JUnit 5 to set a value to the private field:

@Test
void givenNameChangedWithReflectionUtils_whenGetName_thenReturnName() throws Exception {
    MockService mockService = new MockService();
    Field field = ReflectionUtils
      .findFields(MockService.class, f -> f.getName().equals("person"),
        ReflectionUtils.HierarchyTraversalMode.TOP_DOWN)
      .get(0);

    field.setAccessible(true);
    field.set(mockService, mockedPerson);

    when(mockedPerson.getName()).thenReturn("Jane Doe");

    Assertions.assertEquals("Jane Doe", mockService.getName());
}

This method works in the same way as the previous method. The main difference is how we get the field:

  • We use the ReflectionUtils.findFields() method to get the field.
  • It takes the class object of the MockService class and a predicate to find the field. The predicate we use here is to find the field with the name “person”.
  • Additionally, we need to specify the HierarchyTraversalMode. This is important when we have a hierarchy of classes and we want to find the field in the hierarchy.
  • In our case, we have only one class, so we can use any of the TOP_DOWN or BOTTOM_UP modes.

This gives us the field and we can set the field’s value once again and perform the test.

5. Enable Mocking With Spring Test

If we are using Spring for our project, Spring Test provides a utility class ReflectionTestUtils to set private fields.

5.1. Dependency

Let’s start by adding the Spring Test dependency to our project:

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-test</artifactId>
    <version>5.3.25</version>
    <scope>test</scope>
</dependency>

Alternatively, if using Spring Boot, we could use the Spring Boot Starter Test dependency to do the same.

5.2. Test Case

Next, let’s use this class in our test to enable the mocking:

@Test
void givenNameChangedWithReflectionTestUtils_whenGetName_thenReturnName() throws Exception {
    MockService mockService = new MockService();

    ReflectionTestUtils.setField(mockService, "person", mockedPerson);

    when(mockedPerson.getName()).thenReturn("Jane Doe");
    Assertions.assertEquals("Jane Doe", mockService.getName());
}

Here, we use the ReflectionTestUtils.setField() method to set the private field. Internally, this too uses the Java Reflection API to set the field but removes the need for boilerplate code.

6. Conclusion

In this article, we looked at different ways to mock private fields with Mockito. We explored the Java Reflection API, JUnit 5, and Spring Test to mock private fields.

As always, the code for the examples is available over on GitHub.

Course – LS – All

Get started with Spring and Spring Boot, through the Learn Spring course:

>> CHECK OUT THE COURSE
res – REST with Spring (eBook) (everywhere)
Comments are closed on this article!