用Java测试基于时间的逻辑单元

时间:2012-02-23 09:16:55

标签: java unit-testing date time

我有一个方法,根据当前日期的不同,对从DB中提取的数据实现不同的逻辑。

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

建议?

3 个答案:

答案 0 :(得分:14)

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

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

我使用类似

的东西
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,它忽略输入/事件中的时间并使用当前时间。

答案 1 :(得分:8)

你需要考虑在课堂上注入一些东西,让你自定义时间的呈现方式。

例如

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
     }
  }
}

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

答案 2 :(得分:2)

我最近遇到了类似的问题代码我无法重构太多(时间限制,不想无意中破坏任何东西)。它有一个我想测试的方法,它调用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

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