如何编写unit testing以确保我的基于日期/时间的代码适用于所有时区和/ DST?

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

特别:

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

您可以使用@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 ); } } 

你可以使用这样的规则:

 public class SomeTest { @Rule public UTCRule utcRule = new UTCRule(); .... } 

这将在执行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 ); } } 

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

 public class UTCTest extends AbstractTZTest { @Rule public TZRule tzRule = new TZRule( DateTimeZone.UTC ); } 

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

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

由于测试用例是inheritance的,所以 – 只需要定义规则即可。

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

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

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

 for (String zoneId : DateTimeZone.getAvailableIDs()) { DateTime testedDate1; DateTime testedDate2; try { final DateTimeZone tz = DateTimeZone.forID(zoneId); // your test with testedDate1 and testedDate2 } catch (final IllegalArgumentException e) { // catching DST problem testedDate1 = testetDate1.plusHours(1); testedDate2 = testetDate2.plusHours(1); // repeat your test for this dates } } 

单个测试的更改

 DateTimeZone default; DateTimeZone testedTZ; @Before public void setUp() { default = GateTimeZone.getDefault(); DateTimeZone.setDefault } @After public void tearDown() { default = GateTimeZone.setDefault(); DateTimeZone.setDefault(testedTZ) } @Test public void test() { //... }