時間に依存するコードをテストするためにJava System.currentTimeMillisをオーバーライドする


129

System.currentTimeMillisホストマシンのシステムクロックを手動で変更する以外に、コードまたはJVM引数を使用して、で表示される現在の時刻を上書きする方法はありますか?

少し背景:

現在の日付(つまり、月の1日、年の1日など)を中心にロジックの多くを実行する多くの会計ジョブを実行するシステムがあります。

残念ながら、従来のコードの多くは、new Date()またはなどの関数を呼び出しますがCalendar.getInstance()、どちらも最終的にはに呼び出されSystem.currentTimeMillisます。

テストの目的で、今のところ、システムクロックを手動で更新して、テストが実行されているとコードが判断する日時を操作する必要があります。

だから私の質問は:

によって返されるものをオーバーライドする方法はありますかSystem.currentTimeMillis?たとえば、そのメソッドから戻る前に、JVMにオフセットを自動的に加算または減算するように指示するには、

前もって感謝します!


1
私はそれはもう、関連するのかどうか分かりませんが、AspectJのでこれを達成するための別の方法がある、で私の答えを参照してください。stackoverflow.com/questions/18239859/...
NándorエロッドFekete

@NándorElődFeketeリンク内のソリューションは興味深いですが、コードを再コンパイルする必要があります。彼がレガシーコードを扱っていると主張しているという事実を考えると、元のポスターには再コンパイルする能力があるのだろうか。
cleberz 2017

1
@cleberz AspectJの優れたプロパティの1つは、バイトコードを直接操作することです。そのため、元のソースコードを使用する必要はありません。
NándorエロッドFekete

@NándorElődFeketeヒントをありがとうございます。私はaspectJバイトコードレベルのインストルメンテーション(特にJDKクラスのインストルメンテーション)を知りませんでした。少し時間がかかりましたが、rt.jarのコンパイル時のウィービングと、jdk以外のクラスのロード時のウィービングの両方が必要であることを理解できました(システムをオーバーライドします)。 currentTimeMillis()およびSystem.nanoTime())。
クレバーズ2017

@cleberz JREクラス織り込むための別の答えがあります。それもチェックしてください。
NándorエロッドFekete

回答:


115

システムクロックをいじる代わりに、弾丸をかじってレガシーコードをリファクタリングして、交換可能なクロックを使用することを強くお勧めします。理想的には、依存性注入を使用してこれを行う必要がありますが、交換可能なシングルトンを使用した場合でも、テストしやすくなります。

これは、シングルトンバージョンの検索と置換でほぼ自動化できます。

  • 交換してくださいCalendar.getInstance()Clock.getInstance().getCalendarInstance()
  • 交換するnew Date()Clock.getInstance().newDate()
  • 交換するSystem.currentTimeMillis()Clock.getInstance().currentTimeMillis()

(必要に応じて)

最初のステップを実行したら、シングルトンを少しずつDIに置き換えることができます。


50
テスト可能性を高めるためにすべての潜在的なAPIを抽象化またはラップしてコードを乱雑にすることは、私見ではあまり良い考えではありません。単純な検索と置換でリファクタリングを1回実行できたとしても、コードの読み取り、理解、維持がはるかに困難になります。いくつかのモックフレームワークは、System.currentTimeMillisの動作を変更できる必要があります。変更できない場合は、AOPまたは自作のインストルメンテーションを使用する方が適切です。
jarnbjo 2010年

21
@jarnbjo:ええと、もちろんあなたの意見は大歓迎ですが、これはかなり確立された手法であるIMOであり、私は確かにそれを大きな効果で使用しました。これは、テスト容易性を向上させるだけでなく、システム時間への依存を明示的にするため、コードの概要を取得するときに役立ちます。
Jon Skeet、2010年

4
@ virgo47:同意しないことに同意する必要があると思います。「依存関係を明示的に示す」ことはコードを汚染するとは思わない-コードをより明確にする自然な方法だと思う。また、静的呼び出しを介してこれらの依存関係を不必要に非表示にすることも「完全に許容できる」とは見なしません。
Jon Skeet

