Mock Java time and date using JMockIt

Example scenario: A class under test (CUT) gets the current date and uses collaborating classes that invoke month dependent rules. How can it be unit tested?

Sure you can get the current date in the test and set the month. But, the CUT and all of its collaborators may also get the current date. Without changing the environment system time how will you force the month to be, for example, December?

That this is a problem may indicate there is a code smell in the design, but in a non test infected group, tests are added, if at all, after the code is shipped. Thus, changes are scheduled for the next agilefall.

One easy approach is to just change what Calendar.getInstance() gives you. With JMockIt this is very easy. Just put something like the anonymous mock (see lising 1) in the test method.

Source code

/** 
  * Test December rules.
  * @author J. Betancourt
  */
@Test
public void should_invoke_December_rules(){
    new MockUp<Calendar>() {
    	@Mock
    	public Calendar getInstance(Invocation inv) {
    		Calendar cal = inv.proceed();
    		cal.set(Calendar.MONTH, 11);
    		return cal;
    	}
    };

    doTestStuffHere();

}

Listing 1.

Note that within the mocked method I still have access to the mocked class instance cause I also included an Invocation as the first argument, thus I invoke that to get the calendar as usual, then I change the month. Kind of like an Aspect ‘around’ advice.

JMockIt performs instrumentation via java.lang.intrument and the ASM library, so even collaborating objects will use this Mocked instance for the life of the test. This is a big feature offered by JMockIt compared to other Mocking frameworks afaik.

This should be applicable to mocking other temporal methods like System.currentTimeMillis(), and that in itself effects other methods to get date and time.

Caution
Mocking JDK classes may have side effects on running tests. In one of my tests, the java.io.Printwriter was mocked. This caused Eclipse to disconnect from the running JUnit test. The solution was: right after executing the method that involved the use of the writer, doing a mock tearDown(). This was on a JMockIt version 1.2. It probably changed in the latest version.

Shouldn’t time be accessed from a testable configurable component?
Getting time related data via Calendar.getInstance(), System.currentTimeMillis(), or other JVM provided facilities is bad practice in some cases. It is similar to using “new “, creating hidden object graphs, and making testing difficult. A better approach is to centralize the time related access through a service or utility class. One benefit of this is that to change the environment date for a ‘system’ test, you don’t have to change the vacuum tubes in the mainframe system, just change the calendar access via configuration.

Notes
1. This worked on JDK 1.6 using JMockIt version 1.2. Is it a good technique? Let me know.
2. After I wrote this I searched for this topic and found many good blog posts discussing this subject. Why didn’t I find these when I initially had the problem?

Further reading

Something I’m listening to while I code …

“Become, Seem, Appear” performed by Oregon on their CD “Oregon In Concert”. On YouTube
Price for a new CD is $5,545.60. I better find my CD and store it in a safe place!

“The Silence Of A Candle” performed by Oregon on their CD “Oregon In Concert”. On YouTube.

Audio CD (November 6, 2001), Original Release Date: 1975, ASIN: B00005RDJS

Similar Posts:

Creative Commons License
This work is licensed under a Creative Commons Attribution-NonCommercial-NoDerivs 3.0 Unported License.

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>