Course – LS – All
announcement - icon

Get started with Spring Boot and with core Spring, 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 examine a few of them.

2. Project Setup

We’ll 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

First, 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 can’t set the field directly, or change the value of the field.

Now 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’re 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
announcement - icon

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

>> CHECK OUT THE COURSE

res – REST with Spring (eBook) (everywhere)
Comments are open for 30 days after publishing a post. For any issues past this date, use the Contact form on the site.