Wednesday, August 26, 2009

JUnit: How to test your private methods

Some weeks ago I was creating some unit tests for a hobby project I'm working with. But I found myself stuck trying to test some private methods.

My first though was "Should I even need to test those methods?" I invested some time looking for alternatives, but those methods must be private. What could I do?

One option was to modify those methods to be public, anyway this is a hobby project and I know I'm not going to call them from outside. But since the main reason of this project is to learn I though I would best invest the needed time to find the correct solution.

Being a fan of the Java reflection API, I decided to investigate this approach. This is the result:

/**
* Convenient method to execute private methods from other classes.
* @param test Instance of the class we want to test
* @param methodName Name of the method we want to test
* @param params Arguments we want to pass to the method
* @return Object with the result of the executed method
* @throws Exception
*/
private Object invokePrivateMethod (Object test, String methodName, Object params[]) throws Exception {
Object ret = null;

final Method[] methods =
test.getClass().getDeclaredMethods();
for (int i = 0; i < methods.length; ++i) {
if (methods[i].getName().equals(methodName)) {
methods[i].setAccessible(true);
ret = methods[i].invoke(test, params);
break;
}
}

return ret;
}
As you can see the code is simply doing the next steps:
  1. Retrieve an array of declared methods
  2. Loop the array looking for the method we want to test
  3. Once found, set the method as public
  4. Execute the method and return the result
And we want to use it, we only need to do something like:
MyClass instance = new MyClass();
String expResult = "Expected Result";
Object[] params = {"A String Value", "Another Value"};
String result = (String) this.invokePrivateMethod(instance, "myPrivateName", params);
assertEquals(expResult, result);
This is the best I could find. Fair enough but, do you know of a better or more elegant solution?

See you soon.

11 comments:

  1. nice trick! but appart of that, what is the point? From the point of view of good coding, a private method should be only invoked by public methods or the constructor of a class, which means that is accessible for testing by invoking the public method that is going to call them. So, by writting a test for the public method, you are testing the privated ones!

    ReplyDelete
  2. Hi Dani,

    Even when you're right, there's always an exception to the rule ;)

    My class works with files. I have some methods that do some parsing on the filenames to retrieve some info. To have a nice wrapping test I cannot have tons of files, so I use the default class constructor (no arguments). Now I have an empty instance of my class since I didn't use the real constructor.

    The only way I found to solve this situation was to directly access the private methods that are used by my class constructor.

    In this way I can simulate a real environment :)

    ReplyDelete
  3. Hello

    I'd like to suggest to use FEST-Reflect instead of raw Java Reflection. This project provides a fluent interface and a little more type safety. It makes Reflection a lot easier to use :)

    http://fest.easytesting.org/reflect

    Disclaimer: I'm the creator of FEST-Reflect

    Cheers,
    -Alex

    ReplyDelete
  4. Hi Alex,

    Thanks a lot for pointing this project. I didn't knew about it :)

    ReplyDelete
  5. I have to say you are the first and only one who I have ever heard of, who's a fan of the Java reflection API. :)

    ReplyDelete
  6. Hi Casper,

    Is not like I love the way the API is wrote. What I like is the flexibility that can give me when needed.

    The last years I've worked a lot with Tcl, and I must say that the flexibility that I have there is hard to find anywhere else (and really dangerous sometimes) ;)

    ReplyDelete
  7. You can also implement your JUnit-Tests in Groovy which can call private methods like any other method.

    ReplyDelete
  8. Aggggggg TCL! What a crap! ;-)
    I have to say, that the last week I have been working on a project where I am using reflection + groovy compile on runtime and my conclusion is that even if is dangerous, is just an amazing way to define new classes in runtime. So, I would say I love reflection also ;-)

    ReplyDelete
  9. Although I agree that sometimes you need to test your private method, most of the time, it may means that the class is doing more than one thing. There maybe something inside that class that could be extracted out and be made more reusable.

    A cohesive class would be small enough that testing the public methods would indirectly test all the private ones in all its permutations. Then again, that is the ideal case of course.

    You also have to remember that Reflection comes at a cost, namely it makes your test more resistant to refactoring. And that is not just the name changes. At times, the benefits might outweight the cost, using Reflections helps a lot in a lot of cases too (ie. framework building).

    That's just my opinion though. Then again, you are working with your own pet projects, so no need to be too strict for that matter ;-). There will be always time to refactor if needs be.

    ReplyDelete
  10. @Carlos: I think dp4j.com offers a better or more elegant solution, with reflection being injected at compile-time.

    ReplyDelete
  11. Tanks, good setup and very usable for testing private methods

    ReplyDelete