Java – How to deal with static utility classes when designing for testability

javastatic methodstddtesting

We are trying to design our system to be testable and in most parts developed using TDD. Currently we are trying to solve the following problem:

In various places it is necessary for us to use static helper methods like ImageIO and URLEncoder (both standard Java API) and various other libraries that consist mostly of static methods (like the Apache Commons libraries). But it is extremely difficult to test those methods that use such static helper classes.

I have several ideas for solving this problem:

  1. Use a mock framework that can mock static classes (like PowerMock). This may be the simplest solution but somehow feels like giving up.
  2. Create instantiable wrapper classes around all those static utilities so they can be injected into the classes that use them. This sounds like a relatively clean solution but I fear we'll end up creating an awful lot of those wrapper classes.
  3. Extract every call to these static helper classes into a function that can be overridden and test a subclass of the class I actually want to test.

But I keep thinking that this just has to be a problem that many people have to face when doing TDD – so there must already be solutions for this problem.

What is the best strategy to keep classes that use these static helpers testable?

Best Answer

(No "official" sources here, I'm afraid - it's not like there's a specification for how to test well. Just my opinions, which will hopefully be useful.)

When these static methods represent genuine dependencies, create wrappers. So for things like:

  • ImageIO
  • HTTP clients (or anything else network-related)
  • The file system
  • Getting the current time (my favourite example of where dependency injection helps)

... it makes sense to create an interface.

But many of the methods in Apache Commons probably shouldn't be mocked/faked. For example, take a method to join together a list of strings, adding a comma between them. There's no point in mocking these - just let the static call do its normal work. You don't want or need to replace the normal behaviour; you're not dealing with an external resource or something that's hard to work with, it's just data. The result is predictable and you'd never want it to be anything other than what it'll give you anyway.

I suspect that having removed all the static calls which really are convenience methods with predictable, "pure" outcomes (like base64 or URL encoding) rather than entry points into a whole big mess of logical dependencies (like HTTP) you'll find it's entirely practical to do the right thing with the genuine dependencies.