26
更新 Java 8に組み込まれた新しいjava.timeパッケージには、java.time.Clock「必要に応じて代替クロックをプラグインできるようにする」クラスが含まれています。
バジルブルク2014年

7
この答えは、DIがすべて良いことを意味します。個人的には、これをうまく利用したアプリケーションはまだ現実の世界で見たことがありません。代わりに、無意味な個別のインターフェース、ステートレスな「オブジェクト」、データのみの「オブジェクト」、および凝集度の低いクラスがたくさんあります。IMO、シンプル、およびオブジェクト指向の設計(真のステートフルオブジェクトを使用)は、より良い選択です。staticメソッドに関しては、オブジェクト指向ではないことを確認してください。ただし、インスタンスがどのインスタンス状態にも作用しない状態のない依存関係を注入することは、実際には優れていません。それはとにかく効果的に「静的」な振る舞いを偽装する空想的な方法にすぎません。
ホジェリオ

78

tl; dr

ホストマシンのシステムクロックを手動で変更する以外に、System.currentTimeMillisを介して提示されるように、コードまたはJVM引数を使用して現在時刻を上書きする方法はありますか?

はい。

Instant.now( 
    Clock.fixed( 
        Instant.parse( "2016-01-23T12:34:56Z"), ZoneOffset.UTC
    )
)

Clock java.time内

プラグイン可能なクロックの交換の問題に対する新しい解決策があり、偽の日時値でのテストを容易にします。Java 8java.timeパッケージには、明確な目的を持つ抽象クラスが含まれていますjava.time.Clock

必要に応じて代替クロックを接続できるようにする

独自のの実装をプラグインすることもできClockますが、ニーズを満たすためにすでに作成されている実装を見つけることができます。便宜上、java.timeには特別な実装を生成する静的メソッドが含まれています。これらの代替実装は、テスト中に価値があります。

ケイデンスの変更

さまざまな tick…方法で、現在の瞬間を異なるリズムで増加させる時計が生成されます。

デフォルトでは、Clock頻繁に更新されて、時間報告ミリ秒として罰金としてのJava 8とJava 9でナノ秒 (ハードウェアに依存します)。実際の現在の瞬間を異なる粒度で報告するように要求できます。

偽時計

一部のクロックは嘘をつき、ホストOSのハードウェアクロックとは異なる結果を生成する可能性があります。

  • fixed -単一の変化しない(増分しない)瞬間を現在の瞬間として報告します。
  • offset-現在の瞬間を報告しますが、渡されたDuration引数によってシフトされます。

たとえば、今年の最初のクリスマスの最初の瞬間に固定します。つまり、サンタとトナカイが最初に立ち寄ったときです。早い時間帯は、今日のようですPacific/Kiritimati+14:00

LocalDate ld = LocalDate.now( ZoneId.of( "America/Montreal" ) );
LocalDate xmasThisYear = MonthDay.of( Month.DECEMBER , 25 ).atYear( ld.getYear() );
ZoneId earliestXmasZone = ZoneId.of( "Pacific/Kiritimati" ) ;
ZonedDateTime zdtEarliestXmasThisYear = xmasThisYear.atStartOfDay( earliestXmasZone );
Instant instantEarliestXmasThisYear = zdtEarliestXmasThisYear.toInstant();
Clock clockEarliestXmasThisYear = Clock.fixed( instantEarliestXmasThisYear , earliestXmasZone );

その特別な固定クロックを使用して、常に同じ瞬間を返します。私たちは、クリスマスの日の最初の瞬間を取得するクリスマス島 UTCを示すとともに、壁時計時間 12月24日の前の日に14時間前、午前10時のを。

Instant instant = Instant.now( clockEarliestXmasThisYear );
ZonedDateTime zdt = ZonedDateTime.now( clockEarliestXmasThisYear );

instant.toString():2016-12-24T10:00:00Z

zdt.toString():2016-12-25T00:00 + 14:00 [太平洋/キリティマティ]

IdeOne.comのライブコードを参照してください。

実際の時間、異なるタイムゾーン

