はじめに

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コールバックを全て実行してくれ(るんだと思い:sweat_smile: )ます。

ちなみに、非同期コールバックは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() が変わっている
}));

参考リンク

  1. YouTubeは自動翻訳字幕出せるようになってた。素敵!