Using Java's Reflection for Testing

Sun, Jun 19, 2016
Category: programming language Tags: [Java]

Writing Boilerplate Test Code is Boring

Java Reflection

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 repository.

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();
    // code continue in next block for actual testing
  }
}

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.

{ //continue from top
  {
    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:

TestClass.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.

comments powered by Disqus