【问题标题】:Override Java System.currentTimeMillis for testing time sensitive code覆盖 Java System.currentTimeMillis 以测试时间敏感代码
【发布时间】:2010-01-04 19:39:53
【问题描述】:

除了手动更改主机上的系统时钟之外,是否有其他方法(在代码中或使用 JVM 参数)覆盖当前时间(如 System.currentTimeMillis 所示)?

一点背景:

我们有一个系统运行许多会计作业,这些作业的大部分逻辑都围绕当前日期(即本月 1 日、一年 1 日等)

不幸的是,很多遗留代码调用了诸如new Date()Calendar.getInstance() 之类的函数,它们最终都会调用System.currentTimeMillis

出于测试目的,目前,我们只能手动更新系统时钟来控制代码认为正在运行测试的时间和日期。

所以我的问题是:

有没有办法覆盖System.currentTimeMillis 返回的内容?例如,告诉 JVM 在从该方法返回之前自动添加或减去一些偏移量?

【问题讨论】:

  • 我不知道它是否相关,但还有另一种方法可以用 AspectJ 实现这一点,请参阅我的答案:stackoverflow.com/questions/18239859/…
  • @NándorElődFekete 链接中的解决方案很有趣,但是它需要重新编译代码。我想知道原始发帖人是否有能力重新编译,因为他声称要处理遗留代码。
  • @cleberz AspectJ 的优点之一是直接对字节码进行操作,因此不需要原始源代码即可工作。
  • @NándorElődFekete 非常感谢您的提示。我不知道 aspectJ 字节码级别的工具(尤其是 JDK 类工具)。我花了一段时间,但我发现我必须同时对 rt.jar 进行编译时编织以及非 jdk 类的加载时编织才能满足我的需求(覆盖 System.jar)。 currentTimeMillis() 和 System.nanoTime())。
  • @cleberz 我有另一个答案weaving through JRE classes,你也可以看看。

标签: java testing jvm systemtime


【解决方案1】:

tl;博士

除了手动更改主机上的系统时钟之外,是否有其他方法可以通过代码或 JVM 参数覆盖当前时间(如 System.currentTimeMillis 所示)?

是的。

Instant.now( 
    Clock.fixed( 
        Instant.parse( "2016-01-23T12:34:56Z"), ZoneOffset.UTC
    )
)

Clock在java.time

对于可插入时钟替换问题,我们有一个新的解决方案,以方便使用 faux 日期时间值进行测试。 Java 8 中的 java.time package 包含一个抽象类 java.time.Clock,具有明确的目的:

允许在需要时插入备用时钟

您可以插入自己的Clock 实现,尽管您可能会找到一个已经满足您需求的实现。为方便起见,java.time 包含静态方法以产生特殊实现。这些替代实现在测试期间可能很有价值。

改变节奏

各种tick… 方法产生的时钟以不同的节奏递增当前时刻。

默认的Clock 报告的时间更新频率与Java 8 中的milliseconds 和Java 9 中的nanoseconds 一样好(取决于您的硬件)。您可以要求以不同的粒度报告真实的当前时刻。

假时钟

有些时钟可能会撒谎,产生与主机操作系统硬件时钟不同的结果。

  • fixed - 将单个不变(非递增)时刻报告为当前时刻。
  • offset - 报告当前时刻,但被传递的 Duration 参数移动。

例如,锁定今年最早的圣诞节的第一刻。换句话说,当Santa and his reindeer make their first stop。现在最早的时区似乎是Pacific/Kiritimati+14:00

LocalDate ld = LocalDate.now( ZoneId.of( "America/Montreal" ) );
LocalDate xmasThisYear = MonthDay.of( Month.DECEMBER , 25 ).atYear( ld.getYear() );
ZoneId earliestXmasZone = ZoneId.of( "Pacific/Kiritimati" ) ;
ZonedDateTime zdtEarliestXmasThisYear = xmasThisYear.atStartOfDay( earliestXmasZone );
Instant instantEarliestXmasThisYear = zdtEarliestXmasThisYear.toInstant();
Clock clockEarliestXmasThisYear = Clock.fixed( instantEarliestXmasThisYear , earliestXmasZone );

使用那个特殊的固定时钟总是返回相同的时刻。我们在Kiritimati 得到圣诞节的第一刻,UTC 显示 14 小时前的wall-clock time,即 12 月 24 日之前的上午 10 点。

