How to Test Private Methods with JUnit

When writing unit tests, the best practice is to test public methods, as they are the entry points to your classes and provide the intended behavior to external consumers. Testing private methods, on the other hand, is often considered an implementation detail and not directly exposed to the external API. In this article, we will discuss why testing private methods is not generally a good option and explore an approach to test them using JUnit 5.

Testing Private Methods with JUnit 5

When writing unit tests, you should focus on testing public methods, as they are the entry points to your classes and provide the intended behavior to external consumers. Testing private methods, on the other hand, is often considered an implementation detail and not directly exposed to the external API. However, there may be situations where you find the need to test private methods. For example, when they contain complex logic that you want to test. In this article, we will discuss why testing private methods is not generally a good option and explore an approach to test them using JUnit 5.

If you are new to JUnit 5 we recommend checking this article: JUnit 5 Made Easy

Why Testing Private Methods is not a good idea

  1. Encapsulation and Abstraction: Private methods are implementation details and are encapsulated within a class for a reason. They are not meant to be directly accessed or tested from external sources. By focusing on testing public methods, you ensure that the class behaves correctly from the perspective of its public interface, promoting better encapsulation and abstraction.
  2. Refactoring Limitations: Testing private methods can lead to fragile tests that break easily during refactoring. Private methods are subject to change without affecting the public API, which means their implementation details can change without breaking any external contracts. When testing private methods, you tightly couple your tests to the implementation details, making them more prone to breakage when refactoring occurs.
  3. Incomplete Test Coverage: By testing only public methods, you ensure that you are testing the class as a whole, considering all the interactions and behaviors that occur through its public interface. Testing private methods in isolation may result in incomplete test coverage, as you may miss important interactions and dependencies between private methods and other parts of the class.
  4. Maintainability and Readability: Focusing on testing public methods enhances the maintainability and readability of your test suite. Tests for public methods serve as documentation and provide clear examples of how tto use the class. On the other hand, testing private methods adds complexity and can make the test suite harder to understand and maintain.

When to Consider Testing Private Methods

Although it is not a best practice to prioritize testing public methods, there may be scenarios where testing private methods becomes necessary or beneficial:

  1. Complex Private Logic: If a private method contains complex logic that is critical to the class’s behavior, testing it directly can provide an additional layer of confidence and help ensure the correctness of the implementation.
  2. Legacy Code and Lack of Testable Public API: In some cases, you may be working with legacy code that lacks a well-designed and testable public API. Testing private methods can be a way to indirectly cover the desired behavior until you refactor the code.

How to Test Private Methods with JUnit 5

While JUnit 5 does not provide built-in support for directly testing private methods, there are techniques and libraries that can help you overcome this limitation. Here is an approach using reflection and JUnit 5’s @Test annotation:

import org.junit.jupiter.api.Test;
import java.lang.reflect.Method;
import static org.junit.jupiter.api.Assertions.assertEquals;

public class PrivateTest {

	@Test
	void testPrivateMethod() throws Exception {
		// Create an instance of MyClass
		MyClass myClass = new MyClass();

		// Obtain the private method using reflection
		Method privateMethod = MyClass.class.getDeclaredMethod("privateMethod", String.class);
		privateMethod.setAccessible(true);

		// Invoke the private method on the instance
		String result = (String)
				privateMethod.invoke(myClass, "john");

		// Assert the result
		assertEquals("Hello john", result);
	}
}

In the example above, we have a class MyClass with a private method privateMethod. To test this private method, we use reflection to obtain a reference to the method and set its accessibility to true to bypass the access restrictions. We then invoke the private method on an instance of MyClass and assert the expected result.

Here is the class:

public class MyClass {

	private String privateMethod(String input) {
		// Private method implementation
		return "Hello " + input;
	}

}

This is the sample project which contains both the Class MyClass and the Test Class:

how to test private methods in java

Steps to test a private method with JUnit

Here are the steps to test a private method using reflection:

  1. Create an instance: Instantiate the class under test, MyClass, or obtain an existing instance.
  2. Obtain the private method: Use the getDeclaredMethod method from the Class class to retrieve the private method. Pass the name of the private method as the first argument and the parameter types (if any) as subsequent arguments. The method is obtained based on its name and parameter types, regardless of its visibility.
  3. Set accessibility: Since the method is private, we need to set its accessibility to true to be able to invoke it. Use the setAccessible(true) method on the Method object to override the access restrictions.
  4. Invoke the private method: Use the invoke method on the Method object to invoke the private method. Pass the instance of the class as the first argument (or null for static methods), followed by any required method parameters. If the private method returns a value, you can capture and store it.
  5. Assert the result: Use JUnit’s assertion methods, such as assertEquals, to compare the result of the private method with the expected value.
junit test private methods

It’s important to note that testing private methods in this way has some drawbacks. As mentioned earlier, it can make your tests more fragile, harder to maintain, and more tightly coupled to the implementation details. Therefore, you should use it sparingly and with caution.

Conclusion

Testing private methods is generally not a best practice in unit testing, as the focus should primarily be on testing the public interface of your classes. By ensuring the correctness of the public methods, you indirectly validate the behavior of the private methods they invoke. However, in certain cases, testing private methods can be necessary or beneficial. By using reflection and techniques like the one demonstrated in this article, you can test private methods when needed. Just remember to consider the drawbacks and be cautious when using this approach.

Remember, the goal of unit testing is to provide confidence in the behavior of your code and promote maintainable and readable tests. Prioritize testing public methods and focus on comprehensive coverage of your classes through their public interfaces.

Source code for this article: https://github.com/fmarchioni/mastertheboss/tree/master/test/junit5-private

Found the article helpful? if so please follow us on Socials