JodaTime and JUnit

JUnit Rules are powerful things. There are all sorts of things you can do with a Rule that makes test code cleaner and less error-prone.

Because a Rule wraps the test method, it’s really useful for things that need both set-up and tear-down to make them work and avoid polluting other tests.

The trouble with the older @Before and @After JUnit approach is that it is very easy to forget to do some tear-down in the @After. If there’s a commonly used pattern in your tests requiring @Before and @After it’s also harder to reuse because you have two separate methods to change.

A Rule is a great way of packaging the code for that commonly used procedure and easily reusing it elsewhere. Here’s an example using JodaTime.

With JodaTime you can easily set a fixed date time for a unit test, like this:

DateTimeUtils.setCurrentMillisFixed(100000);

This sets a fixed time to a number of milliseconds and whenever you use the JodaTime API to create a new DateTime, the fixed time will be used. So now, in your test methods, you make use of that fixed time like this:

DateTime aTimeToUseInThisTest = new DateTime().plusSeconds(10);

Simple, but there are two problems with it. First, you have to remember to call DateTimeUtils.setCurrentMillisSystem() in your tear down method to avoid potentially polluting other test cases, even in other classes. Second, it’s not obvious in that line of code where you create a new DateTime that it will be using a fixed time. The reader has to have noticed the set-up of the fixed time, and remembered it.

We can fix both those problems with a JUnit rule.

Now, instead of set-up and tear-down in your @Before and @After, you simply declare your Rule something like this:

@Rule
public FixedTimeRule fixedTime = new FixedTimeRule(1000000);

The implementation of the Rule handles the set-up and the tear-down so that it is now impossible to forget to reset, and no subsequent tests will ever be polluted. Because it’s one line of code it’s much easier to reuse than adding code to separate @Before and @After methods.

Here’s the bit I like even more. When you want to make use of the fixed time in your test methods, you can do it like this and it will be clear to the reader that you’re using a fixed time:

DateTime aTimeToUseInThisTest = fixedTime.plusSeconds(10);

It’s very easy to read that line and immediately understand that we are making an offset from some fixed time, even if you haven’t already spotted the Rule. The trick is make sure that your Junit Rule implements an appropriate interface so that you can use it as a stand-in for the original class. In this case, we implement the JodaTime ReadableDateTime by delegating to an underlying instance of DateTime, allowing you to change “new DateTime().plusSeconds(10)” into “fixedTime.plusSeconds(10)”. You can quickly implement all the required delegation methods by asking your IDE…

That may be simple, but a good test is one that clearly expresses intent, and clean code is code that you can intuitively understand without having to hunt down distant lines of code that carry significant pre-requisite knowledge.

Here’s one way to implement that Rule, which you should package with other reusable test utility code as per https://cleantestcode.wordpress.com/2014/04/13/reusable-test-utility-code-and-maven/.


public class FixedTimeRule implements ReadableDateTime, TestRule{

    //  We'll delegate all ReadableDateTime methods
    //  to this internal DateTime
    private DateTime fixed;

    //  You could provide constructors to intialise
    //  from DateTime or Calendar or whatever
    public FixedTimeRule(long fixedTimeMillis) {
        this.fixed = new DateTime(fixedTimeMillis);
    }
    
    // The JUnit Rule simply needs to wrap the test method
    // evaluation with calls to set the fixed time and
    // afterwards switch back to system time
    @Override
    public Statement apply(final Statement base, Description d) {
       return new Statement() {
          @Override
          public void evaluate() throws Throwable {
            DateTimeUtils.setCurrentMillisFixed(fixed.getMillis());
            base.evaluate();
            DateTimeUtils.setCurrentMillisSystem();
          }
       };
    }

    // Let the IDE generate the delegation methods so that
    // the Rule satisfies the interface
    @Override
    public int get(DateTimeFieldType type) {
        return fixed.get(type);
    }
    
    //  Loads more delegation methods follow....

}

You can also use a custom annotation on individual test methods to allow some tests to use a fixed time and others to behave normally.

Advertisements
Posted in Clean Test Code, JUnit, JUnit Rule

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: