[2023年4月版]Awaitilityで非同期のJUnitテスト
JUnit で非同期のテストしたい時に
JUnitのテスト書いてて非同期のテストしたいって時に使えるパッケージが Awaitility です。
- Github: https://github.com/awaitility/awaitility
- Getting Started: https://github.com/awaitility/awaitility/wiki/Getting_started
- Document: https://github.com/awaitility/awaitility/wiki/Usage
- Javadoc: https://www.javadoc.io/doc/org.awaitility/awaitility/latest/index.html
準備
Maven には以下のように依存関係を追加します。
<dependencies>
...
<dependency>
<groupId>org.awaitility</groupId>
<artifactId>awaitility</artifactId>
<version>4.2.0</version>
<scope>test</scope>
</dependency>
...
</dependencies>
Gradle には以下のように依存関係を追加します。
dependencies {
...
testImplementation 'org.awaitility:awaitility:4.2.0'
}
テストケースの作成
使い方ですが、自分はちょっと混乱しました。(なので記事書いてますが)
まず、import するべきパッケージですが、ドキュメントには以下のように記載されてます。
必須なのは以下、
- org.awaitility.Awaitility.*;
で、追加で以下のパッケージも import しとくといいよ~とのことですが…
java.time.Duration.*
java.util.concurrent.TimeUnit.*
org.hamcrest.Matchers.*
org.junit.Assert.*
java.time.Duration.*
や java.util.concurrent.TimeUnit.*
は時間指定の際に必要なので適宜、import するとして、
org.hamcrest.Matchers.*
と org.junit.Assert.*
ですが、これ JUnit4 ではOKですが、JUnit5 では、
import org.junit.jupiter.api.Test;
import static org.hamcrest.CoreMatchers.*;
import static org.junit.jupiter.api.Assertions.*;
になるかと~。
いや、ぶっちゃけJUnit5がアレなんですが。まぁ、いいけどさ…
つづいて、テストケースのサンプルです。
もっとも基本的ですべてを物語るケースが以下です。
@Test
public void testAwaitility() {
await().until(() -> true);
}
シンプルに "true
になるまで待つ" ってだけなんですが…
until
の引数が評価関数というか Lambda なのがミソです。
デフォルトのタイムアウトは10秒です。
シンプルすぎて混乱したのがここで、true
になるまで待つのはその通りですが、戻り値が true
であればなんでもOKなので、
@Test
public void testAwaitility2() {
await().until(() -> "test".equals("test"));
}
こんなんでもOKなんですね。
以下のアサーションと分離するタイプも書けるので上記と混乱してしまいました。
@Test
public void testAwaitility3() {
await().until(() -> "test", equalTo("test"));
}
この場合は、何かを返す関数はその時の状態を返せばよくて、アサーションが成功するまで待ってくれると、そういうことなんですね。
かならず true
を返す関数にしなくてはいけないと思ってしまって意味が分かりませんでした。
続いてAtomic使ってタイマーに書き込ませる処理、ちゃんと非同期してみましょう。
@Test
public void testAtomicInteger() {
var atomic = new AtomicInteger(0);
var timer = new Timer();
timer.schedule( new TimerTask() {
public void run() {
atomic.set(10);
}
} , 5000);
await().untilAtomic(atomic, equalTo(10));
}
こんな感じで、遅れて10
になるAtomicInteger
もちゃんとアサーションできます。(ドヤぁぁぁぁ!!!)
ついでに、Timerをキャンセルすればちゃんと?失敗します。
@Test
public void testAtomicInteger() {
var atomic = new AtomicInteger(0);
var timer = new Timer();
timer.schedule( new TimerTask() {
public void run() {
atomic.set(10);
}
} , 5000);
// 実行をキャンセル
timer.cancel();
// これはタイムアウトで失敗
await().untilAtomic(atomic, equalTo(10));
}
AtomicBoolean
はショートカットもあります。
@Test
public void testAtomicBoolean() {
var atomic = new AtomicBoolean(false);
new Timer().schedule( new TimerTask() {
public void run() {
atomic.set(true);
}
} , 5000);
await().untilTrue(atomic);
}
バッチリですね!
そのほか、詳しい使い方もドキュメントにしっかり記載されているので、よく読んでおきます…
おしまい
さて、今夜はこんな感じです。
なにかと非同期でのテスト、役立つのではないでしょうか。
お疲れさまでした。
ところで TimerTask はラムダ式使えないのかい。。。
Discussion