Instant instant = Instant.now( clockEarliestXmasThisYear );
ZonedDateTime zdt = ZonedDateTime.now( clockEarliestXmasThisYear );

instant.toString(): 2016-12-24T10:00:00Z

zdt.toString(): 2016-12-25T00:00+14:00[太平洋/Kiritimati]

live code in IdeOne.com

真实时间,不同时区

您可以控制Clock 实现分配的时区。这在某些测试中可能很有用。但我不建议在生产代码中这样做,您应该始终明确指定可选的 ZoneIdZoneOffset 参数。

您可以指定 UTC 为默认时区。

ZonedDateTime zdtClockSystemUTC = ZonedDateTime.now ( Clock.systemUTC () );

您可以指定任何特定的时区。以continent/region 的格式指定proper time zone name,例如America/MontrealAfrica/CasablancaPacific/Auckland。切勿使用 3-4 个字母的缩写,例如 ESTIST,因为它们不是真正的时区,没有标准化,甚至不是唯一的(!)。

ZonedDateTime zdtClockSystem = ZonedDateTime.now ( Clock.system ( ZoneId.of ( "America/Montreal" ) ) );

您可以指定 JVM 当前的默认时区应该是特定 Clock 对象的默认时区。

ZonedDateTime zdtClockSystemDefaultZone = ZonedDateTime.now ( Clock.systemDefaultZone () );

运行此代码进行比较。请注意,它们都报告同一时刻,时间轴上的同一点。它们仅在wall-clock time 上有所不同;换句话说,三种方式来表达同一件事,三种方式来展示同一时刻。

System.out.println ( "zdtClockSystemUTC.toString(): " + zdtClockSystemUTC );
System.out.println ( "zdtClockSystem.toString(): " + zdtClockSystem );
System.out.println ( "zdtClockSystemDefaultZone.toString(): " + zdtClockSystemDefaultZone );

America/Los_Angeles 是运行此代码的计算机上的 JVM 当前默认区域。

zdtClockSystemUTC.toString(): 2016-12-31T20:52:39.688Z

zdtClockSystem.toString(): 2016-12-31T15:52:39.750-05:00[美国/蒙特利尔]

zdtClockSystemDefaultZone.toString(): 2016-12-31T12:52:39.762-08:00[America/Los_Angeles]

根据定义,Instant 类始终采用 UTC。所以这三个与区域相关的Clock 用法具有完全相同的效果。

Instant instantClockSystemUTC = Instant.now ( Clock.systemUTC () );
Instant instantClockSystem = Instant.now ( Clock.system ( ZoneId.of ( "America/Montreal" ) ) );
Instant instantClockSystemDefaultZone = Instant.now ( Clock.systemDefaultZone () );

instantClockSystemUTC.toString(): 2016-12-31T20:52:39.763Z

instantClockSystem.toString(): 2016-12-31T20:52:39.763Z

instantClockSystemDefaultZone.toString(): 2016-12-31T20:52:39.763Z

默认时钟

Instant.now 默认使用的实现是Clock.systemUTC() 返回的实现。这是您未指定Clock 时使用的实现。在pre-release Java 9 source code for Instant.now 中亲自查看。

public static Instant now() {
    return Clock.systemUTC().instant();
}

OffsetDateTime.nowZonedDateTime.now 的默认 ClockClock.systemDefaultZone()。见source code

public static ZonedDateTime now() {
    return now(Clock.systemDefaultZone());
}

默认实现的行为在 Java 8 和 Java 9 之间发生了变化。在 Java 8 中,尽管类能够存储nanoseconds 的分辨率,但捕获当前时刻的分辨率仅为milliseconds。 Java 9 带来了一个新的实现,能够以纳秒的分辨率捕捉当前时刻——当然,这取决于您的计算机硬件时钟的能力。


关于java.time

java.time 框架内置于 Java 8 及更高版本中。这些类取代了麻烦的旧 legacy 日期时间类,例如 java.util.DateCalendarSimpleDateFormat

要了解更多信息,请参阅Oracle Tutorial。并在 Stack Overflow 上搜索许多示例和解释。规格为JSR 310

Joda-Time 项目现在位于maintenance mode,建议迁移到java.time 类。

您可以直接与您的数据库交换 java.time 对象。使用符合JDBC 4.2 或更高版本的JDBC driver。不需要字符串,不需要java.sql.* 类。 Hibernate 5 & JPA 2.2 支持 java.time

