11.6 Testing Private Methods

11.6.1 Problem

You can't test certain methods because they're private and your test can't get to them.

11.6.2 Solution

Refactor the functionality into standalone classes or make the methods package scope.

11.6.3 Discussion

Suppose you have the following code:

public class Widget {
    public void draw(Graphics g) {
        computeCoordinates(  );
        // paint the component
    private void computeCoordinates(  ) {
        // some complex logic here

The real complexity lies in the computeCoordinates( ) method, but tests cannot call the method directly because it is private. You could write tests against the draw( ) method, but such tests might be slow and would be more complicated than necessary. You might have to wait while the widget paints itself on screen, or you might have to create mock Graphics objects. Making the computeGraphics( ) method package-scope is a simple fix and makes it easy for tests in the same package to call the method. We use this approach in many of our classes and it works well.

Increasing visibility in order to facilitate testing is not without drawbacks. Good designs seek to minimize visibility of fields and methods. By making this method package-scope, any other class in the same package can now call it. This can increase coupling and increase overall application complexity. While we find ourselves using this technique from time to time, it is often an indicator that something needs changing in the design.

Hard-to-test code is a "smell" suggesting that the design can be improved.

Rather than increasing the visibility of methods, investigate refactoring into new classes. These classes encapsulate the desired functionality and are testable on their own. In our previous example, you might create a class called CoordinateCalculator that does the work formerly found in the computeCoordinates( ) method.

Incidentally, standalone helper classes can be reused throughout an application, while private utility methods are only good for a single class. This is an example of how testing and refactoring lead to better designs.

11.6.4 See Also

Recipe 6.8 discusses how to break up large methods into smaller, testable methods.