如何编写单元测试以确保我的基于日期/时间的代码适用于所有时区和/或DST?

Aar*_*lla 21 java timezone unit-testing jodatime dst

我正在使用JodaTime 2.1,我正在寻找一种单元测试代码的模式,该代码执行日期/时间操作以确保它在所有时区都表现良好且独立于DST.

特别:

  1. 我怎么能模拟系统时钟(所以我不必模拟所有我调用的地方new DateTime()来获取当前时间)
  2. 如何在默认时区执行相同的操作?

Aar*_*lla 26

你可以使用一个@Rule.以下是规则的代码:

import org.joda.time.DateTimeZone;
import org.junit.rules.TestWatcher;
import org.junit.runner.Description;

public class UTCRule extends TestWatcher {

    private DateTimeZone origDefault = DateTimeZone.getDefault();

    @Override
    protected void starting( Description description ) {
        DateTimeZone.setDefault( DateTimeZone.UTC );
    }

    @Override
    protected void finished( Description description ) {
        DateTimeZone.setDefault( origDefault );
    }
}
Run Code Online (Sandbox Code Playgroud)

你可以使用这样的规则:

public class SomeTest {

    @Rule
    public UTCRule utcRule = new UTCRule();

    ....
}
Run Code Online (Sandbox Code Playgroud)

这将在SomeTest执行每个测试之前将当前时区更改为UTC,并在每次测试后恢复默认时区.

如果要检查多个时区,请使用如下规则:

import org.joda.time.DateTimeZone;
import org.junit.rules.TestWatcher;
import org.junit.runner.Description;

public class TZRule extends TestWatcher {

    private DateTimeZone origDefault = DateTimeZone.getDefault();

    private DateTimeZone tz;

    public TZRule( DateTimeZone tz ) {
        this.tz = tz;
    }

    @Override
    protected void starting( Description description ) {
        DateTimeZone.setDefault( tz );
    }

    @Override
    protected void finished( Description description ) {
        DateTimeZone.setDefault( origDefault );
    }
}
Run Code Online (Sandbox Code Playgroud)

将所有受影响的测试放在抽象基类中AbstractTZTest并对其进行扩展:

public class UTCTest extends AbstractTZTest {
    @Rule public TZRule tzRule = new TZRule( DateTimeZone.UTC );
}
Run Code Online (Sandbox Code Playgroud)

这将AbstractTZTest用UTC 执行所有测试.对于要测试的每个时区,您需要另一个类:

public class UTCTest extends AbstractTZTest {
    @Rule public TZRule tzRule = new TZRule( DateTimeZone.forID( "..." );
}
Run Code Online (Sandbox Code Playgroud)

由于测试用例是继承的,所以 - 只需要定义规则.

以类似的方式,您可以移动系统时钟.使用调用的规则DateTimeUtils.setCurrentMillisProvider(...)模拟测试在特定时间运行并DateTimeUtils.setCurrentMillisSystem()恢复默认值.

注意:您的提供商需要一种方法来设置时钟,或者所有新DateTime实例都具有相同的值.每次getMillis()调用时,我经常将值提前一毫秒.

注2:这只适用于joda-time.它不会影响new java.util.Date().

注3:您不能再并行运行这些测试.它们必须按顺序运行,否则其中一个很可能会在另一个测试运行时恢复默认时区.