web-dev-qa-db-ja.com

日付/時刻ベースのコードがすべてのタイムゾーンでDSTの有無にかかわらず機能することを確認するために、単体テストを作成するにはどうすればよいですか?

私はJodaTime2.1を使用しており、日付/時刻操作を実行して、すべてのタイムゾーンで正常に動作し、 [〜#〜] dst [〜# 〜]

具体的には:

  1. システムクロックをモックするにはどうすればよいですか(現在の時刻を取得するためにnew DateTime()を呼び出すすべての場所をモックする必要はありません)
  2. デフォルトのタイムゾーンで同じことをするにはどうすればよいですか?
22
Aaron Digulla

これには_@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( "..." );
}
_

テストケースは継承されるので、それだけです。ルールを定義するだけです。

同様の方法で、システムクロックをシフトできます。 DateTimeUtils.setCurrentMillisProvider(...)を呼び出してテストが特定の時間に実行されることをシミュレートし、DateTimeUtils.setCurrentMillisSystem()を呼び出してデフォルトに戻すルールを使用します。

注:プロバイダーには、時計を刻む方法が必要です。そうしないと、すべての新しいDateTimeインスタンスの値が同じになります。 getMillis()が呼び出されるたびに、値をミリ秒ずつ進めることがよくあります。

注2:これはjoda-timeでのみ機能します。 new Java.util.Date()には影響しません。

注3:これらのテストを並行して実行することはできなくなりました。それらは順番に実行する必要があります。そうしないと、別のテストの実行中に、そのうちの1つがデフォルトのタイムゾーンに戻る可能性があります。

27
Aaron Digulla
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()
{
//...
}
2
Ilya