How to Unit-test private methods


I know it’s kind of wrong to test private methods and there are a lot of articles and books that will explain you why. But sometimes you have an old code base where you have to unit-test some private methods to speed up development time. Today I got such a code base. It’s wasn’t that old, but I needed to change a private method that was called by some other methods. Those other methods had some parameters that needed to be set up depending on the user and the actions that user  took in the application.

....

This was way too much work for this simple method I had to change. I knew from a presentation at AMIS that is was possible to change the method accessor at runtime, but I just didn’t knew how. Time to go to our library. I checked some books and no results. On google I found some big articles (I listed the best one at the sources section, definitely a good thing to read when you want to know more about it)

But enough talking, let me show you what I did after some reading and debugging:


public void testSecretIngredient() throws Exception {
    CoffeeMachine machine = new CoffeeMachine();

    Class machineClass = machine.getClass();
    Method method = machineClass.getDeclaredMethod("getSecretIngredient");
    method.setAccessible(true);
    Object result = method.invoke(machine);
   
    System.out.println(result.getClass());
}

First I created a CoffeeMachine object and invoked the getClass() method on it. When you analyze this object in the debugger you get lots of interesting information:

How to Unit-test private methods private method 01

In the declaredMethods field you can see there are two methods. The getSecretIngredient() and getCoffee() method. The getCoffee methods is testable, but we’re interested in the secret ingredient!
Create a Method object to get the method (it took me some time to grasp that a Method is an Object)
Now the magic method I was looking for: setAccessible. This method makes the getSecretIngredient public so we can invoke it.
Because the compiler doesn’t allow us to do a machine.getSecretIngredient(); (which is now technically possible) we have to invoke the method via the invoke method. You have to pass the object on which you want to invoke the method and you’re ready to go.

Unfortunately I’m now allow to tell you what secret ingredient the AMIS Coffee Machine uses so you have to believe me that the unit test returns the right Object.

Testing with parameters

It’s also possible to pass parameters to the methods. Assume we have a Long and Integer parameter on our getSecretIngredient method.

The getDeclaredMethod method then looks like this:


Method method=machineClass.getDeclaredMethod("getSecretIngredient"new Class[]{Long.class, Integer.class});

and the invoke:


Object result=method.invoke(machine, 10L20);

Conclusion

Fortunately the Reflection API is very slow, maybe Sun did this on purpose. Think of the evil things you can do with this API. You can screw up refactorings and access methods that were hidden on purpose. So promise me you won’t tell this to junior developers, they will use this API in production code and break all the encapsulation you introduced to protect themselves.

When you want to know more about testing private methods I recommend this article.

2 Comments

  1. Tom August 2, 2009
  2. Peter Lawrey September 25, 2007