Test private methods using Junit5

Chanaka Rathnakumara
3 min readApr 9, 2021

Unit testing plays a major role in the software development life cycle. Unit testing helps to write bug-free, quality, and efficient code. When we are using the TDD approach for a particular project unit testing is a must. In java, we normally use Junit framework to write unit test cases and Mockito to mock objects for unit testing.

We can write test cases for an individual code block, if they are public or protected or default. But for private methods?? Can we write unit tests to test private methods? Yes!! we can.

The best way to test a private method is via another public method. But what if we really want to write a unit testing for a private method. We can achieve it by using java reflections. Some say this is a bad idea and a bad practice to test a private method in a class. But I think this helps developers to write bug-free, quality code.

I am using Java 11 for this project. Maven as the build tool and Lombok, Junit and Mokito libraries. This simple application is to calculate employee salary. First, I created my basic classes.

POM file

<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.16</version>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>5.1.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.platform</groupId>
<artifactId>junit-platform-runner</artifactId>
<version>1.5.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>3.4.0</version>
<scope>test</scope>
</dependency>

Employee class

@Data
@NoArgsConstructor
public class Employee {

private String name;

private Integer salary;

private Integer extraIncome;

}

Calculator class

public class Calculator {

private final Integer bonus = 20;

public Integer totalIncome( Employee employee){
return totalIncomePrivateMethod(employee)+bonus;
}

private Integer totalIncomePrivateMethod(Employee employee){
return employee.getSalary()+ employee.getExtraIncome();
}
}

We have 2 methods here. One is private and one is public. We are going to write unit tests for both methods. But before test private methods in the code, we need to add 2 classes to our test folder. These classes basically help us to write unit tests for private methods.

Executor-class

@Slf4j
public final class Executor {

private Executor() {
}

public static <T> T executePrivateMethod(Class<?> aClass, String methodName, Param param) throws Throwable {
Constructor<?> constructor = aClass.getConstructors()[0];
var instance = constructor.newInstance();
return execute(instance, methodName, param, null);
}

public static <T> T executePrivateMethod(Class<?> aClass, String methodName, Param param, Param param2) throws Throwable {
Constructor<?> constructor = aClass.getConstructors()[0];
var instance = constructor.newInstance();
return execute(instance, methodName, param, param2);
}

public static <T> T executePrivateMethod(Object instance, String methodName, Param param) throws Throwable {
return execute(instance, methodName, param, null);
}

public static <T> T executePrivateMethod(Object instance, String methodName, Param param1,
Param param2) throws Throwable {
return execute(instance, methodName, param1, param2);
}

public static <T> T executePrivateMethod(Object instance, String methodName) throws Throwable {
T result = null;

try {
Method method = instance.getClass().getDeclaredMethod(methodName);
method.setAccessible(true);

try {
result = (T) method.invoke(instance);

} catch (IllegalAccessException e) {
log.error(e.getMessage(), e);
} catch (InvocationTargetException e) {
log.error(e.getMessage(), e);
throw e.getTargetException();
}
} catch (NoSuchMethodException e) {
log.error(e.getMessage(), e);
}

return result;
}

private static <T> T execute(Object instance, String methodName, Param param, Param param2) throws Throwable {
T result = null;

try {
Method method = isNull(param2) ? instance.getClass().getDeclaredMethod(methodName, param.getType())
: instance.getClass().getDeclaredMethod(methodName, param.getType(), param2.getType());
method.setAccessible(true);

try {
result = isNull(param2) ? (T) method.invoke(instance, param.getValue())
: (T) method.invoke(instance, param.getValue(), param2.getValue());
} catch (IllegalAccessException e) {
log.error(e.getMessage(), e);
} catch (InvocationTargetException e) {
log.error(e.getMessage(), e);
throw e.getTargetException();
}
} catch (NoSuchMethodException e) {
log.error(e.getMessage(), e);
}

return result;
}

}

Parameter class

@Builder
@Getter
@AllArgsConstructor
public class Param {
private Class<?> type;
private Object value;
}

Executor-class is to execute the private method. Param class to build the parameters. Here returnTotalIncome method used to test the public method. returnTotalIncomeWithoutBonus method used to test the private method.

In executor.executePrivateMethod([Private Method Class / Class instance ], “[Private Method Name]”, [Parameters with Param.Builder()]). We can implment more methods to support multiple parameters in Executor class.

@RunWith(MockitoJUnitRunner.class)
public class CalculatorTest {

private Calculator calculator = spy(Calculator.class);

@Test
public void returnTotalIncome(){
Employee employee = spy(Employee.class);
employee.setSalary(25);
employee.setExtraIncome(5);
int result = calculator.totalIncome(employee);
assertEquals(50, result);
}

@Test
public void returnTotalIncomeWithoutBonus() throws Throwable {
Employee employee = spy(Employee.class);
employee.setSalary(25);
employee.setExtraIncome(5);
var result = Executor.executePrivateMethod(Calculator.class, "totalIncomePrivateMethod", Param.builder().type(Employee.class)
.value(employee).build());

assertEquals(30, result);
}
}

As you can see with this approach we can test our private methods. Try this !!! Happy Coding !!!

--

--

Chanaka Rathnakumara

Tech Enthusiast || Full Stack Developer || Freelancer || Cloud Architect