Coerced Access via Reflection

Java’s reflection features distinguish it from most other strongly typed, compiled languages. Java reflection allows deep introspection and manipulation of types at runtime. Used judiciously, this can lead to highly capable generic code in production systems, although care must be taken to avoid the inherent performance penalty. You can also commit great evils with the power of reflection.5 However, let’s use it for good: to increase our ability to test.

5. For some great examples of the evils that can be committed, see http://stackoverflow.com/questions/2481862/how-to-limit-setaccessible-to-only-legitimate-uses. My favorite part is making Strings mutable.

Consider a simple class with only a single private method, as shown in Listing 9-11. In itself, this class is useless because the method cannot be invoked, but it characterizes the problem of testing private methods quite well. Regardless of whether it is private, a sufficiently complex or reused method deserves its own tests. Procedural decomposition encapsulates areas of logical functionality that can be independently testable, regardless of whether the functionality should be exposed outside of the class. By that line of reasoning, private methods are imminently testable.

Listing 9-11: A simple class with a single private method

package com.vance.qualitycode;
public class ReflectionCoercion {
  private int hardToTest() {
    return 42;
  }
}

How can we test this method? In addition to type introspection and invocation, Java reflection allows you to manipulate the accessibility of an element as well, through a simple yet powerful method on the AccessibleObject class called setAccessible(). Subject to a SecurityManager check and some other minor restrictions, an argument of true makes the method accessible. In other words, a couple of simple reflection statements allow you to invoke the private methods of a class (Listing 9-12).

Listing 9-12: Coercing test access to private methods with reflection

public class ReflectionCoercionTest {
  @Test
  public void testHardToTest()
      throws NoSuchMethodException,
             InvocationTargetException,
             IllegalAccessException {
    ReflectionCoercion sut = new ReflectionCoercion();
    Method targetMethod =
      sut.getClass().getDeclaredMethod("hardToTest");
    targetMethod.setAccessible(true);

    Object result = targetMethod.invoke(sut);

    Assert.assertTrue(result instanceof Integer);
    Assert.assertEquals(42, result);
  }
}

First, we instantiate our class under test. Next, we use reflection to introspect on the method we wish to test. In a more complicated scenario, we may have to differentiate between different overloads of the same method name, but that is just a more sophisticated use of reflection. Ignoring the access level of the method is as simple as calling setAccessible().6

6. This can be restricted with a SecurityManager, but that should be easy to keep out of your tests.

This code would be awkward and repetitive if you needed to write it every time you tested a private method. Fortunately, several Java tools provide this feature in various forms, including dp4j,7 PowerMock,8 and Privateer.9

7. http://code.google.com/p/dp4j/

8. http://code.google.com/p/powermock/

9. http://java.net/projects/privateer/

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.22.71.106