January 17, 2012

Mocking with JodaTime's DateTime and Google Guava's Supplier

 

Introduction

If you're a seasoned unit tester, you've learned to take note when you see any code working with time, concurrency, random, persistence and disc I/O.

The reason for this is that tests can be very brittle and sometimes down-right impossible to test properly. This post will show how to abstract out "time" by injecting a replacement for it in the consumer.  This post will be using Spring 3 as the Dependency Injection container, though Guice, other DI containers or constructor/setters on POJOs would work as well.  I will also ignore Locales since the focus is on the injection of the DateTime, not DateTime itself.

Existing code

You've been handed a piece of code to unit test (or you are creating one and this is your first stab at it).  Our first piece of code, only one class: (This class is a Spring 3.1 controller and the purpose is to return back the current time as a String)

@Controller
@RequestMapping(value = "/time")
@VisibleForTesting
class TimeController {

    @RequestMapping(value = "/current", method = RequestMethod.GET)
    @ResponseBody
    public String showCurrentTime() {
        // BAD!!! Can't test
        DateTime dateTime = new DateTime();
        return DateTimeFormat.forPattern("hh:mm").print(dateTime);
    }
}

Take note that the class does a "new DateTime()" in the class.  Here is the corresponding test class:

What happens when we run the test?  How about assuming we have a very slow machine. You could (and most likely will) end up with your comparison DateTime to be different than the returned DateTime. This is a problem!

First thing to do is to remove the dependency, but how are we going to do this?  If we make the DateTime a field on the class, we will still have the same problem.  Introduce Google Guava's Supplier interface.


Google Guava Supplier

The Supplier interface only has one method, "get()" which will return an instance of whatever the supplier is setup for.  An example, the supplier will return a user's first name if they have logged in, and a default one if they have not:

public class FirstNameSupplier implements Supplier<String> {

    private String value;
    private static final String DEFAULT_NAME = "GUEST";

    public FirstNameSupplier() {
        // Just believe that this goes and gets a User from somewhere
        String firstName = UserUtilities.getUser().getFirstName();
        // more Guava
        if(isNullOrEmpty(firstName)) {
            value = DEFAULT_NAME;
        } else {
            value = firstName;
        }
    }

    @Override
    public String get() {
        return value;
    }
}

To your implementing method, you don't care what the first name is, only that you get one.


Refactoring out DateTime

Let's move on. For a much more real example of using a Supplier (and the point of this post) let's implement a DateTime supplier to give us back the current DateTime. While we're at it, let's also create an interface so that we can create mock implementations for testing:

public interface DateTimeSupplier extends Supplier<DateTime> {
    DateTime get();
}

and an implementation:

public class DateTimeUTCSupplier implements DateTimeSupplier {
    @Override
    public DateTime get() {
        return new DateTime(DateTimeZone.UTC);
    }
}

Now we can take the DateTimeUTCSupplier and inject that into our code that needs the current DateTime as the DateTimeSupplier interface:

@Controller
@RequestMapping(value = "/time")
@VisibleForTesting
class TimeController {

    @Autowired
    @VisibleForTesting
    // Injected DateTimeSupplier
    DateTimeSupplier dateTime;

    @RequestMapping(value = "/current", method = RequestMethod.GET)
    @ResponseBody
    public String showCurrentTime() {
        return DateTimeFormat.forPattern("hh:mm").print(dateTime.get());
    }
}

In order to test this, we'll need to create a MockDateTimeSupplier and have a controller to pass in the specific instance we want to return:

public class MockDateTimeSupplier implements DateTimeSupplier {

    private final DateTime mockedDateTime;

    public MockDateTimeSupplier(DateTime mockedDateTime) {
        this.mockedDateTime = mockedDateTime;
    }

    @Override
    public DateTime get() {
        return mockedDateTime;
    }
}

Notice that the object being saved is passed in via the constructor. This will not get you the current date/time back, but will return back the specific instance you want

and finally our test that exercises (slightly) the TimeController we implemented above:

public class TimeControllerTest {

    private final int HOUR_OF_DAY = 12;
    private final int MINUTE_OF_DAY = 30;

