【问题标题】:Unit testing time-based logic in JavaJava中基于时间的逻辑单元测试
【发布时间】:2012-02-23 09:16:54
【问题描述】:

我有一个方法可以根据当前日期对从数据库中获取的数据实现不同的逻辑。

我想通过让单元测试创​​建对象、将它们保存在数据库中并调用测试方法来对其进行测试。但是,为了获得可预测的结果,我每次都需要更改系统日期,而我不知道如何在 Java 中做到这一点。

建议?

【问题讨论】:

  • 更改您的方法,以便您确实将时间作为参数,然后您可以通过给出您想要的任何时间来测试它。什么反对这个?
  • 你能告诉你如何获得当前日期和时间吗?也许嘲笑会起作用......
  • 现在只有new Date(),也许我确实需要一个 TimeProvider 类

标签: java unit-testing date time


【解决方案1】:

您可以使用当前日期生成预期结果。

或者你编写你的系统来使用你在测试时给它的日期/时间(而不是时钟)这样时间总是测试所期望的。

我使用类似的东西

interface TimeSource {
    long currentTimeMS(); // actually I have currentTimeNS
    void currentTimeMS(long currentTimeMS);
}

enum VanillaTimeSource implements TimeSource {
    INSTANCE;

    @Override
    public long currentTimeMS() {
        return System.currentTimeMillis();
    }

    @Override
    public void currentTimeMS(long currentTimeMS) {
        // ignored
    }
}

class FixedTimeSource implements TimeSource {
    private long currentTimeMS;
    @Override
    public long currentTimeMS() {
        return currentTimeMS;
    }

    @Override
    public void currentTimeMS(long currentTimeMS) {
        this.currentTimeMS =              currentTimeMS;
    }
}

在测试中,我使用了可以数据驱动的 FixedTimeSource,例如由输入/事件设置。在生产中,我使用 VanillaTimeSource.INSTANCE 忽略输入/事件中的时间并使用当前时间。

【讨论】:

  • 第一个建议可能行不通。我还应该提到,准确获取的数据也是基于日期(根据某一列)。这意味着即使逻辑没有不同,我也必须根据一年中的几乎每一天生成不同的结果。我可能会选择第二个建议
  • 您可以在检查结果之前生成预期结果作为运行测试的一部分。您不需要提前生成它们。
  • 确实创建一个接收时间作为参数的类/组件确保它是一个好的组件,因为它与系统时间解耦。您将所有特定于应用程序的配置(“使用系统时间”)放在组件之外。它只需要依赖于外部可配置的刺激。
  • 很酷的类名。很酷的解决方案。
【解决方案2】:

你需要考虑在你的类中注入一些东西,允许你自定义时间的呈现方式。

例如

public interface TimeProvider {
   DateTime getCurrentTime();
}

public class UnderTest {

  // Inject this in some way (e.g. provide it in the constructor)
  private TimeProvider timeProvider;

  public void MyMethod() {
     if (timeProvider.getCurrentTime() == "1234") {
        // Do something
     }
  }
}

现在在您的单元测试中,您可以提供时间提供程序的虚假实现。在实际生产代码中,您可以只返回当前日期时间。

【讨论】:

  • 当然,您也可以按照belgther 的建议将其作为参数提供:)
【解决方案3】:

我最近遇到了类似的问题,我无法重构太多代码(时间限制,不想无意中破坏任何东西)。它有一个我想测试的方法,称为 System.currentTimeMillis() ,我想测试的情况取决于返回的值。比如:

public class ClassINeedToTest {
    public boolean doStuff() {
        long l = System.currentTimeMillis();
        // do some calculation based on l
        // and return the calculation
    }
}

为了允许单元测试,我重构了这个类,使它有一个受保护的辅助方法

protected long getCurrentTimeMillis() {
     // only for unit-testing purposes
     return System.currentTimeMillis();
}

这个方法是由doStuff()调用的。这并没有改变功能,但现在意味着当我在单元测试中调用它时,我可以覆盖它以返回一个特定的值,比如

ClassINeedToTest testClass = new ClassINeedToTest() {
    protected long getCurrentTimeMillis() {
        // return specific date for my test
        return 12456778L;
    }
}; 
boolean result = testClass.doStuff();
// test result with an assert here

但这确实意味着我已经污染了我的班级的界面,所以你可能会认为成本太高了。如果您可以更多地重构代码,可能会有更好的方法。

【讨论】:

    猜你喜欢
    • 2013-03-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-12-02
    • 1970-01-01
    • 2010-09-30
    • 2012-02-05
    相关资源
    最近更新 更多