views:

90

answers:

5

I'm trying to unit test a class with a number of private methods. Each of the private methods can be rather extensive.

I can either make the method package scoped (which causes a warning), or I can use the code below to test it:

Method method = instance.getClass().getDeclaredMethod("methodName");
method.setAccessible(true);
Object object = method.invoke(instance);
assertNotNull(object);

The class is not a "God Object" and most of its methods touch all of its fields.

Any suggestions on how this can be handled better?

A: 

This is probably a stretch if you're not already using it... but Groovy is really great for violating access restrictions for unit testing... you can just reach in and call the methods as though they were public without additional reflection.

danb
A: 

I'm curious what kind of warning you are getting when you package scope methods, but anyway, a way around this kind of issue is to make the test a static inner class of the object. This can have tradeoffs; you may need to exclude the class from being packaged with the code if deployment size is an important issue, and you need to be careful about accidentally introducing dependencies from the testing framework into the production code.

Another potential option is to have a static inner class that helps expose the methods you need, and the test would use it as a pass-through to the private methods. The downside here is that this class essentially exposes the private methods to anyone who wants to use the class, so you have to be careful to clearly express that this class is for testing purposes only.

Yishai
A: 

You may consider using reflection.

cadrian
+9  A: 

Testing private methods may also be a testing-smell.

My reference is the excellent book http://www.manning.com/rainsberger/

  1. You are supposed to test behaviors instead of method : the granularity is a bit different.

    Example 1 : to test a Pile, how do you test push and pop without referencing each other? But testing the global behavior is possible. This reminds us that, even for testing, objects are the right granularity, not methods.

    Example 2 : when you want to test the interaction between several objects, testing method by method is clearly not correct, you want to test a global behavior.

  2. If a method is not public, it cannot be called by the outside world, and it's behaviour is less strictly defined. But more than everything, if you test a private method, you will not be able to refactor your code later. So testing should be done on public code only.

KLE
All of your private methods are either not used and should be removed from your code base or called by a public method (or multiple public methods) and your inputs to the public methods should be designed to test all of the private methods called by the public method.
Thomas Owens
+1 for "testing smell". If you have some many private methods, you should probably split the class into smaller classes with each getting a subset of the private methods. Then (some of) these methods can become public methods.
sleske
+1  A: 

KLE is right.

However, if you're working with legacy code and you have to deal with dependencies in private methods, one last-ditch option is JMockit. I haven't used it, but read about it in The Art of Unit Testing. It's supposed to be able to swap calls from the original class to your fake class.

Use this for breaking dependencies on other objects so you can test the public methods, not for testing private methods. And use it as a safety net on the way towards refactoring to a decoupled design.

TrueWill
+1 nice addition.
KLE