我正在使用 JodaTime 2.1,我正在寻找一种模式来单元测试代码,该代码执行日期/时间操作,以确保它在所有时区都表现良好并且独立于DST。
具体来说:
- 如何模拟系统时钟(这样我就不必模拟我调用的所有地方
new DateTime()
来获取当前时间) - 我怎样才能为默认时区做同样的事情?
您可以@Rule
为此使用 a 。这是规则的代码:
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();
....
}
这将在每次测试执行之前将当前时区更改为 UTC,并在每次测试SomeTest
后恢复默认时区。
如果要检查多个时区,请使用如下规则:
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 );
}
AbstractTZTest
这将使用 UTC执行所有测试。对于您要测试的每个时区,您需要另一个类:
public class UTCTest extends AbstractTZTest {
@Rule public TZRule tzRule = new TZRule( DateTimeZone.forID( "..." );
}
由于测试用例是继承的,仅此而已 - 您只需要定义规则。
以类似的方式,您可以移动系统时钟。使用调用DateTimeUtils.setCurrentMillisProvider(...)
来模拟测试在特定时间运行并DateTimeUtils.setCurrentMillisSystem()
恢复默认值的规则。
注意:您的提供商将需要一种方法来使时钟滴答作响,否则所有新DateTime
实例都将具有相同的值。每次getMillis()
调用时,我经常将值提前一毫秒。
注意 2:这仅适用于 joda-time。它不影响new java.util.Date()
。
注意 3:您不能再并行运行这些测试。它们必须按顺序运行,否则其中一个很可能会在另一个测试运行时恢复默认时区。
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()
{
//...
}