简体   繁体   English

如何使用 JMockit 模拟 Date 类的默认构造函数?

[英]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).我想模拟java.util.date的默认构造函数,因此它不会构造表示创建时间的Date对象,但始终是相同的Date对象(在我的示例中为 2010 年 12 月 31 日)。 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 .我尝试使用JMockitJUnit执行此操作,但是在执行下面的测试时,输出始终为Thu Jan 01 01:00:00 CET 1970 So what is wrong with my mock of Date() ?那么我对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!
        }
    }
}

al nik's answer was a good hint for me. al nik 的回答对我来说是一个很好的提示。 It is better to mock the System class instead of the Date class to generate a fake time.最好模拟System类而不是Date类来生成假时间。 My own solution in the end was simply to mock the System.currentTimeMillis() method (this method is called by Date() internally).最后我自己的解决方案只是模拟System.currentTimeMillis()方法(此方法由Date()内部调用)。

JMockit 1.5 and later JMockit 1.5 及更高版本

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 JMockit 1.4 及更早版本

@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.正如Test Driven书中所建议的,在您的 java 类中使用 SystemTime 抽象是一种很好的做法。 Replace your method calls (System#currentTimeMillis and Calendar#getInstance) and direct construction (new Date()) with static method calls like:将您的方法调用(System#currentTimeMillis 和 Calendar#getInstance)和直接构造(new Date())替换为静态方法调用,例如:

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 类返回的内容。
SystemTime use a TimeSource interface that by default delegates to System.currentTimeMillis() SystemTime 使用默认委托给 System.currentTimeMillis() 的 TimeSource 接口

public interface TimeSource {
    long millis();
}

a configurable SystemTime implementation could be something like this一个可配置的 SystemTime 实现可能是这样的

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 Joda-Time 库简化了 Java 中日期的处理,并为您提供开箱即用的功能

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.您模拟了构造函数,并在内部创建了一个 Date 实例(与构造的实例无关),然后将其扔掉。 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).由于默认构造函数被模拟出来,日期未初始化为当前时间,因此您将获得零时间(代表 1970-01-01)。

To modify the returned date you need to use a magic "it" attribute, like so:要修改返回的日期,您需要使用一个神奇的“it”属性,如下所示:

@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:这是一个基于@asmaier 的精彩回答的完整 JUnit 示例:

    @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 :使用Maven时,在pom.xml配置JMockit如下:

    <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>

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM