开发者

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());
    }

    @MockClas开发者_Go百科s(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!
        }
    }
}


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();
    }
}


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


You mocked the constructor, and inside you made an instance of Date (that has nothing to do with the one constructed) and just threw it away. Since the default constructor is mocked out, date is not initialized to the current time, and so you get the time of zero (which represents 1970-01-01).

To modify the returned date you need to use a magic "it" attribute, like so:

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

    public Date it;
    @Mock
    public void $init() {
        // This is sick!
        it.setDate(31);
        it.setYear(110); // 110 = 2010!
        it.setMonth(11); // 11 = December!
    }
}


And here's a complete JUnit example based on @asmaier's great answer:

    @Test
    public void dateConstructorReturnsMockedDate() throws ParseException {
        final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
        final Date mockDate = dateFormat.parse("2002-02-02");

        new MockUp<System>(){
            @Mock
            public long currentTimeMillis() {
                return mockDate.getTime();
            }
        };

        final Date actualDate = new Date();
        assertThat(actualDate).isEqualTo(mockDate); // using AssertJ
    }

When using Maven, configure JMockit as follows in pom.xml:

    <dependencies>
        <dependency>
            <groupId>org.jmockit</groupId>
            <artifactId>jmockit</artifactId>
            <version>${jmockit.version}</version>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <artifactId>maven-surefire-plugin</artifactId>
                <version>${maven-surefire-plugin.version}</version>
                <configuration>
                    <argLine>
                        -javaagent:${settings.localRepository}/org/jmockit/jmockit/${jmockit.version}/jmockit-${jmockit.version}.jar
                    </argLine>
                    <disableXmlReport>true</disableXmlReport>
                </configuration>
            </plugin>
        </plugins>
    </build>
    <properties>
        <jmockit.version>1.44</jmockit.version>
    </properties>
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