    @Test
    public void testShowCurrentTime() throws Exception {
        TimeController controller = new TimeController();
        // Create the mock DateTimeSupplier with our known DateTime
        controller.dateTime = new MockDateTimeSupplier(new DateTime(2012, 1, 1, HOUR_OF_DAY, MINUTE_OF_DAY, 0, 0));

        // Call our method
        String dateTimeString = controller.showCurrentTime();

        // Using hamcrest for easier to read assertions and condition matchers
        assertThat(dateTimeString, is(String.format("%d:%d", HOUR_OF_DAY, MINUTE_OF_DAY)));
    }

}

Conclusion

This post has shown how to use Google Guava's Supplier interface to abstract out a DateTime object so you can better design your implementations with unit testing in mind! Suppliers are a great way to solve the "just give me something", mind you it's a known type of something.


Good luck!

8 comments:

eneveu said...
This comment has been removed by the author.
eneveu said...

[trying again with proper HTML escaping]

Cool post.

Couldn't you avoid creating a DateTimeSupplier interface, and instead inject a Supplier directly?

@Autowired
@VisibleForTesting
Supplier<DateTime> dateTime;

In which case you wouldn't need the MockDateTimeSupplier class, and could instead do:

controller.dateTime = Suppliers.ofInstance(new DateTime(2012, 1, 1, HOUR_OF_DAY, MINUTE_OF_DAY, 0, 0));

Or would Spring's autowiring fail because it would have to inject a "generified bean" (Supplier), and the type info would be gone at runtime?

Anonymous said...

So you just described the factory pattern... no big deal

without having read the javadoc... is there more to the Supplier Interface than just being a predefined Factory interface?

Mike! said...

Hey Anonymous,

You're right, the Guava Supplier is a typed factory at it's core, but the main functionality comes from the ability to guarantee an result/object, pass and transform input and return Functions to be used within an algorithm.

This blog post is not specifically about Supplier interface, the blog post is about a possible solution to dealing with Dates and how to test them.

You've identified the pattern used here so probably know, almost all solutions involve at least one pattern from the original Gang of Four book. The point of this blog is not to show off brand new code for every solution, it is to share solutions to problems common to all developers and often times I find the simplest solution is to use existing open source software. I chose to use a Supplier instead of creating my own Factory pattern comes down to the "Don't repeat yourself" principal that every developer should know and love. Why re-write something that someone else has written and has been tested by tens of thousands of developers world wide?

I would suggest you read the JavaDocs on Supplier and while you're at it, many of the other amazing library additions to the Guava project (I use ImmutableList/ImmutableMap, Multimaps, Iterators, Strings, @VisibleForTesting, and many more)

Posting a comment with flame-tones in it does not help the community share information, nor does it foster an "ask anything, there are no stupid questions" attitude that I have tried to build. In the future, if you would like to flame a posting, please visit Facebook, Reddit or Twitter

Mike!

Anonymous said...

Jodatime provides methods to "stop" the time in the class. http://joda-time.sourceforge.n.... Constructors use the methods in this class to initialize their fields. This can be useful for unit test without modifying the original code. Method to use setCurrentMillisFixed.

Anonymous said...

Sorry I forget name of class DateTimeUtils and link http://joda-time.sourceforge.net/apidocs/org/joda/time/DateTimeUtils.html

sorin cristea said...

Hi Mike,
Thanks for your post and explications. I don't understand exactly which is the advantage of using Supplier interface? From my point of view the only advantage is that we can use the injection directly, we are forced to implement the get() method, if there are other advantage please light me :). Thanks.

Mike! said...

I'll try to address these separately. First, setCurrentMillisFixed with the JodaTime constructors for dates is a solution, and while it is tread safe, it isn't parallelizable for tests. If you run tests concurrently and modify the system date in one test, you can't guarantee that the test will use the system date for that specific test case. This is why I recommend creating an abstraction for dates when creating calculations. Another possible strategy would be to pass around Intervals (I'm considering writing a post on that).

@sorin cristea, the Supplier interface is a very generic factory pattern (or some would like to call it a no-argument function). The advantages would be if you wanted to maybe look up a date from a database, if it isn't a suitable date, then return a default. The possibilities are pretty much endless on what you can use the Supplier for, I tend to think of it as (from the class level) "I don't really care where/how I will be getting this, but I can expect to get a workable object"

Mike!