Clock実装によって割り当てられるタイムゾーンを制御できます。これは、一部のテストで役立つ場合があります。ただし、これは、常にオプションZoneIdまたはZoneOffset引数を明示的に指定する必要がある本番用コードではお勧めしません。

UTCをデフォルトゾーンとして指定できます。

ZonedDateTime zdtClockSystemUTC = ZonedDateTime.now ( Clock.systemUTC () );

特定のタイムゾーンを指定できます。指定適切なタイムゾーン名をの形式でcontinent/region、のようなAmerica/MontrealAfrica/CasablancaまたはPacific/Auckland。決してなど3-4文字の略称を使用していないESTか、ISTそのままではない真の時間帯ではなく、標準化、さらには一意ではありません(!)。

ZonedDateTime zdtClockSystem = ZonedDateTime.now ( Clock.system ( ZoneId.of ( "America/Montreal" ) ) );

JVMの現在のデフォルトのタイムゾーンを特定のClockオブジェクトのデフォルトにする必要があることを指定できます。

ZonedDateTime zdtClockSystemDefaultZone = ZonedDateTime.now ( Clock.systemDefaultZone () );

このコードを実行して比較します。それらはすべて同じ瞬間、タイムライン上の同じポイントを報告することに注意してください。それらは壁時計時間のみが異なります。つまり、同じことを言う3つの方法、同じ瞬間を表示する3つの方法です。

System.out.println ( "zdtClockSystemUTC.toString(): " + zdtClockSystemUTC );
System.out.println ( "zdtClockSystem.toString(): " + zdtClockSystem );
System.out.println ( "zdtClockSystemDefaultZone.toString(): " + zdtClockSystemDefaultZone );

America/Los_Angeles このコードを実行したコンピューター上のJVMの現在のデフォルトゾーンでした。

zdtClockSystemUTC.toString():2016-12-31T20:52:39.688Z

zdtClockSystem.toString():2016-12-31T15:52:39.750-05:00 [アメリカ/モントリオール]

zdtClockSystemDefaultZone.toString():2016-12-31T12:52:39.762-08:00 [America / Los_Angeles]

Instantクラスが定義によってUTCに常にあります。したがって、これらの3つのゾーン関連のClock使用法はまったく同じ効果があります。

Instant instantClockSystemUTC = Instant.now ( Clock.systemUTC () );
Instant instantClockSystem = Instant.now ( Clock.system ( ZoneId.of ( "America/Montreal" ) ) );
Instant instantClockSystemDefaultZone = Instant.now ( Clock.systemDefaultZone () );

instantClockSystemUTC.toString():2016-12-31T20:52:39.763Z

instantClockSystem.toString():2016-12-31T20:52:39.763Z

instantClockSystemDefaultZone.toString():2016-12-31T20:52:39.763Z

デフォルトの時計

のデフォルトで使用される実装Instant.nowは、によって返されるものClock.systemUTC()です。これは、を指定しない場合に使用される実装ですClock。のプレリリースJava 9ソースコードをご確認くださいInstant.now

public static Instant now() {
    return Clock.systemUTC().instant();
}

デフォルトClockOffsetDateTime.nowZonedDateTime.nowですClock.systemDefaultZone()ソースコードを参照してください。

public static ZonedDateTime now() {
    return now(Clock.systemDefaultZone());
}

Java 8とJava 9の間でデフォルト実装の動作が変更されました。Java8では、ナノ秒の分解能を格納するクラスの機能にもかかわらず、現在の瞬間はミリ秒単位の分解能でキャプチャされます。Java 9は、ナノ秒の分解能で現在の瞬間をキャプチャできる新しい実装をもたらします。もちろん、コンピューターのハードウェアクロックの機能によって異なります。


java.timeについて

java.timeのフレームワークは、Java 8に組み込まれており、後にされています。これらのクラスは面倒古い取って代わるレガシーのような日付時刻クラスをjava.util.DateCalendar、& SimpleDateFormat

ジョダタイムプロジェクトは、今でメンテナンスモードへの移行をアドバイスjava.timeのクラス。

