16

I'm torn between using DI and static utility classes on my code.

I currently created a DateUtil static class that is only meant to be accessed statically. This class is responsible for just creating and manipulating dates using a variety of libraries with absolutely no external 3rd party dependency.

Everything looks fine but now unit testing becomes messier since now we have to use PowerMockito to properly stub the behavior of the static class.

On the contrary, if I removed the static-ness of DateUtil and converted it into a Spring @Component, then now unit tests can be a tad bit easier, however, now we have all these dependencies everywhere and I'm not sure that this is the purpose of Spring managed beans.

How should I proceed with this? Is it okay to using Spring beans everywhere and just forget about static code entirely? Is it okay to abuse Spring's DI capabilities? When is it "too much DI"?

0

3 Answers 3

11

The prevailing assumption that static methods are not unit-testable seems to have arisen from developers being accustomed to writing unit tests against instantiable class instances, and all of the things that come with that like mocks, stubs, interfaces, dependency injection, etc. You can't do any of that with static classes. However ...

Static methods operating on (more or less) primitive types like Date don't require any of that. Assuming that your static methods are pure (i.e. contain no side-effects and avoid static state), you can test them by merely calling them with a test value and asserting the correct return result.

That's it. Doing anything else is going to drown your fellow programmers in an unnecessary sea of complexity for no additional benefit.

Non-static classes have a very specific purpose. If you need that purpose, by all means use one. But if you don't need it, why use it if your only rationale is that you can't apply your usual class-testing techniques? The remedy to that is to learn a more suitable testing technique, not shoehorn your method under test into a class just because you think you need a container to test it in.

Further Reading
Is static universally “evil” for unit testing and if so why does Resharper recommend it?

15
  • 1
    In addition to this answer. Take a look at Apache Commons. Many of their libs are Utils (mostly). And a lot of these Utils have static methods. The codebase can be checked out, compiled and unit tested. Start with Common-io, common-collections, etc. If methods are pure, they are unit testables. If they don't they still can be tested, up to a point.
    – Laiv
    Commented Feb 7, 2018 at 20:38
  • 5
    The problem with static methods and unit testing isn't that the static methods are harder to unit test, it's that the unit test for the code calling the static method inevitably also tests the static method. For those pure, simple static methods (e.g. methods in java.lang.Math), it makes sense that these are static, but for anything more complicated, I'd make it an proper object so it can be easily mocked for unit testing of calling code. Commented Feb 8, 2018 at 6:07
  • 1
    @DavidPacker: Presumably, the OS is already trustworthy. Commented Feb 8, 2018 at 16:25
  • 1
    So are database engines, in most cases, Robert. And you don't want to have a static dependency on them either. The problem I have with dates is that calling a new Date with no arguments will usually yield two different values. Which is unpredictable and makes unit testing more difficult.
    – Andy
    Commented Feb 8, 2018 at 17:07
  • 1
    @Laiv: Classes have a very specific purpose. If you need that purpose, by all means use a class. But if you don't need it, why use it if your only rationale is that you can't apply your usual class-testing techniques? The remedy to that is to learn a more suitable testing technique, not shoehorn your method into a class just because you think you need a container to test it in. Commented Feb 9, 2018 at 20:41
6

Here is my rule of thumb. Use static utility classes if the methods are just 1-2 lines and perform operations which are easy to name with just a word or two, and which are very unlikely to ever change. So, for example Math.max(double, double) makes a great static utility method as does MoreObjects.firstNonNull(Object...).

Since the definition of what these methods do is very unlikely to change, it is also OK to take their behavior into account when testing other classes which make use of these methods. If you test your method which uses Math.max(), you do not try to stub the behavior of Math.max(), do you? One could argue that this way your test is not really a unit test, but who cares. If your method uses some method of String class, do you try to stub that behavior? It wouldn't make any sense.

If the criteria above are not met, I prefer normal DI. So, in the case of dates, if the operations are something like firstMondayAfter(Date), a static utility class should be completely fine. If it were more like getStartOfMyCompanyFiscalYear(Date), I'd go with DI.

2
  • 4
    Your criteria would not be met 99 percent of the time. Commented Feb 7, 2018 at 20:28
  • 4
    @RobertHarvey Yes, and that's why I use static utility classes sparingly. Commented Feb 7, 2018 at 20:31
0

Which option seems more easily testable should never be the main reason for a code design decision but the code design should be evaluated on its own. The code is not written to serve the tests but the future user. Still, it can sometimes help identify a code smell, if something seems difficult to test.

Utility classes are well testable with PowerMockito under the condition that they were written robustly with an understanding for pure functions. Pure functions are functions with no side-effects. That means, the function will always return the same for a specific input of parameters - i.e. it does not depend on other global variables, which could change, or on the time of it's call.

Pro utility classes:

  • They are pure Java. That means they can be reused later in other projects - depending on no more but Java.

Pro Spring beans (@Component, @Service...):

  • They provide more flexibility when the excpected behaviour is varying between specific cases. You can have an interface bean and several implementations of it with the @Qualifier that identifies the specific case. Also, the behaviour/s that is/are expected by the users/customers may change anytime in the future. Then it will be easier to react to that with Spring beans - easier to maintain.

Conclusion: Utility classes are a good choice for helper functions that are not project specific and that will (for that reason) probably never change. They are more of a basic functionality that you would like to see in a basic library to reuse at any time. So by using a utility class you can easily generate that basic library or copy the class into other Java-based projects.

Spring beans are a good choice for project specific functionality that might vary or need to change due to new project specific requirements.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.