In order to properly create software we need to write Unit Tests. Most of the time this is pretty straight forward, however, occasionally our software contains private methods that need to be tested. You may try to get around this by just testing the public methods that call these private methods, but that is generally not good enough. Luckily, Java provides an API for handling this situation, Reflection.
Reflection allows the programmer to inspect all properties and methods of a class regardless of if they are public, private, or protected. This is incredibly useful, albeit a tad cumbersome, when writing Unit Tests. Let's setup an example class to try out reflection with:
public class Person { private String firstName; private String lastName; public Person(String firstName, String lastName) { this.firstName = firstName; this.lastName = lastName; } public String getFullName() { return getInternalFullName("Sr."); } private String getInternalFullName(String suffix) { return firstName + " " + lastName + " " + suffix; } }
This class is clearly very simple and there is obviously a better way to get the full name, but for examples sake let's assume that this is the best way to design the Person class. Now, we need to write a test for the getInternalFullName method. This is where reflection comes in:
@Test public void testGetFullName() throws NoSuchMethodException, IllegalAccessException, InvocationTargetException { Person person = new Person(); Method method = person.getClass().getDeclaredMethod("getInternalFullName", String.class); method.setAccessible(true); String fullName = method.invoke(person, "Jr."); // Assert something about the fullName }
This is what is happening in the previous example:
- We are using Reflection to set the local variable "method" to our private Person.getInternalFullName method for the specific "person", we do this by passing the name of our private method and the class that it belongs to to person.getClass().getDeclaredMethod()
- We then set the method's accessibility to true, remember this is just for this specific instance of the Person class.
- Finally, we invoke the method, by passing in the instance of the class and any arguments that the method takes (in the proper order).
For more on this see this Stack Overflow post.
We have seen how to use reflection to access private methods of an instance of a class, but what about static methods? As it turns out, reflection allows us to do that as well. Let's add a static method to our person class:
private static String createSlug(Person person, String delimiter) { return person.getFirstName() + delimiter + person.getLastName(); }
Again, this is not the best use of a static method, but for our purposes it will work. Invoking this private static method with reflection varies only slightly from the non-static method invocation:
@Test public void testCreateSlug() throws NoSuchMethodException, IllegalAccessException, InvocationTargetException { Person person = new Person("Stephen", "Goeddel"); Method method = Person.class.getDeclaredMethod("createSlug", Person.class, String.class); method.setAccessible(true); String slug = method.invoke(null, person, "-"); // Assert something interesting with the slug
There are a few key differences between the static and non-static variants of using reflection to invoke private methods:
- Instead of using an instance of the Person class, we use the class itself to get the declared method.
- We need to pass in a var arg of class to getDeclaredMethod, this is the classes, in order, that the private method takes as parameters.
- When invoking the method we pass in null as the first argument in place of the instance from the previous example.
More information about this technique is available on this Stack Overflow post.
Thanks to Reflection, we are now able to test our private methods without having to muck up our design just to run tests.