如何编写unit testing以确保我的基于日期/时间的代码适用于所有时区和/ DST?
我正在使用JodaTime 2.1,我正在寻找一种unit testing代码的模式,该代码执行日期/时间操作以确保它在所有时区都表现良好且独立于DST 。
特别:
- 我如何模拟系统时钟(所以我不必模拟我调用
new DateTime()
以获取当前时间的所有位置) - 如何在默认时区执行相同操作?
您可以使用@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() { //... }