Nov 14, 2013

Testing private methods in Java and Groovy

If you search the internet for the topic testing private methods you will find several opinions if this is good practice or not. Some people even consider it a misconception. I personally think that it is a good idea to encapsulate logic that makes only sense in the context of a single class into a private method. If the code inside of this method exceeds a certain complexity it should be tested.

In this post however I do not want to start a discussion on this topic but show how it can be done easily in Groovy and Java. Let's have a look at the following code:

public class SomeClass {
  private String methodToBeTested(String input) {
    return input.toLowerCase(); 
  } 
}

// inside of a test
SomeClass someClass = new SomeClass();
assert "foo" == someClass.methodToBeTested("Foo");

If you run this code in Groovy you may expect an Error because the test calls a private method of SomeClass. However as of Groovy 2.1.9 this is currently not the case due to a bug in Groovy:
http://jira.codehaus.org/browse/GROOVY-1875
Consequently testing private methods in Groovy currently is really simple: just do it.

However if SomeClass is a class produced by the Java compiler the test will fail with an error like this:

methodToBeTested(java.lang.String) has private access in SomeClass         someClass.methodToBeTested("Foo");

The Groovy developers are working on a fix for the issue that will hopefully make it into Groovy 3. So the Groovy code shown above is eventually not compatible with future Groovy releases.

To overcome this testing problem one can use the Java Reflection API.
The test code must be changed like this:

// inside of a test SomeClass
someClass = new SomeClass();
Method method = SomeClass.class.getDeclaredMethod("methodToBeTested", String.class)
method.setAccessible(true);
assert "foo" == method.invoke(someClass, "Foo");

Note that the reflection methods can throw several Exceptions. If the test is written in Java these must be caught or the test method must be adjusted accordingly.