Did you know? DZone has great portals for Python, Cloud, NoSQL, and HTML5!

Mike lives in Edmonds, Washington where he enjoys experimenting with new development frameworks and expanding these to the cloud. Mike continually pushes his knowledge utilizing test driven development and different development methodologies. This site is just a duplication/re-syndication of the original blog hosted at http://www.ensor.cc Mike is a DZone MVB and is not an employee of DZone and has posted 6 posts at DZone. View Full User Profile

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

January 27, 2012 AT 12:58 AM
  • submit to reddit

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!

 

From http://www.ensor.cc/2012/01/mocking-with-jodatimes-datetime-and.html

Comments

Steve Brickman replied on Fri, 2012/01/27 - 3:15am

Joda Time might not be the best example in this case because DateTimeUtils.html#setCurrentMillisFixed() can solve the original unit testing problem out of the box.

Eyal Golan replied on Fri, 2012/01/27 - 3:59am

Mike, If you already change the class to be testable, then the better approach would be to constructor inject the supplier. Injection by field is very recommended. (BTW, Guice have Provider, which is exactly as supplier, which is made for providing helpers / services). Steve, using setCurrentMillis looks like workaround for testing. You still do new Date() inside the code, which means untestable.

Christoph Kutzinski replied on Fri, 2012/01/27 - 12:02pm in response to: ziffdavis

Steve, even for JodaTime the approach is better, as DateTimeUtils.html#setCurrentMillisFixed() is static. Therefore you would make other tests possibly dependent on what you set as currentMillisFixed. Having a per-test time lets you decouple tests.

Mike Ensor replied on Wed, 2012/02/22 - 12:01am

Hey Eyal,

Yes, I could have moved the injection to a constructor injector, but I was not focusing on optimizing how the code works, just wanted to show how to externalize a time object so you can have a known time instead of relying on java's date.

If you notice in my example, I'm constructing a new DateTime and filling in the variables to be a specific time, not just a new Date.

Mike!

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.