从哪里获取 java.time 类?

【讨论】:

    【解决方案2】:

    强烈建议不要弄乱系统时钟,而是硬着头皮重构旧代码以使用可替换时钟。 理想情况下应该通过依赖注入来完成,但即使您使用可替换的单例,您也可以获得可测试性。

    这几乎可以通过单例版本的搜索和替换实现自动化:

    • Calendar.getInstance() 替换为Clock.getInstance().getCalendarInstance()
    • new Date() 替换为Clock.getInstance().newDate()
    • System.currentTimeMillis() 替换为Clock.getInstance().currentTimeMillis()

    (根据需要等)

    一旦您迈出了第一步,您就可以一次用 DI 替换单例。

    【讨论】:

    • 通过抽象或包装所有潜在的 API 以增加可测试性来使代码混乱,恕我直言,这不是一个好主意。即使您可以通过简单的搜索和替换进行一次重构,代码也会变得更难阅读、理解和维护。几个 mock 框架应该可以修改 System.currentTimeMillis 的行为,如果不能,使用 AOP 或自制工具化是更好的选择。
    • @jarnbjo:好吧,当然欢迎您提出您的意见 - 但这是 IMO 非常成熟的技术,我当然已经使用它取得了很好的效果。它不仅提高了可测试性 - 它还使您对系统时间的依赖更加明确,这在获取代码概览时非常有用。
    • @virgo47:我认为我们必须同意不同意。我不认为“明确说明你的依赖关系”是污染代码——我认为这是使代码更清晰的一种自然方式。我也不认为通过静态调用不必要地隐藏这些依赖项是“完全可以接受的”。
    • 更新 Java 8 中内置的新 java.time package 包括一个 java.time.Clock 类“允许在需要时插入备用时钟”。
    • 这个答案意味着 DI 一切都很好。就个人而言,我还没有在现实世界中看到一个可以很好地利用它的应用程序。相反,我们看到大量无意义的独立接口、无状态“对象”、仅数据“对象”和低内聚类。 IMO、简单性和面向对象的设计(具有真实的、有状态的对象)是更好的选择。关于static 方法,确定它们不是面向对象的,但是注入一个其实例不对任何实例状态进行操作的无状态依赖项并不是真的更好;无论如何,这只是伪装什么是有效的“静态”行为的一种奇特方式。
    【解决方案3】:

    这是一个使用 PowerMockito 的示例。还有一个new Date() 的例子。
    有关mocking system classes 的更多详细信息。

    @RunWith(PowerMockRunner.class)
    @PrepareForTest(LegacyClass.class)
    public class SystemTimeTest {
        
        private final Date fakeNow = Date.from(Instant.parse("2010-12-03T10:15:30.00Z"));
    
        @Before
        public void init() {
            PowerMockito.mockStatic(System.class);
            PowerMockito.when(System.currentTimeMillis()).thenReturn(fakeNow.getTime());
            System.out.println("Fake currentTimeMillis: " + System.currentTimeMillis());
        }
    
        @Test
        public void legacyClass() {
            new LegacyClass().methodWithCurrentTimeMillis();
        }
    
    }
    

    您正在测试的一些遗留类:

    class LegacyClass {
    
        public void methodWithCurrentTimeMillis() {
            long now = System.currentTimeMillis();
            System.out.println("LegacyClass System.currentTimeMillis() is " + now);
        }
    
    }
    

    控制台输出

    Fake currentTimeMillis: 1291371330000
    LegacyClass System.currentTimeMillis() is 1291371330000
    

    【讨论】:

      【解决方案4】:

      一种在使用 EasyMock、没有 Joda Time 和没有 PowerMock 的 Java 8 Web 应用程序中覆盖当前系统时间以进行 JUnit 测试的工作方式。

      这是你需要做的:

      被测类需要做什么

      步骤 1

      将新的java.time.Clock 属性添加到测试类MyService 并确保新属性将使用实例化块或构造函数以默认值正确初始化:

      import java.time.Clock;
      import java.time.LocalDateTime;
      
      public class MyService {
        // (...)
        private Clock clock;
        public Clock getClock() { return clock; }
        public void setClock(Clock newClock) { clock = newClock; }
      
        public void initDefaultClock() {
          setClock(
            Clock.system(
              Clock.systemDefaultZone().getZone() 
              // You can just as well use
              // java.util.TimeZone.getDefault().toZoneId() instead
            )
          );
        }
        { 
          initDefaultClock(); // initialisation in an instantiation block, but 
                              // it can be done in a constructor just as well
        }
        // (...)
      }
      

      第二步

      将新属性clock 注入到调用当前日期时间的方法中。例如,在我的情况下,我必须检查存储在 dataase 中的日期是否发生在 LocalDateTime.now() 之前,我将其替换为 LocalDateTime.now(clock),如下所示:

      import java.time.Clock;
      import java.time.LocalDateTime;
      
      public class MyService {
        // (...)
        protected void doExecute() {
          LocalDateTime dateToBeCompared = someLogic.whichReturns().aDate().fromDB();
          while (dateToBeCompared.isBefore(LocalDateTime.now(clock))) {
            someOtherLogic();
          }
        }
        // (...) 
      }
      

      测试类需要做什么

      第三步

      在测试类中,在调用测试方法doExecute()之前创建一个模拟时钟对象并将其注入到测试类的实例中,然后立即将其重置,如下所示:

      import java.time.Clock;
      import java.time.LocalDateTime;
      import java.time.OffsetDateTime;
      import org.junit.Test;
      
      public class MyServiceTest {
        // (...)
        private int year = 2017;
        private int month = 2;
        private int day = 3;
      
        @Test
        public void doExecuteTest() throws Exception {
          // (...) EasyMock stuff like mock(..), expect(..), replay(..) and whatnot
      
          MyService myService = new MyService();
          Clock mockClock =
            Clock.fixed(
              LocalDateTime.of(year, month, day, 0, 0).toInstant(OffsetDateTime.now().getOffset()),
              Clock.systemDefaultZone().getZone() // or java.util.TimeZone.getDefault().toZoneId()
            );
          myService.setClock(mockClock); // set it before calling the tested method
      
          myService.doExecute(); // calling tested method 
      
          myService.initDefaultClock(); // reset the clock to default right afterwards with our own previously created method
      
          // (...) remaining EasyMock stuff: verify(..) and assertEquals(..)
          }
        }
      

      在调试模式下检查,您会看到 2017 年 2 月 3 日的日期已正确注入 myService 实例并用于比较指令,然后已正确重置为当前日期 initDefaultClock()

      【讨论】:

      • 感谢您的示例。 IMO,这应该是公认的答案。
      【解决方案5】:

      作为said by Jon Skeet

      “使用 Joda Time”几乎总是任何涉及“如何使用 java.util.Date/Calendar 实现 X?”的问题的最佳答案

      这样吧(假设您刚刚将所有new Date() 替换为new DateTime().toDate()

      //Change to specific time
      DateTimeUtils.setCurrentMillisFixed(millis);
      //or set the clock to be a difference from system time
      DateTimeUtils.setCurrentMillisOffset(millis);
      //Reset to system time
      DateTimeUtils.setCurrentMillisSystem();
      

      如果您想导入具有接口的库(请参阅下面 Jon 的评论),您可以使用 Prevayler's Clock,它将提供实现以及标准接口。完整的 jar 文件只有 96kB,所以它不应该破坏银行...

      【讨论】:

      • 不,我不推荐这个——它不会让你走向真正可更换的时钟;它让您始终使用静力学。使用 Joda Time 当然是个好主意,但我仍然想使用 Clock 接口或类似接口。
      • @Jon: True - 用于测试,没关系,但最好有接口。
      • @Jon:如果您想测试已经使用 JodaTime 的时间相关代码,这是一种流畅且恕我直言的完美且简单的方法。如果 JodaTime 已经为您解决了所有问题,那么尝试通过引入另一个抽象层/框架来重新发明轮子并不是可行的方法。
      • @JonSkeet:这不是迈克最初要求的。他想要一个工具来测试与时间相关的代码,而不是生产代码中成熟的可替换时钟。我更喜欢让我的架构尽可能复杂,但尽可能简单。在这里引入额外的抽象层(即接口)根本没有必要
      • @StefanHaberl:有很多东西并不是严格意义上的“必要”——但我真正的建议是让已经存在的依赖显式更容易测试隔离中。用静态来伪造系统时间有静态的所有正常问题——它是全局状态没有好的理由。 Mike 正在寻求一种方法来编写使用当前日期和时间的可测试代码。我仍然相信我的建议比有效地捏造静态要干净得多(并使依赖关系更清晰)。
      【解决方案6】:

      在我看来,只有非侵入性的解决方案才能奏效。特别是如果您有外部库和大型遗留代码库,则没有可靠的方法来模拟时间。

      JMockit ...仅适用于有限数量的times

      PowerMock & Co ...需要将clients 模拟为 System.currentTimeMillis()。又是一种侵入性选择。

      从这里我只看到提到的 javaagentaop 方法对整个系统是透明的。有没有人这样做并且可以指出这样的解决方案?

      @jarnbjo:你能展示一些 javaagent 代码吗?

      【讨论】:

      • jmockit 解决方案可以无限次使用 -XX:-Inline hack(在 Sun 的 HotSpot 上):virgo47.wordpress.com/2012/06/22/changing-system-time-in-java 帖子中提供了完整的便捷类 SystemTimeShifter。类可以在你的测试中使用,或者它可以很容易地用作你真正的主类之前的第一个主类,以便在不同的时间运行你的应用程序(甚至整个应用服务器)。当然,这主要是为了测试目的,而不是为了生产环境。
      【解决方案7】:

      Powermock 效果很好。只是用它来模拟System.currentTimeMillis()

      【讨论】:

      • 我尝试使用 Powermock,如果我理解正确,它不会真正模拟被调用方。相反,它会找到所有调用者,然后应用模拟。这可能非常耗时,因为如果您想真正确定您的应用程序在转移的时间内工作,您必须让它检查您的类路径中的每个类。如果我对 Powermock 的模拟方式有误,请纠正我。
      • @virgo47 不,你理解错了。 PowerMock 永远不会“检查类路径中的每个类”;它只会检查您明确告诉它检查的类(通过测试类上的@PrepareForTest 注释)。
      • @Rogério - 这正是我的理解 - 我说“你必须让它......”。也就是说,如果您希望在类路径(任何库)上的任何位置调用System.currentTimeMillis,则必须检查每个类。我就是这个意思,没有别的意思。关键是你在调用方模拟了这种行为(“你具体告诉”)。这对于简单的测试是可以的,但对于您无法确定从哪里调用该方法的测试(例如,涉及库的更复杂的组件测试)则不行。这并不意味着 Powermock 完全错了,它只是意味着你不能将它用于这种类型的测试。
      • @virgo47 是的,我明白你的意思。我以为你的意思是 PowerMock 必须自动检查类路径中的每个类,这显然是荒谬的。然而,在实践中,对于单元测试,我们通常知道哪个类(es)读取时间,因此指定它不是问题;实际上,对于集成测试,PowerMock 中指定所有使用类的要求可能是个问题。
      【解决方案8】:

      如果你运行的是 Linux,你可以使用 libfaketime 的 master 分支,或者在测试时提交4ce2835

      只需将环境变量设置为您希望模拟 Java 应用程序的时间,然后使用 ld-preloading 运行它:

      # bash
      export FAKETIME="1985-10-26 01:21:00"
      export DONT_FAKE_MONOTONIC=1
      LD_PRELOAD=/usr/local/lib/faketime/libfaketimeMT.so.1 java -jar myapp.jar
      

      第二个环境变量对于 Java 应用程序至关重要,否则会冻结。在撰写本文时,它需要 libfaketime 的 master 分支。

      如果您想更改 systemd 托管服务的时间,只需将以下内容添加到您的单元文件覆盖中,例如对于弹性搜索,这将是 /etc/systemd/system/elasticsearch.service.d/override.conf:

      [Service]
      Environment="FAKETIME=2017-10-31 23:00:00"
      Environment="DONT_FAKE_MONOTONIC=1"
      Environment="LD_PRELOAD=/usr/local/lib/faketime/libfaketimeMT.so.1"
      

      不要忘记使用 `systemctl daemon-reload 重新加载 systemd

      【讨论】:

        【解决方案9】:

        虽然使用一些 DateFactory 模式看起来不错,但它并不涵盖您无法控制的库 - 想象一下 Validation annotation @Past 的实现依赖于 System.currentTimeMillis(有这样的)。

        这就是我们使用jmockit直接模拟系统时间的原因:

        import mockit.Mock;
        import mockit.MockClass;
        ...
        @MockClass(realClass = System.class)
        public static class SystemMock {
            /**
             * Fake current time millis returns value modified by required offset.
             *
             * @return fake "current" millis
             */
            @Mock
            public static long currentTimeMillis() {
                return INIT_MILLIS + offset + millisSinceClassInit();
            }
        }
        
        Mockit.setUpMock(SystemMock.class);
        

        因为无法获得原始的未模拟值 millis,我们使用 nano timer 代替 - 这与挂钟无关,但在这里相对时间就足够了:

        // runs before the mock is applied
        private static final long INIT_MILLIS = System.currentTimeMillis();
        private static final long INIT_NANOS = System.nanoTime();
        
        private static long millisSinceClassInit() {
            return (System.nanoTime() - INIT_NANOS) / 1000000;
        }
        

        有记录的问题,即使用 HotSpot 的时间在多次调用后恢复正常 - 这是问题报告:http://code.google.com/p/jmockit/issues/detail?id=43

        为了克服这个问题,我们必须打开一个特定的 HotSpot 优化 - 使用这个参数 -XX:-Inline 运行 JVM。

        虽然这对于生产来说可能并不完美,但它对于测试来说还不错,而且对于应用程序绝对透明,尤其是当 DataFactory 没有商业意义并且仅仅因为测试而引入时。如果有内置的 JVM 选项可以在不同的时间运行,那就太好了,可惜没有这样的 hack 是不可能的。

        完整的故事在我的博文中: http://virgo47.wordpress.com/2012/06/22/changing-system-time-in-java/

        帖子中提供了完整的便捷类 SystemTimeShifter。类可以在你的测试中使用,或者它可以很容易地用作你真正的主类之前的第一个主类,以便在不同的时间运行你的应用程序(甚至整个应用服务器)。当然,这主要是为了测试目的,而不是为了生产环境。

        编辑 2014 年 7 月:JMockit 最近发生了很大变化,您必须使用 JMockit 1.0 才能正确使用它 (IIRC)。绝对不能升级到界面完全不同的最新版本。我正在考虑只内联必要的东西,但由于我们在新项目中不需要这个东西,所以我根本没有开发这个东西。

        【讨论】:

        • 你改变每个班级的时间的事实是双向的......例如,如果你模拟 nanoTime 而不是 currentTimeMillis 你必须非常小心不要破坏java。 util.concurrent。根据经验,如果 3rd 方库难以在测试中使用,则不应模拟库的输入:您应该模拟库本身。
        • 我们将这种技术用于不模拟其他任何东西的测试。这是集成测试,我们测试整个堆栈是否在一年中第一次发生某些活动时执行它应该做的事情,等等。关于 nanoTime 的好点,幸运的是我们不需要模拟那个,因为它没有挂钟完全的意思。我希望看到 Java 具有时移功能,因为我已经不止一次需要它们,并且因此而弄乱系统时间是非常不走运的。
        【解决方案10】:

        使用面向方面的编程(AOP,例如 AspectJ)来编织 System 类以返回您可以在测试用例中设置的预定义值。

        或编织应用程序类以将调用重定向到System.currentTimeMillis()new Date() 到您自己的另一个实用程序类。

        然而,编织系统类 (java.lang.*) 有点棘手,您可能需要对 rt.jar 执行离线编织并使用单独的 JDK/rt.jar 进行测试。

        它被称为二进制编织,还有专门的tools 来执行系统类的编织并规避一些问题(例如,引导 VM 可能不起作用)

        【讨论】:

        • 当然可以,但是使用模拟工具比使用 AOP 工具要容易得多;后一种工具对于所讨论的目的来说太通用了。
        【解决方案11】:

        如果您想模拟具有 System.currentTimeMillis() 参数的方法,则可以将 Matchers 类的 anyLong() 作为参数传递。

        附:我能够使用上述技巧成功运行我的测试用例,并且只是分享有关我正在使用 PowerMock 和 Mockito 框架的测试的更多详细信息。

        【讨论】:

          【解决方案12】:

          确实没有办法直接在 VM 中执行此操作,但您可以通过编程方式在测试机器上设置系统时间。大多数(全部?)操作系统都有命令行命令来执行此操作。

          【讨论】:

          • 例如,在 Windows 上,datetime 命令。在 Linux 上,date 命令。
          • 为什么不能用 AOP 或工具化来做到这一点(曾经做过)?
          猜你喜欢
          • 1970-01-01
          • 2012-02-28
          • 1970-01-01
          • 1970-01-01
          • 2015-03-04
          • 2021-02-08
          • 1970-01-01
          • 1970-01-01
          • 2023-03-04
          相关资源
          最近更新 更多