【问题标题】:How to mock the default constructor of the Date class with JMockit?如何使用 JMockit 模拟 Date 类的默认构造函数?
【发布时间】:2011-06-01 14:31:26
【问题描述】:

我想模拟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!
        }
    }
}

【问题讨论】:

    标签: java default-constructor jmockit


    【解决方案1】:

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

    【讨论】:

      【解决方案2】:

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

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

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

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

      【讨论】:

        【解决方案3】:

        您模拟了构造函数,并在内部创建了一个 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!
            }
        }
        

        【讨论】:

        • 我试过这个,但它给了我一个NullPointerException at AppTest$MockedDate.$init(AppTest.java) at java.util.date.&lt;init&gt;
        • 奇怪,我刚试了一下,它可以工作,使用当前的 jmockit。
        • 你好,我可以为“它”设置一个不同的日期对象吗? it = new Date(...)
        【解决方案4】:

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

        【讨论】:

          猜你喜欢
          • 2013-09-03
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2021-04-27
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多