詳細については、Oracleチュートリアルを参照してください。スタックオーバーフローで多くの例と説明を検索してください。仕様はJSR 310です。

java.timeオブジェクトをデータベースと直接交換することができます。JDBC 4.2以降に準拠したJDBCドライバーを使用します。文字列もクラスも必要ありません。java.sql.*

java.timeクラスはどこで入手できますか?

  • Java SE 8 Java SE 9以降
    • 内蔵。
    • 実装がバンドルされた標準Java APIの一部。
    • Java 9では、いくつかのマイナーな機能と修正が追加されています。
  • Java SE 6および Java SE 7
    • java.time機能の多くはThreeTen-BackportでJava 6&7にバックポートされています。
  • アンドロイド
    • Androidの新しいバージョンには、java.timeクラスの実装がバンドルされています。
    • 以前のAndroid(<26)では、ThreeTenABPプロジェクトはThreeTen-Backport(上記)を採用しています。ThreeTenABPの使用方法…を参照してください。

ThreeTen-エクストラプロジェクトでは、追加のクラスでjava.timeを拡張します。このプロジェクトは、java.timeに将来追加される可能性があることを証明する場です。あなたはここにいくつかの有用なクラスのような見つけることがIntervalYearWeekYearQuarter、および多くを


これが正解です
bharal

42

ジョン・スキートが言ったように:

「Joda Timeを使用する」は、「java.util.Date/Calendarを使用してXを実現するにはどうすればよいですか?」に関する質問への最良の回答です。

