[Angular] テストレシピ - fakeAsyncの使い処
はじめに
Angularのテストの中で、以下のような記述を見かけたことはないでしょうか?
it('世界に挨拶します (async)', fakeAsync(() => {
// テストコード
}));
ここでは何が起きているのか?
どんなときにこのように書けばよいのか?
すでに解説記事がいくつもありますが、自分なりにまとめてみます。
fakeAsyncを使うと何が起きるの?
マニュアル には、このように書かれています。
Wraps a function to be executed in the fakeAsync zone:
- microtasks are manually executed by calling flushMicrotasks(),
- timers are synchronous, tick() simulates the asynchronous passage of time.
簡単に訳すと、
fakeAsyncゾーンの中で実行するように関数をラップします。
- microtaskは、flushMicrotasks()を呼ぶことで手動実行されます。
- タイマーは同期します。tick() を使うと、非同期な時間待ちをシミュレートできます。
なんのことやらですね。
fakeAsyncゾーンとは?
「fakeAsyncゾーン」というのは、「fakeAsync」という名前の「ゾーン」です。
「ゾーン」というのは、実行コンテキストのようなものだそうです。
ゾーンを解説するプレゼン動画 → https://www.youtube.com/watch?v=3IqtmUscE_U&t=150 1
何をしてくれるかというと、非同期コールバックをプロキシ化して、AOPできるようにしたようなものです。
非同期コールバックが複数箇所にあったとして、呼び出し前に実行コンテキストを整えて、非同期コールバック間で情報を共有できるようにしてくれます。
どのように実現しているかというと、setTimeout
やらなんやらにモンキーパッチをあてているそうです。
microtaskとは?
Zone.jsのドキュメントによると、microtaskというのは、Promise.Thenに渡される非同期コールバックのことです。
つまり、flushMicrotasks()
を呼ぶと、キューに溜まったPromise.Thenコールバックを全て実行してくれ(るんだと思い )ます。
ちなみに、非同期コールバックはmicrotaskとあわせて3つに分類されます。
- MicroTask
- EventTask
- MacroTask
tick()
は?
tick()
は簡単です。引数に指定したミリ秒時間待ってくれるだけです。
fakeAsync使うと美味しいの?
おけ、テストをラップしてくれるのはわかった。
どんなシーンで使うと嬉しいでしょうか。
解決しないといけないPromiseがあるとき
Promiseの解決結果がテストに影響するのであれば、fakeAsyncで囲みます。
例えば、Router.navigateByUrl()
を呼び出しているクラスをテストするときに使います。
it('xxxしたら、oooへ遷移', fakeAsync(() => {
// Given
const location = TestBed.get(Location);
// When
instance.xxx(); // この中で、navigateByUrl を使っている
flushMicrotasks(); // 溜まっているPromiseを強制解決
// Then
expect(location.path()).toBe('ooo'); // location.path() が変わっている
}));
参考リンク
- Angular 公式ガイド
- AngularとZone.jsとテストの話
- async awaitでキレイなコードが書けないと辛い気持ちになってたところ、zone.jsを使ったらいろいろと解決できそうなことが分かった
- Angular: Testing async stuff in the fakedAsync zone VS. providing custom schedulers
- Angular fakeAsyncTest 使い方の纏め
- Implements Zones for JavaScript
-
YouTubeは自動翻訳字幕出せるようになってた。素敵! ↩