覆盖 Java System.currentTimeMillis 以测试时间敏感代码
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/2001671/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Override Java System.currentTimeMillis for testing time sensitive code
提问by Mike Clark
Is there a way, either in code or with JVM arguments, to override the current time, as presented via System.currentTimeMillis
, other than manually changing the system clock on the host machine?
System.currentTimeMillis
除了手动更改主机上的系统时钟之外,有没有办法在代码中或使用 JVM 参数覆盖当前时间,如通过 呈现的那样?
A little background:
一点背景:
We have a system that runs a number of accounting jobs that revolve much of their logic around the current date (ie 1st of the month, 1st of the year, etc)
我们有一个系统运行许多会计工作,这些工作围绕当前日期(即一个月的第一天,一年的第一天等)进行大部分逻辑
Unfortunately, a lot of the legacy code calls functions such as new Date()
or Calendar.getInstance()
, both of which eventually call down to System.currentTimeMillis
.
不幸的是,许多遗留代码调用诸如new Date()
或 之类的函数Calendar.getInstance()
,这两者最终都会调用到System.currentTimeMillis
.
For testing purposes, right now, we are stuck with manually updating the system clock to manipulate what time and date the code thinks that the test is being run.
现在,出于测试目的,我们不得不手动更新系统时钟来操纵代码认为测试正在运行的时间和日期。
So my question is:
所以我的问题是:
Is there a way to override what is returned by System.currentTimeMillis
? For example, to tell the JVM to automatically add or subtract some offset before returning from that method?
有没有办法覆盖返回的内容System.currentTimeMillis
?例如,告诉 JVM 在从该方法返回之前自动添加或减去一些偏移量?
Thanks in advance!
提前致谢!
采纳答案by Jon Skeet
I stronglyrecommend that instead of messing with the system clock, you bite the bullet and refactor that legacy code to use a replaceable clock. Ideallythat should be done with dependency injection, but even if you used a replaceable singleton you would gain testability.
我强烈建议您不要弄乱系统时钟,而是硬着头皮重构遗留代码以使用可替换的时钟。理想情况下,这应该通过依赖注入来完成,但即使您使用可替换的单例,您也会获得可测试性。
This could almost be automated with search and replace for the singleton version:
这几乎可以通过搜索和替换单例版本实现自动化:
- Replace
Calendar.getInstance()
withClock.getInstance().getCalendarInstance()
. - Replace
new Date()
withClock.getInstance().newDate()
- Replace
System.currentTimeMillis()
withClock.getInstance().currentTimeMillis()
- 替换
Calendar.getInstance()
为Clock.getInstance().getCalendarInstance()
。 - 替换
new Date()
为Clock.getInstance().newDate()
- 替换
System.currentTimeMillis()
为Clock.getInstance().currentTimeMillis()
(etc as required)
(等根据需要)
Once you've taken that first step, you can replace the singleton with DI a bit at a time.
一旦你迈出了第一步,你可以一次用 DI 替换单例。
回答by Jeremy Raymond
There really isn't a way to do this directly in the VM, but you could all something to programmatically set the system time on the test machine. Most (all?) OS have command line commands to do this.
确实没有办法直接在 VM 中执行此操作,但是您可以通过编程方式在测试机器上设置系统时间。大多数(全部?)操作系统都有命令行命令来执行此操作。
回答by Stephen
正如乔恩·斯基特所说:
"use Joda Time" is almost always the best answer to any question involving "how do I achieve X with java.util.Date/Calendar?"
对于涉及“如何使用 java.util.Date/Calendar 实现 X”的任何问题,“使用 Joda 时间”几乎总是最佳答案。
So here goes (presuming you've just replaced all your new Date()
with new DateTime().toDate()
)
所以这里去(假设你刚刚更换所有的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();
If you want import a library that has an interface (see Jon's comment below), you could just use Prevayler's Clock, which will provide implementations as well as the standard interface. The full jar is only 96kB, so it shouldn't break the bank...
如果您想导入具有接口的库(请参阅下面的 Jon 评论),您可以只使用Prevayler's Clock,它将提供实现以及标准接口。完整的罐子只有 96kB,所以它不应该破坏银行......
回答by mhaller
Use Aspect-Oriented Programming (AOP, for example AspectJ) to weave the System class to return a predefined value which you could set within your test cases.
使用面向方面的编程(AOP,例如 AspectJ)来编织 System 类以返回您可以在测试用例中设置的预定义值。
Or weave the application classes to redirect the call to System.currentTimeMillis()
or to new Date()
to another utility class of your own.
或者编织应用程序类以将调用重定向到System.currentTimeMillis()
或重定向到new Date()
您自己的另一个实用程序类。
Weaving system classes (java.lang.*
) is however a little bit more trickier and you might need to perform offline weaving for rt.jar and use a separate JDK/rt.jar for your tests.
java.lang.*
然而,编织系统类 ( ) 有点棘手,您可能需要为 rt.jar 执行离线编织并使用单独的 JDK/rt.jar 进行测试。
It's called Binary weavingand there are also special toolsto perform weaving of System classes and circumvent some problems with that (e.g. bootstrapping the VM may not work)
它被称为二进制编织,还有一些特殊的工具来执行系统类的编织并规避一些问题(例如引导虚拟机可能不起作用)
回答by ReneS
回答by user1477398
In my opinion only a none-invasive solution can work. Especially if you have external libs and a big legacy code base there is no reliable way to mock out time.
在我看来,只有非侵入性的解决方案才能奏效。特别是如果您有外部库和大量遗留代码库,则没有可靠的方法来模拟时间。
JMockit ... works only for restricted number of times
JMockit ...仅适用于有限的次数
PowerMock & Co ...needs to mock the clientsto System.currentTimeMillis(). Again an invasive option.
PowerMock & Co ...需要将客户端模拟到 System.currentTimeMillis()。又是一个侵入性的选择。
From this I only see the mentioned javaagentor aopapproach being transparent to the whole system. Has anybody done that and could point to such a solution?
由此我只看到提到的javaagent或aop方法对整个系统是透明的。有没有人这样做过并且可以指出这样的解决方案?
@jarnbjo: could you show some of the javaagent code please?
@jarnbjo:你能展示一些 javaagent 代码吗?
回答by virgo47
While using some DateFactory pattern seems nice, it does not cover libraries you can't control - imagine Validation annotation @Past with implementation relying on System.currentTimeMillis (there is such).
虽然使用一些 DateFactory 模式看起来不错,但它不包括您无法控制的库 - 想象验证注释 @Past 与依赖 System.currentTimeMillis 的实现(有这样的)。
That's why we use jmockit to mock the system time directly:
这就是为什么我们使用 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);
Because it's not possible to get to the original unmocked value of millis, we use nano timer instead - this is not related to wall clock, but relative time suffices here:
因为不可能达到millis的原始unmocked值,所以我们使用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;
}
There is documented problem, that with HotSpot the time gets back to normal after a number of calls - here is the issue report: http://code.google.com/p/jmockit/issues/detail?id=43
有记录的问题,HotSpot 的时间在多次调用后恢复正常 - 这是问题报告:http: //code.google.com/p/jmockit/issues/detail?id=43
To overcome this we have to turn on one specific HotSpot optimization - run JVM with this argument -XX:-Inline
.
为了克服这个问题,我们必须打开一个特定的 HotSpot 优化——用这个参数运行 JVM -XX:-Inline
。
While this may not be perfect for production, it is just fine for tests and it is absolutely transparent for application, especially when DataFactory doesn't make business sense and is introduced only because of tests. It would be nice to have built-in JVM option to run in different time, too bad it is not possible without hacks like this.
虽然这对于生产来说可能并不完美,但它对于测试来说很好,并且对于应用程序来说是绝对透明的,尤其是当 DataFactory 没有商业意义并且仅因为测试而引入时。有内置的 JVM 选项在不同的时间运行会很好,太糟糕了,没有这样的 hack 是不可能的。
Complete story is in my blog post here: http://virgo47.wordpress.com/2012/06/22/changing-system-time-in-java/
完整的故事在我的博客文章中:http: //virgo47.wordpress.com/2012/06/22/changed-system-time-in-java/
Complete handy class SystemTimeShifter is provided in the post. Class can be used in your tests, or it can be used as the first main class before your real main class very easily in order to run your application (or even whole appserver) in a different time. Of course, this is intented for testing purposes mainly, not for production environment.
帖子中提供了完整的便捷类 SystemTimeShifter。类可以在您的测试中使用,也可以非常轻松地用作真正的主类之前的第一个主类,以便在不同的时间运行您的应用程序(甚至整个应用程序服务器)。当然,这主要用于测试目的,而不是用于生产环境。
EDIT July 2014: JMockit changed a lot lately and you are bound to use JMockit 1.0 to use this correctly (IIRC). Definitely can't upgrade to newest version where interface is completly different. I was thinking about inlining just the necessary stuff, but as we don't need this thing in our new projects I'm not developing this thing at all.
编辑 2014 年 7 月:JMockit 最近发生了很大变化,您必须使用 JMockit 1.0 才能正确使用它(IIRC)。绝对不能升级到界面完全不同的最新版本。我正在考虑内联必要的东西,但由于我们在新项目中不需要这个东西,我根本没有开发这个东西。
回答by Basil Bourque
tl;dr
tl;博士
Is there a way, either in code or with JVM arguments, to override the current time, as presented via System.currentTimeMillis, other than manually changing the system clock on the host machine?
除了手动更改主机上的系统时钟之外,有没有办法在代码中或使用 JVM 参数覆盖当前时间,如通过 System.currentTimeMillis 呈现的那样?
Yes.
是的。
Instant.now(
Clock.fixed(
Instant.parse( "2016-01-23T12:34:56Z"), ZoneOffset.UTC
)
)
Clock
In java.time
Clock
在 java.time
We have a new solution to the problem of a pluggable clock replacement to facilitate testing with fauxdate-time values. The java.time packagein Java 8includes an abstract class java.time.Clock
, with an explicit purpose:
我们为可插拔时钟替换问题提供了新的解决方案,以方便使用虚假日期时间值进行测试。该java.time包中的Java 8包含一个抽象类java.time.Clock
,有明确的目的:
to allow alternate clocks to be plugged in as and when required
允许在需要时插入备用时钟
You could plug in your own implementation of Clock
, though you likely can find one already made to meet your needs. For your convenience, java.time includes static methods to yield special implementations. These alternate implementations can be valuable during testing.
您可以插入自己的 实现Clock
,尽管您可能会找到一个已经满足您需求的实现。为方便起见,java.time 包含静态方法以产生特殊实现。这些替代实现在测试期间可能很有价值。
Altered cadence
改变节奏
The various tick…
methods produce clocks that increment the current moment with a different cadence.
各种 tick…
方法产生的时钟以不同的节奏递增当前时刻。
The default Clock
reports a time updated as frequently as millisecondsin Java 8 and in Java 9 as fine as nanoseconds(depending on your hardware). You can ask for the true current moment to be reported with a different granularity.
默认情况下,Java 8 中的Clock
更新频率为毫秒,Java 9 中的更新频率为纳秒(取决于您的硬件)。您可以要求以不同的粒度报告真实的当前时刻。
tickSeconds
- Increments in whole secondstickMinutes
- Increments in whole minutestick
- Increments by the passedDuration
argument.
tickSeconds
- 整秒递增tickMinutes
- 整分钟递增tick
- 通过传递的Duration
参数递增。
False clocks
假时钟
Some clocks can lie, producing a result different than that of the host OS' hardware clock.
一些时钟可能会撒谎,产生与主机操作系统硬件时钟不同的结果。
fixed
- Reports a single unchanging (non-incrementing) moment as the current moment.offset
- Reports the current moment but shifted by the passedDuration
argument.
For example, lock in the first moment of the earliest Christmas this year. in other words, when Santa and his reindeer make their first stop. The earliest time zone nowadays seems to be Pacific/Kiritimati
at +14:00
.
例如,锁定今年最早的圣诞节的第一个时刻。换句话说,当圣诞老人和他的驯鹿第一站时。最早的时区现在似乎是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 );
Use that special fixed clock to always return the same moment. We get the first moment of Christmas day in Kiritimati, with UTC showing a wall-clock timeof fourteen hours earlier, 10 AM on the prior date of the 24th of December.
使用那个特殊的固定时钟总是返回相同的时刻。我们在Kiritimati看到圣诞节的第一个时刻,UTC 显示的挂钟时间提前了 14 小时,即 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[Pacific/Kiritimati]
Instant.toString(): 2016-12-24T10:00:00Z
zdt.toString(): 2016-12-25T00:00+14:00[Pacific/Kiritimati]
True time, different time zone
真实时间,不同时区
You can control which time zone is assigned by the Clock
implementation. This might be useful in some testing. But I do not recommend this in production code, where you should always specify explicitly the optional ZoneId
or ZoneOffset
arguments.
您可以控制Clock
实现分配的时区。这在某些测试中可能很有用。但是我不建议在生产代码中使用它,您应该始终明确指定可选ZoneId
或ZoneOffset
参数。
You can specify that UTC be the default zone.
您可以将 UTC 指定为默认区域。
ZonedDateTime zdtClockSystemUTC = ZonedDateTime.now ( Clock.systemUTC () );
You can specify any particular time zone. Specify a proper time zone namein the format of continent/region
, such as America/Montreal
, Africa/Casablanca
, or Pacific/Auckland
. Never use the 3-4 letter abbreviation such as EST
or IST
as they are nottrue time zones, not standardized, and not even unique(!).
您可以指定任何特定的时区。以、、 或等格式指定正确的时区名称。永远不要使用 3-4 个字母的缩写,例如或因为它们不是真正的时区,不是标准化的,甚至不是唯一的(!)。continent/region
America/Montreal
Africa/Casablanca
Pacific/Auckland
EST
IST
ZonedDateTime zdtClockSystem = ZonedDateTime.now ( Clock.system ( ZoneId.of ( "America/Montreal" ) ) );
You can specify the JVM's current default time zone should be the default for a particular Clock
object.
您可以指定 JVM 的当前默认时区应该是特定Clock
对象的默认时区。
ZonedDateTime zdtClockSystemDefaultZone = ZonedDateTime.now ( Clock.systemDefaultZone () );
Run this code to compare. Note that they all report the same moment, the same point on the timeline. They differ only in wall-clock time; in other words, three ways to say the same thing, three ways to display the same moment.
运行此代码进行比较。请注意,它们都报告同一时刻,时间轴上的同一点。它们仅在挂钟时间上有所不同;换句话说,说同样的事情的三种方式,展示同一时刻的三种方式。
System.out.println ( "zdtClockSystemUTC.toString(): " + zdtClockSystemUTC );
System.out.println ( "zdtClockSystem.toString(): " + zdtClockSystem );
System.out.println ( "zdtClockSystemDefaultZone.toString(): " + zdtClockSystemDefaultZone );
America/Los_Angeles
was the JVM current default zone on the computer that ran this code.
America/Los_Angeles
是运行此代码的计算机上的 JVM 当前默认区域。
zdtClockSystemUTC.toString(): 2016-12-31T20:52:39.688Z
zdtClockSystem.toString(): 2016-12-31T15:52:39.750-05:00[America/Montreal]
zdtClockSystemDefaultZone.toString(): 2016-12-31T12:52:39.762-08:00[America/Los_Angeles]
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]
The Instant
class is always in UTC by definition. So these three zone-related Clock
usages have exactly the same effect.
根据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
InstantClockSystemUTC.toString(): 2016-12-31T20:52:39.763Z
InstantClockSystem.toString(): 2016-12-31T20:52:39.763Z
InstantClockSystemDefaultZone.toString(): 2016-12-31T20:52:39.763Z
Default clock
默认时钟
The implementation used by default for Instant.now
is the one returned by Clock.systemUTC()
. This is the implementation used when you do not specify a Clock
. See for yourself in pre-release Java 9 source code for Instant.now
.
for 默认使用的实现Instant.now
是由 返回的实现Clock.systemUTC()
。这是未指定Clock
. 在预发布的 Java 9 源代码中Instant.now
亲自查看.
public static Instant now() {
return Clock.systemUTC().instant();
}
The default Clock
for OffsetDateTime.now
and ZonedDateTime.now
is Clock.systemDefaultZone()
. See source code.
默认Clock
的OffsetDateTime.now
和ZonedDateTime.now
是Clock.systemDefaultZone()
。请参阅源代码。
public static ZonedDateTime now() {
return now(Clock.systemDefaultZone());
}
The behavior of the default implementations changed between Java 8 and Java 9. In Java 8, the current moment is captured with a resolution only in millisecondsdespite the classes' ability to store a resolution of nanoseconds. Java 9 brings a new implementation able to capture the current moment with a resolution of nanoseconds – depending, of course, on the capability of your computer hardware clock.
默认实现的行为在 Java 8 和 Java 9 之间发生了变化。在 Java 8 中,尽管类能够存储nanoseconds分辨率,但捕获当前时刻的分辨率仅为毫秒。Java 9 带来了一种新的实现,能够以纳秒的分辨率捕捉当前时刻——当然,这取决于计算机硬件时钟的能力。
About java.time
关于java.time
The java.timeframework is built into Java 8 and later. These classes supplant the troublesome old legacydate-time classes such as java.util.Date
, Calendar
, & SimpleDateFormat
.
该java.time框架是建立在Java 8和更高版本。这些类取代了麻烦的旧的遗留日期时间类,例如java.util.Date
, Calendar
, & SimpleDateFormat
。
The Joda-Timeproject, now in maintenance mode, advises migration to the java.timeclasses.
现在处于维护模式的Joda-Time项目建议迁移到java.time类。
To learn more, see the Oracle Tutorial. And search Stack Overflow for many examples and explanations. Specification is JSR 310.
要了解更多信息,请参阅Oracle 教程。并在 Stack Overflow 上搜索许多示例和解释。规范是JSR 310。
You may exchange java.timeobjects directly with your database. Use a JDBC drivercompliant with JDBC 4.2or later. No need for strings, no need for java.sql.*
classes.
您可以直接与您的数据库交换java.time对象。使用符合JDBC 4.2或更高版本的JDBC 驱动程序。不需要字符串,不需要类。java.sql.*
Where to obtain the java.time classes?
从哪里获得 java.time 类?
- Java SE 8, Java SE 9, and later
- Built-in.
- Part of the standard Java API with a bundled implementation.
- Java 9 adds some minor features and fixes.
- Java SE 6and Java SE 7
- Much of the java.time functionality is back-ported to Java 6 & 7 in ThreeTen-Backport.
- Android
- Later versions of Android bundle implementations of the java.time classes.
- For earlier Android (<26), the ThreeTenABPproject adapts ThreeTen-Backport(mentioned above). See How to use ThreeTenABP….
- Java SE 8、Java SE 9及更高版本
- 内置。
- 具有捆绑实现的标准 Java API 的一部分。
- Java 9 添加了一些小功能和修复。
- Java SE 6和Java SE 7
- 多的java.time功能后移植到Java 6和7在ThreeTen-反向移植。
- 安卓
- java.time 类的更高版本的 Android 捆绑实现。
- 对于早期的 Android(<26),ThreeTenABP项目采用了ThreeTen-Backport(上面提到过)。请参阅如何使用ThreeTenABP ...。
The ThreeTen-Extraproject extends java.time with additional classes. This project is a proving ground for possible future additions to java.time. You may find some useful classes here such as Interval
, YearWeek
, YearQuarter
, and more.
该ThreeTen-额外项目与其他类扩展java.time。该项目是未来可能添加到 java.time 的试验场。你可能在这里找到一些有用的类,比如Interval
,YearWeek
,YearQuarter
,和更多。
回答by Paresh Mayani
If you want to mock the method having System.currentTimeMillis()
argument then you can pass anyLong()
of Matchers class as an argument.
如果要模拟具有System.currentTimeMillis()
参数的方法,则可以将anyLong()
Matchers 类作为参数传递。
P.S. I am able to run my test case successfully using the above trick and just to share more details about my test that I am using PowerMock and Mockito frameworks.
PS 我能够使用上述技巧成功运行我的测试用例,只是为了分享有关我使用 PowerMock 和 Mockito 框架的测试的更多详细信息。
回答by KiriSakow
A working way to override current system time for JUnit testing purposes in a Java 8 web application with EasyMock, without Joda Time, and without PowerMock.
在 Java 8 Web 应用程序中使用 EasyMock、没有 Joda Time 和没有 PowerMock 覆盖当前系统时间以进行 JUnit 测试。
Here's what you need to do:
您需要执行以下操作:
What needs to be done in the tested class
测试类需要做什么
Step 1
第1步
Add a new java.time.Clock
attribute to the tested class MyService
and make sure the new attribute will be initialized properly at default values with an instantiation block or a constructor:
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
}
// (...)
}
Step 2
第2步
Inject the new attribute clock
into the method which calls for a current date-time. For instance, in my case I had to perform a check of whether a date stored in dataase happened before LocalDateTime.now()
, which I remplaced with LocalDateTime.now(clock)
, like so:
将新属性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();
}
}
// (...)
}
What needs to be done in the test class
测试类需要做什么
Step 3
第 3 步
In the test class, create a mock clock object and inject it into the tested class's instance just before you call the tested method doExecute()
, then reset it back right afterwards, like so:
在测试类中,创建一个模拟时钟对象,并在调用测试方法之前将其注入到测试类的实例中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(..)
}
}
Check it in debug mode and you will see the date of 2017 Feb 3 has been correctly injected into myService
instance and used in the comparison instruction, and then has been properly reset to current date with initDefaultClock()
.
在调试模式下检查,您将看到 2017 年 2 月 3 日的日期已正确注入myService
实例并在比较指令中使用,然后已正确重置为当前日期initDefaultClock()
。