Testing Private Methods in Java
Posted: August 19th, 2011 | Author: simeshev | Filed under: GeneralJava 5 provides vargargs API that allows to test private methods in a very concise way. Here is how.
Yesterday, during a meetup at Loopt, a question of an API for testing private methods was brought up. I used to use JUnit add-on PrivateAccessor [1] when I was testing private methods. The problem was that applying PrivateAccessor produced prohibitively verbose code, so you had to think really hard to understand what the test was doing.
invoke() with varargs
Luckily, Java 5 provides vargargs API that allows to write really concise code for invoking private methods and return results. Here is the invoke() method that makes use of vargargs. Feel free to use it in your code:
/**
* Invokes a private method on an object.
*
* @param obj the object on what to invoke the method.
* @param methodName the name of the method to invoke.
* @param parameters a list of parameter
* @return the result of dispatching the method represented by the object.
*/
public static <T> T invoke(Object obj, String methodName, Object... parameters) {
try {
// Create a list of parameter classes
final Class[] parameterTypes = new Class[parameters.length];
for (int i = 0; i < parameters.length; i++) {
parameterTypes[i] = parameters[i].getClass();
}
// Get method
Class> clazz = obj.getClass();
Method method = clazz.getDeclaredMethod(methodName, parameterTypes);
method.setAccessible(true);
// Call method
return (T)method.invoke(obj, parameters);
} catch (RuntimeException e) {
throw e;
} catch (Exception e) {
throw new IllegalArgumentException(e);
}
}
Complete Example
Below is a complete JUnit tests that tests StringBuilder’s private method append(). Notice how neatly the invocation of append() fits into a short single line:
import java.lang.reflect.Method;
import junit.framework.TestCase;
public final class StringBuilderTest extends TestCase {
/**
* Object under test
*/
private StringBuilder object;
/**
* Tests String's private method 'append(StringBuilder sb)'.
*/
public void testAppend() {
StringBuilder toAppend = new StringBuilder("Test");
StringBuilder result = invoke(object, "append", toAppend);
assertEquals("Test", result.toString());
}
/**
* Invokes a private method on an object.
*
* @param obj the object on what to invoke the method.
* @param methodName the name of the method to invoke.
* @param parameters a list of parameter
* @return the result of dispatching the method represented by the object.
*/
public static <T> T invoke(Object obj, String methodName, Object... parameters) {
try {
// Create a list of parameter classes
final Class[] parameterTypes = new Class[parameters.length];
for (int i = 0; i < parameters.length; i++) {
parameterTypes[i] = parameters[i].getClass();
}
// Get method
Class> clazz = obj.getClass();
Method method = clazz.getDeclaredMethod(methodName, parameterTypes);
method.setAccessible(true);
// Call method
return (T)method.invoke(obj, parameters);
} catch (RuntimeException e) {
throw e;
} catch (Exception e) {
throw new IllegalArgumentException(e);
}
}
public void setUp() throws Exception {
super.setUp();
object = new StringBuilder(10);
}
}
Making invoke() reusable
To make invoke() reusable, I suggest to move it to a separate utility class or at least to create a super class for all your tests that extends junit.TestCase and move invoke() there.
Regards,
Slava Imeshev
Cacheonix: Reliable Distributed Java Cache
References
1. JUnit Add-On PrivateAccessor
(8) Comments: Post a response

Do not test private methods directly. Unit testing is the test on the class from a user of the class’ point of view. This way you can refactor and throw away/create private methods without being afraid of outside code directly using them.
Steven,
I agree. The need for testing a private method cries for making a part of the public API. Though, in this case I someone asked me for help and I though it was worth sharing a solution.
Slava Imeshev
Cacheonix: Distributed Java Cache
Very original! I’ll be sure to add your invoke() method to my ReflectionUtils class for testing private methods. Thanks!
The best way to test private method is through the public method. If the private method is not invoked when a specific public method is invoked, then you probably do not want the private method in the first place.
Would there be a use case where a private method is not invoked by a public method??
‘Would there be a use case where a private method is not invoked by a public method??’
but this is usefull when you follow the Unitty principe (test only one class, all others are mocks)
it is usefull when you have a package access method which is called only by other package classes.
Hi Slava,
That’s a nice tool to have in the developer’s toolbox. I also created similar testing utilities in the Spring Framework in the ReflectionTestUtils class (dating back to 2007).
In fact, based on a user’s suggestion in Spring’s JIRA instance and your blog post, I just introduced a new, generic invokeMethod(Object, String, Object…) method in Spring’s ReflectionTestUtils.
While testing my own implementation, I discovered that there is a small deficiency in your implementation: specifically it does not support automatic boxing and unboxing from primitives to wrapper types. Add the following methods to your test case to see what I mean:
protected Integer add(Integer a, Integer b) {
return a + b;
}
protected int subtract(int a, int b) {
return a – b;
}
public void testAutoboxing() {
// Passes
int sum = invoke(this, “add”, 1, 2);
assertEquals(“add(1,2)”, 3, sum);
// Fails
int difference = invoke(this, “subtract”, 5, 3);
assertEquals(“subtract(5,3)”, 2, difference);
}
Luckily for me, I was able to delegate to Spring’s MethodInvoker class that takes care of finding a “matching” method by taking into account that a declared argument may actually be a primitive that “matches” the wrapper type returned by introspection.
Hopefully this tip will help you in case you come across this issue in your own tests.
Regards,
Sam Brannen (author of the Spring TestContext Framework)
Sam,
Though this code example may certainly benefit from further development, my goal was to demonstrate some benefits of using vargargs together with the reflection API. I am glad to hear that my post inspired you to add invokeMethod() to your API.
Regards,
Slava Imeshev
Cacheonix: Distributed Java Cache
dp4j offers an alternative solution. You use annotations instead of the Reflection API*.
* actually dp4j will replace your annotations with the required reflection API code at compile-time.