如何覆盖/控制 JVM 获取系统日期的方式?

发布于 2024-08-27 21:04:48 字数 124 浏览 8 评论 0原文

如何模拟/欺骗 JVM 来获取当前系统日期以外的日期?我在 JUnit 中有一组测试,我不想更改,但我想更改一个设置,以便当 JVM 检索日期时,它检索到我想要的日期。

你以前做过类似的事情吗?

谢谢。

How do you mock-up/trick the JVM to get a date other that the current system date? I have a set of tests in JUnit I don't want to change, but instead I want to change a setting so that when the JVM retrieves the date it retrieves the date I want.

Have you done something similar before?

Thanks.

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

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

发布评论

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

评论(4

憧憬巴黎街头的黎明 2024-09-03 21:04:49

有几种方法可以做到这一点:

  • 如果您可以重写代码 - 正如其他一些答案中提到的,基本上您需要一个可配置的 NowProvider、DateFactory 或其他策略(我喜欢可注入的 NowProvider.now() 作为 dropin替换 System.currentTimeMillis 我自己)

  • Powermock 和其他一些工具包允许您使用类加载器技巧来覆盖静态方法,甚至系统方法

There are a couple of ways to do this:

  • if you can rewrite code - as mentioned in some other answers, basically you need a configurable NowProvider, DateFactory, or other strategy (I like injectable NowProvider.now() as a dropin replacement for System.currentTimeMillis myself)

  • Powermock, and some other toolkits let you use ClassLoader tricks to override static methods, even system methods

原野 2024-09-03 21:04:49

您可以拥有一个具有两种实现的 DateFactory 接口,一种始终返回 new Date() (这将在生产中使用),以及一个返回 的模拟对象更适合您的单元测试的 Date 对象。

(这是假设您可以更改代码以使其更适合单元测试。)

You could have a DateFactory interface with two implementations, one which always returns new Date() (this would be used in production), and a mock object which returns Date objects which are more appropriate for your unit tests.

(This is assuming you can change your code to make it more amenable to unit testing.)

記柔刀 2024-09-03 21:04:49

我宁愿尝试将该功能隐藏在可模拟接口后面,以便“真实”实现获取系统日期,而您的模拟实现返回您配置的任何日期。

在单元测试中弄乱系统日期对我来说听起来并不是一件好事——如果可以的话,我会尽量避免它。单元测试应该尽可能与上下文无关——从长远来看,这可以省去很多麻烦。

但是,如果您无法修改正在测试的代码,您可能没有其他办法。那么问题是,您只对日期感兴趣,还是对完整时间戳感兴趣?后一种情况对我来说听起来相当无望实现(以上述模拟界面之外的任何其他方式),因为我想在运行测试之前简单地重置日期是不够的 - 您需要在整个单元中返回固定的时间瞬间测试。也许您的测试运行得足够快,可以在时钟滴答声之前完成,也许不是 - 也许它们现在就通过了,然后在添加下一个测试后的某个时间开始随机失败:-(

如果您只需要日期,您可以尝试更改@BeforeClass 方法中的日期,然后在 @AfterClass 中重置它,尽管这很恶心。

I would rather try to hide that functionality behind a mockable interface, so that the "real" implementation gets the system date, while your mock implementation returns whatever date you have configured.

Messing with the system date in unit tests doesn't sound a very nice thing to me - I would try to avoid it if I can. Unit tests should be context-independent as much as possible - that saves a lot of trouble in the long run.

However, if you can't modify the code you are testing, you may have no other way. The question then is, are you interested only in the date, or the full timestamp? The latter case sounds pretty hopeless to me to achieve (in any other way than the above mock interface), as I guess simply resetting the date before running your tests is not enough - you need a fixed instant of time to be returned throughout your unit tests. Maybe your tests run fast enough to finish before the clock ticks, maybe not - maybe they pass right now, then start to fail randomly sometime later after adding the next test :-(

If you only need the date though, you might try changing the date in the @BeforeClass method, then resetting it in @AfterClass, although it is gross.

甜尕妞 2024-09-03 21:04:49

您可以为其编写一个测试规则。像这样的事情可能会起作用:

import org.joda.time.DateTime;
import org.joda.time.DateTimeUtils;
import org.junit.rules.TestRule;
import org.junit.runner.Description;
import org.junit.runners.model.Statement;

public class FixedDateTimeRule implements TestRule {

   private final DateTime fixedDate;

   public FixedDateTimeRule(DateTime fixedDate) {
      this.fixedDate = fixedDate;
   }

   /*
    * (non-Javadoc)
    * 
    * @see org.junit.rules.TestRule#apply(org.junit.runners.model.Statement, org.junit.runner.Description)
    */
   @Override
   public Statement apply(final Statement base, Description description) {
      return new Statement() {

         @Override
         public void evaluate() throws Throwable {
            DateTimeUtils.setCurrentMillisFixed(fixedDate.getMillis());
            try {
               base.evaluate();
            } finally {
               DateTimeUtils.setCurrentMillisSystem();
            }
         }
      };
   }

}

@Rule
public FixedDateTimeRule fixedDateTime = new FixedDateTimeRule(new DateTime(...))
@Test
public void someKindOfTestThatNeedsAFixedDateTime() { ... }

You could write a TestRule for it. Something like this might work:

import org.joda.time.DateTime;
import org.joda.time.DateTimeUtils;
import org.junit.rules.TestRule;
import org.junit.runner.Description;
import org.junit.runners.model.Statement;

public class FixedDateTimeRule implements TestRule {

   private final DateTime fixedDate;

   public FixedDateTimeRule(DateTime fixedDate) {
      this.fixedDate = fixedDate;
   }

   /*
    * (non-Javadoc)
    * 
    * @see org.junit.rules.TestRule#apply(org.junit.runners.model.Statement, org.junit.runner.Description)
    */
   @Override
   public Statement apply(final Statement base, Description description) {
      return new Statement() {

         @Override
         public void evaluate() throws Throwable {
            DateTimeUtils.setCurrentMillisFixed(fixedDate.getMillis());
            try {
               base.evaluate();
            } finally {
               DateTimeUtils.setCurrentMillisSystem();
            }
         }
      };
   }

}

@Rule
public FixedDateTimeRule fixedDateTime = new FixedDateTimeRule(new DateTime(...))
@Test
public void someKindOfTestThatNeedsAFixedDateTime() { ... }
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文