だからここに行く(想定あなただけのすべてを交換してきたあなたnew Date()new DateTime().toDate()

//Change to specific time
DateTimeUtils.setCurrentMillisFixed(millis);
//or set the clock to be a difference from system time
DateTimeUtils.setCurrentMillisOffset(millis);
//Reset to system time
DateTimeUtils.setCurrentMillisSystem();

インターフェースを持つライブラリーをインポートしたい場合(以下のJonのコメントを参照)、Prevayler's Clockを使用するだけで、標準インターフェースだけでなく実装も提供できます。完全なjarファイルは96kBしかないので、破綻しないはずです...


13
いいえ、私はこれをお勧めしません-それはあなたを純粋に交換可能な時計に向かわせません。それはあなたが全体を通して静力学を使用し続けます。もちろん、Joda Timeを使用するのは良い考えですが、それでもClockインターフェースなどを使いたいと思います。
Jon Skeet、2010年

@ジョン:True-テストの場合は問題ありませんが、インターフェースを用意することをお勧めします。
Stephen

5
@ジョン:これは、JodaTimeをすでに使用している時間依存コードをテストしたい場合、スムーズでIMHOの完璧で簡単な方法です。JodaTimeですべてが既に解決されている場合は、さらに別の抽象化レイヤー/フレームワークを導入してホイールを再発明しようとするのは不可能です。
Stefan Haberl 2012

2
@JonSkeet:これは、Mikeが元々求めていたものではありません。彼は時間依存のコードをテストするためのツールを望んでいました。アーキテクチャを必要に応じてできるだけ複雑に保つことを好みますが、できるだけ単純にしてください。ここに追加の抽象化レイヤー(つまりインターフェース)を導入する必要はありません
Stefan Haberl '25 / 07/25

2
@StefanHaberl:厳密に「必要」ではないものはたくさんありますが、実際に私が提案しているのは、既存の依存関係を明示的にして、単独でより簡単にテストできるようにすることです。staticを使用してシステム時間を偽装すると、staticの通常の問題がすべて発生します。これは、正当な理由のないグローバルな状態です。マイクは、現在の日付と時刻を使用するテスト可能なコードを作成する方法を求めていました。私の提案は、静的を効果的に回避するよりもはるかに明確である(そして依存関係を明確にする)と私はまだ信じています。
ジョンスキート

15

いくつかのDateFactoryパターンの使用は良いように見えますが、制御できないライブラリはカバーしていません-System.currentTimeMillisに依存する実装を持つValidationアノテーション@Pastを想像してください(そのようなものがあります)。

そのため、システム時刻を直接モックするためにjmockitを使用します。

import mockit.Mock;
import mockit.MockClass;
...
@MockClass(realClass = System.class)
public static class SystemMock {
    /**
     * Fake current time millis returns value modified by required offset.
     *
     * @return fake "current" millis
     */
    @Mock
    public static long currentTimeMillis() {
        return INIT_MILLIS + offset + millisSinceClassInit();
    }
}

Mockit.setUpMock(SystemMock.class);

元のモック化されていないミリ秒の値に到達することができないため、代わりにナノタイマーを使用します。これは壁時計とは関係ありませんが、ここでは相対時間で十分です。

// runs before the mock is applied
private static final long INIT_MILLIS = System.currentTimeMillis();
private static final long INIT_NANOS = System.nanoTime();

private static long millisSinceClassInit() {
    return (System.nanoTime() - INIT_NANOS) / 1000000;
}

文書化された問題があり、HotSpotを使用すると、何度も電話をかけると時間が正常に戻る-ここに問題レポートがあります:http : //code.google.com/p/jmockit/issues/detail?id=43

これを克服するには、特定のHotSpot最適化をオンにする必要があります-XX:-Inline。この引数を使用してJVMを実行します。

これは本番環境には完璧ではないかもしれませんが、特にDataFactoryがビジネスに意味がなく、テストのために導入された場合は特に、テストに対しては問題なく、アプリケーションに対しては完全に透過的です。組み込みのJVMオプションを別の時間に実行できると便利ですが、このようなハックなしでは不可能です。

完全な話は私のブログ投稿にあります:http : //virgo47.wordpress.com/2012/06/22/changing-system-time-in-java/

完全な便利なクラスSystemTimeShifterが投稿で提供されています。クラスはテストで使用できます。または、アプリケーション(またはappserver全体)を別の時間に実行するために、実際のメインクラスの前の最初のメインクラスとして非常に簡単に使用できます。もちろん、これは運用環境ではなく、主にテストを目的としています。

編集2014年7月:JMockitは最近大きく変更されており、これを正しく使用するにはJMockit 1.0を使用する必要があります(IIRC)。インターフェースが完全に異なる最新バージョンにアップグレードすることはできません。私は必要なものだけをインライン展開することを考えていましたが、新しいプロジェクトではこれを必要としないので、私はこれをまったく開発していません。


1
すべてのクラスの時間を変更しているという事実は、双方向です。たとえば、currentTimeMillisではなくnanoTimeをモックする場合は、java.util.concurrentを壊さないように非常に注意する必要があります。経験則として、サードパーティのライブラリをテストで使用するのが難しい場合は、ライブラリの入力をモックしないでください。ライブラリ自体をモックする必要があります。
Dan Berindei 2013年

1
他のモックを作成しないテストでは、この手法を使用します。これは統合テストであり、何らかのアクティビティが1年に初めて発生したときにスタック全体をテストするかどうかをテストします。nanoTimeの良い点は、幸運にも壁時計がないのでその1つを模擬する必要がないことです。まったく意味があります。タイムシフト機能を備えたJavaがすでに複数回必要であり、システム時刻をいじくっているJavaを見てみたいです。これは非常に不運なためです。
virgo47 2013

7

Powermockはうまく機能します。ちょうどそれをモックするために使用しましたSystem.currentTimeMillis()


私はPowermockを使用しようとしましたが、正しく理解していれば、呼び出し先側を実際に模倣することはできません。代わりに、すべての発信者を検索し、モックを適用します。アプリケーションがシフトされた時間で本当に確実に動作したい場合は、クラスパスのすべてのクラスをチェックする必要があるため、これは非常に時間がかかる可能性があります。Powermockのモック方法が間違っている場合は修正してください。
virgo47 2013年

1
@ virgo47いいえ、あなたはそれが間違っていることを理解しました。PowerMockが「クラスパス内のすべてのクラスをチェックする」ことは決してありません。(@PrepareForTestテストクラスの注釈を通じて)チェックするように具体的に指示したクラスのみをチェックします。
ホジェリオ

1
@Rogério-それがまさに私が理解する方法です-私は「あなたはそれを許さなければならない...」と言いました。つまりSystem.currentTimeMillis、クラスパス(任意のlib)の任意の場所への呼び出しが必要な場合は、すべてのクラスをチェックする必要があります。それが私が意図したことであり、他には何もありません。重要なのは、その動作を発信者側で模倣することです(「具体的に言う」)。これは単純なテストでは問題ありませんが、どこからそのメソッドを呼び出すのかわからないテスト(たとえば、ライブラリが関係するより複雑なコンポーネントテスト)には適していません。これは、Powermockがまったく間違っているという意味ではなく、このタイプのテストに使用できないことを意味します。
virgo47

1
@ virgo47はい、あなたの意味がわかります。PowerMockがクラスパス内のすべてのクラスを自動的に検査する必要があることを意味していると思いましたが、これは明らかに馬鹿げています。ただし、実際には、単体テストでは通常、どのクラスが時間を読み取るかを知っているので、それを指定することは問題ではありません。実際、統合テストでは、PowerMockですべての使用クラスを指定する必要があることが問題になる場合があります。
ホジェリオ

6

Aspect-Oriented Programming(AOP、たとえばAspectJ)を使用してSystemクラスを作成し、テストケース内で設定できる事前定義の値を返します。

または、アプリケーションクラスを織り込んで、呼び出しを独自の別のユーティリティクラスに、System.currentTimeMillis()またはnew Date()別のユーティリティクラスにリダイレクトします。

java.lang.*ただし、システムクラスのウィービング()は少しトリッキーです。rt.jarに対してオフラインウィービングを実行し、テスト用に別のJDK / rt.jarを使用する必要がある場合があります。

これはバイナリウィービングと呼ばれ、システムクラスのウィービングを実行し、それに関するいくつかの問題を回避する特別なツールもあります(たとえば、VMのブートストラップが機能しない場合があります)。


もちろん、これは機能しますが、AOPツールよりもモックツールを使用する方がはるかに簡単です。後者の種類のツールは、問題の目的には一般的すぎます。
ホジェリオ

3

これをVMで直接実行する方法は実際にはありませんが、テストマシンのシステム時間をプログラムで設定する方法はすべてあります。ほとんど(すべて?)のOSには、これを行うコマンドラインコマンドがあります。


たとえば、Windowsではdateand timeコマンドです。Linuxではdateコマンド。
Jeremy Raymond

AOPやインストルメンテーションを使用してこれを実行できないのはなぜですか?
jarnbjo 2010年

3

EasyMockを使用し、Joda Timeを使用せず、PowerMockを使用せずに、Java 8 WebアプリケーションでJUnitテストの目的で現在のシステム時間を上書きするための有効な方法

これはあなたがする必要があることです:

テストしたクラスで行う必要があること

ステップ1

java.time.Clockテストしたクラスに新しい属性を追加MyServiceし、インスタンス化ブロックまたはコンストラクターを使用して、新しい属性がデフォルト値で適切に初期化されることを確認します。

import java.time.Clock;
import java.time.LocalDateTime;

public class MyService {
  // (...)
  private Clock clock;
  public Clock getClock() { return clock; }
  public void setClock(Clock newClock) { clock = newClock; }

  public void initDefaultClock() {
    setClock(
      Clock.system(
        Clock.systemDefaultZone().getZone() 
        // You can just as well use
        // java.util.TimeZone.getDefault().toZoneId() instead
      )
    );
  }
  { 
    initDefaultClock(); // initialisation in an instantiation block, but 
                        // it can be done in a constructor just as well
  }
  // (...)
}

ステップ2

clock現在の日時を呼び出すメソッドに新しい属性を挿入します。たとえば、私の場合、dataaseに保存された日付がの前LocalDateTime.now()に発生したかどうかのチェックを実行する必要がありましLocalDateTime.now(clock)た。

import java.time.Clock;
import java.time.LocalDateTime;

public class MyService {
  // (...)
  protected void doExecute() {
    LocalDateTime dateToBeCompared = someLogic.whichReturns().aDate().fromDB();
    while (dateToBeCompared.isBefore(LocalDateTime.now(clock))) {
      someOtherLogic();
    }
  }
  // (...) 
}

テストクラスで行う必要があること

ステップ3

テストクラスで、モッククロックオブジェクトを作成し、テスト済みメソッドを呼び出す直前にテスト済みクラスのインスタンスに挿入します。doExecute()その後、次のようにすぐにリセットします。

import java.time.Clock;
import java.time.LocalDateTime;
import java.time.OffsetDateTime;
import org.junit.Test;

public class MyServiceTest {
  // (...)
  private int year = 2017;
  private int month = 2;
  private int day = 3;

  @Test
  public void doExecuteTest() throws Exception {
    // (...) EasyMock stuff like mock(..), expect(..), replay(..) and whatnot

    MyService myService = new MyService();
    Clock mockClock =
      Clock.fixed(
        LocalDateTime.of(year, month, day, 0, 0).toInstant(OffsetDateTime.now().getOffset()),
        Clock.systemDefaultZone().getZone() // or java.util.TimeZone.getDefault().toZoneId()
      );
    myService.setClock(mockClock); // set it before calling the tested method

    myService.doExecute(); // calling tested method 

    myService.initDefaultClock(); // reset the clock to default right afterwards with our own previously created method

    // (...) remaining EasyMock stuff: verify(..) and assertEquals(..)
    }
  }

デバッグモードで確認すると、2017年2月3日の日付がmyServiceインスタンスに正しく挿入され、比較命令で使用され、で現在の日付に正しくリセットされていることがわかりますinitDefaultClock()


2

私の意見では、非侵襲的なソリューションしか機能しません。特に、外部ライブラリと大きなレガシーコードベースがある場合、時間を模擬する信頼できる方法はありません。

JMockit ...だけの限られた数のために働く

PowerMock&Co ... クライアントをSystem.currentTimeMillis()にモックする必要があります。再び侵略的なオプション。

このことから、言及されたjavaagentまたはaopアプローチがシステム全体に対して透過的であることがわかります。誰かがそれをして、そのような解決策を指摘できましたか?

@jarnbjo:javaagentコードの一部を見せていただけますか?


3
-XX:-Inlineハック(SunのHotSpot)で無制限の回数動作するjmockitソリューション:virgo47.wordpress.com/2012/06/22/changing-system-time-in-java完全な便利なクラスSystemTimeShifterが提供されますポストで。クラスはテストで使用できます。または、アプリケーション(またはappserver全体)を別の時間に実行するために、実際のメインクラスの前の最初のメインクラスとして非常に簡単に使用できます。もちろん、これは運用環境ではなく、主にテストを目的としています。
virgo47

2

Linuxを実行している場合は、libfaketimeのマスターブランチを使用するか、またはコミットのテスト時に使用できます。 4ce2835

環境変数を、Javaアプリケーションをモックしたい時刻に設定し、ld-preloadingを使用して実行します。

# bash
export FAKETIME="1985-10-26 01:21:00"
export DONT_FAKE_MONOTONIC=1
LD_PRELOAD=/usr/local/lib/faketime/libfaketimeMT.so.1 java -jar myapp.jar

2番目の環境変数は、Javaアプリケーションにとって最も重要です。執筆時にはlibfaketimeのマスターブランチが必要です。

systemdマネージドサービスの時間を変更したい場合は、ユニットファイルのオーバーライドに次のコードを追加するだけです。たとえば、elasticsearchの場合は次のようになります/etc/systemd/system/elasticsearch.service.d/override.conf

[Service]
Environment="FAKETIME=2017-10-31 23:00:00"
Environment="DONT_FAKE_MONOTONIC=1"
Environment="LD_PRELOAD=/usr/local/lib/faketime/libfaketimeMT.so.1"

`systemctl daemon-reloadを使用してsystemdをリロードすることを忘れないでください


-1

System.currentTimeMillis()引数を持つメソッドをモックしたい場合はanyLong()、引数としてマッチャークラスを渡すことができます。

PS上記のトリックを使用してテストケースを正常に実行し、PowerMockおよびMockitoフレームワークを使用しているテストの詳細を共有できます。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.