Using Java's Reflection for Testing
Table of Contents
Writing Boilerplate Test Code is Boring
Repeatedly writing boilerplate for loops for testing is not fun. We can use Java reflection API (java.lang.reflect
package) to maximize code reuse and help testing.
Frameworks like Junit uses reflection for testing. I will introduce a single method today on how reflection can be used for testing.
The code examples below are available at my github repo.
Get an Instance of Class Being Tested
First we will declare a message to print out if all tests passed. Unfortunately you don’t usually get to see this message till the end of debugging.
private final static String SUCCESS = "Congrats! All tests passed.";
Next we will make sure the input array and the expected result array have same number of elements with validateLengths
method which is trivial to implement.
Then we will get a class type Class<?>
from the className
parameter, get the input class type with the first element of the input array inputs[0]
, and get the method being tested with class.getMethod
.
Lastly, we will instantiate an object for the class for the cases where the tested method is an instance method. If the method is a static method, this object will be ignored.
public static <T, R> void assertEqual(String className,
String methodName, T[] inputs, R[] expected) {
// make sure they have same length
validateLengths(inputs, expected);
try {
// use reflection to get type of class indicated by "className"
Class<?> c = Class.forName(className);
Class<T> inputType = (Class<T>) inputs[0].getClass();
Method m = c.getMethod(methodName, inputType);
// static methods will ignore this instance
Object o = c.newInstance();
The Actual Testings
Next we will use for loops to iterate over the array for all test cases. We write these loops here once and then we can call this method from the class being tested.
For nested array, we have to test for deep equality. The testing will quit on the first failed test and print out the information about the input, output, and expected result.
for (int i = 0; i < inputs.length; i++) {
R e = expected[i];
R output = (R) m.invoke(o, inputs[i]);
// has to test deep equal for array type
if (e.getClass().isArray()) {
int n = Array.getLength(e);
for (int j = 0; j < n; j++) {
if (!Array.get(e, j).equals(Array.get(output, j))) {
System.out.println("test failed for "
+ Arrays.asList(inputs[i]) + " expected "
+ Arrays.asList(e) + ", output "
+ Arrays.asList(output));
System.exit(-1);
}
}
} else if (!e.equals(output)) {
System.out.println("test:\n " + inputs[i] + "failed, "
+ " expected " + expected + ", output " + output);
System.exit(-1);
}
}
// all tests passed
System.out.println(SUCCESS);
} catch (ClassNotFoundException | IllegalAccessException
| IllegalArgumentException | InvocationTargetException
| NoSuchMethodException | SecurityException
| InstantiationException e) {
//handle your exceptions here
e.printStackTrace();
}
}
Invoke the Method from the Class Being Tested
To use the test method, call:
assertEqual("my.pkg.className","methodName",inputArray,
expectedResultsArray);
Note
Don’t forget to include the package name in the first parameter as the class name. Please note that Class.getMethod() only searches for public methods.