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

mpmp
  • 781
  • 3
  • 8
  • 12

3 Answers3

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?

Robert Harvey
  • 198,589
  • 55
  • 464
  • 673
  • 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](https://en.m.wikipedia.org/wiki/Pure_function), they are unit testables. If they don't they still can be tested, up to a point. – Laiv Feb 07 '18 at 20:38
  • Yep. Forgot about purity (added it to my answer). – Robert Harvey Feb 07 '18 at 21:50
  • 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. – Maybe_Factor Feb 08 '18 at 06:07
  • Agreed. That's what makes serviceLocator so infamous. But while a service is a good candidate for IoD, utilities are not necessarily. The last is implementation details. IMO. – Laiv Feb 08 '18 at 06:28
  • I agree that static does not necessarily mean evil, however I personally disagree the creation of a Date object should be delegated to a static method. IMO a factory should be provided in its place, because dates have a dependency on the underlying system used for date generation. – Andy Feb 08 '18 at 07:51
  • @Maybe_Factor: `[the problem is that] the unit test for the code calling the static method inevitably also tests the static method` -- I don't understand what you meant there. Isn't the whole point to test the static method? – Robert Harvey Feb 08 '18 at 16:13
  • 1
    @DavidPacker: Presumably, the OS is already trustworthy. – Robert Harvey Feb 08 '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 08 '18 at 17:07
  • @DavidPacker: Using Date functions that return unpredictable results within a static method is not what I consider *pure.* Use the Date class method overload that produces the *correct* result. (in .NET, it's the one that returns January 01, 0001, or the many others that produce a predictable result from a set of predefined input values). – Robert Harvey Feb 08 '18 at 17:13
  • @RobertHarvey The main problem I used to face in such situations (as mentioned by @Maybe_Factor) is that you have to make sure you pass data to the SUT that calls the static method so that the static method gets called with data that is required for it to operate properly. Example - I have to make sure that no mocks are passed to the static method - if yes, I have to stub those calls.. If a collection is passed, have to take care of the preconditions that the static method imposes (maybe the collection must be of size x). So all these adds up to the complexity of testing the current SUT. – Thiyagu Feb 09 '18 at 04:12
  • @RobertHarvey I think I know what is going to be the answer but, would you mind to answer the question *when is It too much DI?*. I think It would complete the actual answer. – Laiv Feb 09 '18 at 20:33
  • @Laiv: Most TDD advocates claim that static classes are bad because you can't inject dependencies into them (mocks, stubs, etc.), but that's not really true. The method parameters *are* the dependencies, and calling the method with those parameters is the *purest* form of DI. I know that doesn't answer your question directly, but... – Robert Harvey Feb 09 '18 at 20:36
  • I Agreed. But I guess the OP is wondering why don't replace static methods by single imstances of a class. Regardless testability – Laiv Feb 09 '18 at 20:38
  • 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 09 '18 at 20:41
  • That's It :-). I thougth this could improve your actual answer. Thank you. As usual, very insightful. – Laiv Feb 09 '18 at 20:44
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.

Michał Kosmulski
  • 3,474
  • 19
  • 18
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.