7

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"?

6

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?

  • 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 Feb 7 '18 at 20:38
  • 4
    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. – Maybe_Factor Feb 8 '18 at 6:07
  • 1
    @DavidPacker: Presumably, the OS is already trustworthy. – Robert Harvey Feb 8 '18 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 Feb 8 '18 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. – Robert Harvey Feb 9 '18 at 20:41
3

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.

  • 3
    Your criteria would not be met 99 percent of the time. – Robert Harvey Feb 7 '18 at 20:28
  • 1
    @RobertHarvey Yes, and that's why I use static utility classes sparingly. – Michał Kosmulski Feb 7 '18 at 20:31

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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