🍥
非同期処理の結果だけでなく内部状態もテストする(Unity/UniTask)
Unity: 2021.2.8
Test Framework: 2.0.1-pre18
UniTask: 2.2.5
非同期処理を実施している最中の挙動を検証したい
長めの非同期処理を書いていると、テストコードで非同期処理を await
している最中にも検証を走らせたい場合があるのではないでしょうか。
例えばカウントダウンする処理があるとします。
public async UniTask Countdown3To0Async(Text text) {
text.text = "3";
await UniTask.Delay(1000);
text.text = "2";
await UniTask.Delay(1000);
text.text = "1";
await UniTask.Delay(1000);
text.text = "0";
}
内部で Text
をいじっていますので、ちゃんと 3 2 1 となっているかテストしたいとします。
[Test]
// Test Framewor 2.0 以降だと Task のテストが書ける (記事投稿時点ではpreview)
public async Task カウントダウンが文字に反映されるかテスト() {
var text = new GameObject().AddComponent<Text>();
await Countdown3To0Async(text);
// ここに来るのはカウントダウンがすべて終わった後
}
しかし、await
するとカウントダウンが終わる3秒後に検証することになり、1秒ごとの Text
の状態を検証することはできません。
UniTask.WhenAll
で検証を同時に走らせる
そこで、実行と評価を同時並行させるため、 UniTask.WhenAll
を使います。
[Test]
public async Task カウントダウンが文字に反映されるかテスト() {
var text = new GameObject().AddComponent<Text>();
// UniTask.Deferで評価処理を作成
var assertAsync = UniTask.Defer(async () => {
await UniTask.Yield(); // 評価を1フレーム後にずらす
Assert.That(text.text, Is.EqualTo("3"));
await UniTask.Delay(1000);
Assert.That(text.text, Is.EqualTo("2"));
await UniTask.Delay(1000);
Assert.That(text.text, Is.EqualTo("1"));
});
// UniTask.WhenAll で実行と評価を並列に実施
await UniTask.WhenAll(Countdown3To0Async(text), assertAsync);
Assert.That(text.text, Is.EqualTo("0"));
}
これで、await
と平行してテストコードで検証を行うことができます
もっといい書き方知ってる人いないですか?
内部状態の検証はできるようになるので解決ですが、テストコードの流れが前後するのでこの書き方は好きではないです。コードは上から下に記述するべきという考えに則ると、こんな風に書きたい。
[Test]
public async Task カウントダウンが文字に反映されるかテスト() {
var text = new GameObject().AddComponent<Text>();
// UniTask.WhenAll で実行と評価を並列に実施
await Countdown3To0Async(text).なんかいい感じの拡張メソッド(async () => {
Assert.That(text.text, Is.EqualTo("3"));
await UniTask.Delay(1000);
Assert.That(text.text, Is.EqualTo("2"));
await UniTask.Delay(1000);
Assert.That(text.text, Is.EqualTo("1"));
});
Assert.That(text.text, Is.EqualTo("0"));
}
Discussion