java 我如何编写单元测试以确保我的基于日期/时间的代码适用于所有时区和有/没有 DST?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/10846704/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How do I write unit tests to make sure my date/time based code works for all time zones and with/out DST?
提问by Aaron Digulla
I'm using JodaTime 2.1 and I'm looking for a pattern to unit test code which performs date/time operations to make sure it behaves well for all time zones and independent of DST.
我正在使用 JodaTime 2.1 并且我正在寻找一种模式来单元测试代码,该代码执行日期/时间操作以确保它在所有时区都表现良好并且独立于DST。
Specifically:
具体来说:
- How can I mock the system clock (so I don't have to mock all the places where I call
new DateTime()
to get the current time) - How can I do the same for the default time zone?
- 我怎样才能模拟系统时钟(所以我不必模拟我打电话
new DateTime()
来获取当前时间的所有地方) - 我怎样才能对默认时区做同样的事情?
回答by Aaron Digulla
You can use a @Rule
for this. Here is the code for the rule:
您可以@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 );
}
}
You can use the rule like this:
您可以像这样使用规则:
public class SomeTest {
@Rule
public UTCRule utcRule = new UTCRule();
....
}
This will change the current time zone to UTC before each test in SomeTest
will be executed and it will restore the default time zone after each test.
这将在SomeTest
执行每个测试之前将当前时区更改为 UTC,并在每次测试后恢复默认时区。
If you want to check several time zones, use a rule like this one:
如果要检查多个时区,请使用如下规则:
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 );
}
}
Put all the affected tests in an abstract base class AbstractTZTest
and extend it:
将所有受影响的测试放在一个抽象基类中AbstractTZTest
并扩展它:
public class UTCTest extends AbstractTZTest {
@Rule public TZRule tzRule = new TZRule( DateTimeZone.UTC );
}
That will execute all tests in AbstractTZTest
with UTC. For each time zone that you want to test, you'll need another class:
这将AbstractTZTest
使用 UTC执行所有测试。对于您要测试的每个时区,您都需要另一个类:
public class UTCTest extends AbstractTZTest {
@Rule public TZRule tzRule = new TZRule( DateTimeZone.forID( "..." );
}
Since test cases are inherited, that's all - you just need to define the rule.
由于测试用例是继承的,仅此而已 - 您只需要定义规则。
In a similar way, you can shift the system clock. Use a rule that calls DateTimeUtils.setCurrentMillisProvider(...)
to simulate that the test runs at a certain time and DateTimeUtils.setCurrentMillisSystem()
to restore the defaults.
以类似的方式,您可以移动系统时钟。使用调用DateTimeUtils.setCurrentMillisProvider(...)
来模拟测试在特定时间运行并DateTimeUtils.setCurrentMillisSystem()
恢复默认值的规则。
Note: Your provider will need a way to make the clock tick or all new DateTime
instances will have the same value. I often advance the value by a millisecond each time getMillis()
is called.
注意:您的提供者需要一种方法来使时钟滴答作响,否则所有新DateTime
实例都将具有相同的值。每次getMillis()
调用时,我经常将值提前一毫秒。
Note 2: That only works with joda-time. It doesn't affect new java.util.Date()
.
注 2:这仅适用于 joda-time。它不影响new java.util.Date()
。
Note 3: You can't run these tests in parallel anymore. They must run in sequence or one of them will most likely restore the default timezone while another test is running.
注意 3:您不能再并行运行这些测试。它们必须按顺序运行,否则其中一个很可能会在另一个测试运行时恢复默认时区。
回答by Ilya
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
}
}
Change for single test
单次测试变更
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()
{
//...
}