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

发布于 2024-10-09 14:35:38 字数 945 浏览 5 评论 0原文

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

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

蝶…霜飞 2024-10-16 14:35:38

al nik 的回答对我来说是一个很好的提示。最好模拟 System 类而不是 Date 类来生成假时间。我自己的解决方案最终只是简单地模拟 System.currentTimeMillis() 方法(该方法由 Date() 内部调用)。

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

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

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();
    }
}
梦年海沫深 2024-10-16 14:35:38

正如测试驱动一书中所建议的,在您的应用程序中使用 SystemTime 抽象是一种很好的做法。 java 类。
将方法调用(System#currentTimeMillis 和 Calendar#getInstance)和直接构造(new Date())替换为静态方法调用,例如:

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

要伪造时间,您只需修改 SystemTime 类返回的内容即可。
SystemTime 使用 TimeSource 接口,默认情况下委托给 System.currentTimeMillis()

public interface TimeSource {
    long millis();
}

一个可配置的 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);
    }
}

并且要伪造返回的时间,您只需执行

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

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

老娘不死你永远是小三 2024-10-16 14:35:38

您模拟了构造函数,并在内部创建了一个 Date 实例(与构造的实例无关),然后将其丢弃。由于默认构造函数是模拟出来的,因此日期不会初始化为当前时间,因此您得到的时间为零(代表 1970-01-01)。

要修改返回的日期,您需要使用神奇的“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!
    }
}

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!
    }
}
奢望 2024-10-16 14:35:38

这是一个基于 @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
    }

当使用 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>

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>
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文