Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to mock the default constructor of the Date class with JMockit?

I want to mock the default constructor of java.util.date so it does not construct a Date object representing the time when it was created, but always the same Date object (in my example below 31 Dec 2010). I tried doing this with JMockit and JUnit, but when executing my test below, the output is always Thu Jan 01 01:00:00 CET 1970. So what is wrong with my mock of Date()?

import java.util.Date;

import org.junit.*;
import mockit.*;

public class AppTest {

    @Before
    public void setUp() {
        Mockit.setUpMocks(MockedDate.class);
    }

    @After
    public void tearDown() {
        Mockit.tearDownMocks();
    }  

   @Test
    public void testDate() {
        Date today=new Date();
        System.out.println(today.toString());
    }

    @MockClass(realClass=Date.class)
    public static class MockedDate {

        @Mock
        public void $init() {
            // Now should be always 31.12.2010!
            new Date(110,11,31);  //110 = 2010! 11 = December! This is sick!
        }
    }
}
like image 319
asmaier Avatar asked Dec 01 '22 09:12

asmaier


2 Answers

al nik's answer was a good hint for me. It is better to mock the System class instead of the Date class to generate a fake time. My own solution in the end was simply to mock the System.currentTimeMillis() method (this method is called by Date() internally).

JMockit 1.5 and later

new MockUp<System>(){

    @Mock
    public long currentTimeMillis() {

        // Now is always 11/11/2011
        Date fake = new Date(111,10,11);
        return fake.getTime();
    }
};

JMockit 1.4 and earlier

@MockClass(realClass = System.class)
public static class MockedSystem {

    @Mock
    public long currentTimeMillis() {

        // Now is always 11/11/2011
        Date fake = new Date(111,10,11);
        return fake.getTime();
    }
}
like image 153
asmaier Avatar answered Dec 10 '22 11:12

asmaier


As suggested in the Test Driven book it's good practice to use a SystemTime abstraction in your java classes. Replace your method calls (System#currentTimeMillis and Calendar#getInstance) and direct construction (new Date()) with static method calls like:

long time = SystemTime.asMillis();
Calendar calendar = SystemTime.asCalendar();
Date date = SystemTime.asDate();

To fake the time you just need to modify what's returned by your SystemTime class.
SystemTime use a TimeSource interface that by default delegates to System.currentTimeMillis()

public interface TimeSource {
    long millis();
}

a configurable SystemTime implementation could be something like this

public class SystemTime {
    private static final TimeSource defaultSrc =
            new TimeSource() {
                public long millis() {
                    return System.currentTimeMillis();
                }
            };

    private static TimeSource source = null;
    public static long asMillis() {
        return getTimeSource().millis();
    }

    public static Date asDate() {
        return new Date(asMillis());
    }
    public static void reset() {
        setTimeSource(null);
    }
    public static void setTimeSource(TimeSource source) {
        SystemTime.source = source;
    }
    private static TimeSource getTimeSource() {
        return (source != null ? source : defaultSrc);
    }
}

and to fake the returned time you simply do

@Test
public void clockReturnsFakedTimeInMilliseconds() throws Exception {
    final long fakeTime = 123456790L;
    SystemTime.setTimeSource(new TimeSource() {
        public long millis() {
                return fakeTime;
        }
    });
    long clock = SystemTime.asMillis();
    assertEquals("Should return fake time", fakeTime, clock);
}

Joda-Time library simplifies working with dates in Java and offers you something like this out of the box

like image 33
mickthompson Avatar answered Dec 10 '22 12:12

mickthompson