Saturday, September 28, 2013

You Don't Need Frameworks As Often As You Think

Mockito is a wonderful tool. It has helped me on a lot of occasion where I had to deal with code that wasn't under my control, yet had to be mocked and/or tested.

Powermock is also a wonderful tool. It builds upon what Mockito (and EasyMock) provide and enables you, among other things, to mock out static method calls. In a pinch, having these tools in your arsenal can be invaluable.

But like any tool, they can be overused. A lot of frameworks are in fact overused, and used where a simpler solution can be achieved without it. This bears some dangers, especially with these testing frameworks.


An Example

How so? Let's consider an example, where we provide a little helper class to deal with configuration, coming from a .properties file. To have some degree in flexibility, we also want to be able to override certain settings using system properties, given at runtime.

Now, we can do this in two ways, as always: test first, or test after. Let's begin with test after, i.e. write the class as we envision it:

Next up, the test. This hits a barrier pretty early: while checking that loading the property file and returning the values from there is easy enough, you now have to mock a value gained by calling a static method... How to do this? There are at multiple ways:
  • you can set the property with System.setProperty() before the test and remove it after the test. This can easily prove very dangerous: if you forget to reset the property, you're in for some interesting debugging of things that happen after this test was run. Depending on the type of thing you mock in this way, you might even change the running system started after this test! So this is clearly not an option.
  • use a mocking framework. Remember that Powermock has this wonderful "mock out static method calls" feature? 


A Powermock Solution

Okay, let's go with Powermock. First, we have to add a couple of dependencies to the pom:


Now, we are ready to have our test use Powermock. We "only" have to change the JUnit test runner, declare which classes are to be pimped by Powermock, replace the System class with a mock, tell that mock to return our mocked value when asked for the key, and we're done. Oh, and don't forget to have Powermock alter your subject under test (SUT). This one confused me for a while - without mentioning the SUT in the @PrepareForTest annotation, your SUT will actually work with the old System.class. While this might be a good safety guard against effects on following tests, it's somewhat counter-intuitive, at least for me. Well, easily remembered, I guess.

This is what the result looks like:


This tests does the trick. It needs roughly 0.6 seconds on my machine. To be fair, this includes initialization, and following tests using Powermock are likely faster in setup. But, I am a firm believer in Infinitest, which means this test will be run whenever I safe a change that relates to Configuration. If that is the case, I am bound for a minimum 0.6 seconds wait. For a unit test, that is almost unacceptably slow. Plus, it adds up. Slows tests tend to not be executed often.


A POJO Solution

There's actually a third option (there's bound to be more, but let's leave it at three): you can use POJOs and write your own mock. Incidentally, this is the solution you arrive at almost every time if you to the test first approach (also called TDD). Because with this, you write the test first, and realize you need a way to mock out the access to to System.properties. How to do this? Easy enough, just wrap it into a little class and have that passed into the subject under test (SUT):


To my eyes, this looks a lot cleaner. It communicates the intent well, the mock is easy to understand and works by hand-made DI (another thing you don't need a framework for as early and often as you might think). It runs, works and is done in roughly 0.022 s. That's a whopping 25 times faster than the Powermock solution! Plus, there is no magic involved here, it's all just plain POJOs. It took me roughly a minute to write, and requires no knowledge of any framework. Some would say the downside is that there now is one more class, but you shouldn't be scared of classes if you're doing Java. Classes are only bad if they serve no purpose.

I would prefer this implementation over the Powermock/test-after one every day, especially since it also gives me a cleaner solution in the context of the Single Responsibility Principle (SRI). I've made the experience that whenever you write code using TDD, this makes you adhere to the SRI more often, and this in turn leads to cleaner code that is much easier to extend, maintain and refactor. Unfortunately, this is something you have to experience to be able to believe it. But once there, people usually wonder how they could live without these techniques at all.

But the biggest thing is: using Powermock like shown above actually hides a problem in your code. It leads to sloppy (well, sloppier) design by making you think there is no problem in using static method calls and having core classes directly depend on system resources and other things not under your control. You pay the price in terms of speed and magic, and tend to not think further. All the while, a clean and simple POJO solution may be available, giving you the benefits of code that is easy to understand, maintain and change.

Don't let frameworks be the answer to every problem. They certainly have their uses, but keep an eye out for an even simpler solution that can be understood without having to be intimate with at least three frameworks. Strive for fast tests, and write tests before the code. This way, you will soon notice your need for frameworks lessen a lot. At the same time, the mind is freed up to apply design and architectural decisions which put frameworks to work in their rightful place: as a tool for the implementation of the design that you decided on, taking away boilerplate code and problems that arise in a pure POJO implementation. Use frameworks when - and only when - they are useful enough to pay the price for using them. Don't let buzzwords and framework-hype deter you from building a good solution.