Without entering into a debate of why or why not private methods should be directly tested, here are the various steps involved in testing a private method using Java Reflection.
Scenario 1 : The private method has no input parameters, operates on a private variable and returns a value.
The class to be tested.
package com.john.exeriment;
/**
* The class to be tested.
*
*/
public class App
{
private String name= "John";
private int numCharactersInName = 0;
public int getNumCharactersInName() {
return numCharactersInName;
}
private int getSizeOfName()
{
numCharactersInName = name.length();
return numCharactersInName;
}
public static void main( String[] args )
{
App app = new App();
System.out.println(" The size of the name is " + app.getSizeOfName());
}
}
-------------
JUNIT Class
package com.john.exeriment;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import junit.framework.Test;
import junit.framework.TestCase;
import junit.framework.TestSuite;
/**
* Unit test for simple App.
*/
public class AppTest extends TestCase
{
/**
* Create the test case
*
* @param testName name of the test case
*/
public AppTest( String testName )
{
super( testName );
}
/**
* @return the suite of tests being tested
*/
public static Test suite()
{
return new TestSuite( AppTest.class );
}
/**
* Test method operates on the private field using its default value
**/
public void testAppMethod() throws SecurityException, NoSuchMethodException, IllegalArgumentException, IllegalAccessException, InvocationTargetException, NoSuchFieldException
{
App myApp = new App();
Method m = myApp.getClass().getDeclaredMethod("getSizeOfName", null);
m.setAccessible(true);
Integer size = (Integer) m.invoke(myApp, null);
assertTrue( "Size should be ", size == myApp.getNumCharactersInName());
}
We assume the method getNumCharactersInName() has been tested.
/**
* Test case modifies the private field of the class using Reflections before testing the required private method.
**/
public void testAppField() throws SecurityException, NoSuchFieldException, IllegalArgumentException, IllegalAccessException, NoSuchMethodException, InvocationTargetException
{
App anotherApp = new App();
Field field = anotherApp.getClass().getDeclaredField("name");
field.setAccessible(true);
field.set(anotherApp, "Testing!!!");
Method m = anotherApp.getClass().getDeclaredMethod("getSizeOfName", null);
m.setAccessible(true);
Integer size = (Integer) m.invoke(anotherApp, null);
assertEquals( "Size should be", 10, size, 0.000001 );
}
}
Scenario 2 : The private method takes a String as an input parameter and does not return a value.
We add the following method to our class.
private void getSizeOfName(String name)
{
numCharactersInName = name.length();
}
And to test it, we have the following JUNIT test.
public void testAppMethodWithInputParam() throws SecurityException, NoSuchMethodException, IllegalArgumentException, IllegalAccessException, InvocationTargetException
{
App myApp = new App();
// The parameter type
Class[] parameterTypes = new Class[1];
parameterTypes[0] = String.class;
// The parameter value
Object[] parameters = new Object[1];
parameters[0] = "Cricket";
Method m = myApp.getClass().getDeclaredMethod("getSizeOfName", parameterTypes);
m.setAccessible(true);
m.invoke(myApp, parameters);
assertEquals( "Size is greater than 0", 7, myApp.getNumCharactersInName());
}
In the test, we set up a String variable which we initialise to the value Cricket, and test if the method initialises the numCharactersInField correctly to 7 characters.
Thus, testing private methods via Reflection is a straightforward process and should be used if there are key parts of the business logic embedded in private methods.