今週は第63回を開催しました!
引き続きKotlin Hands-onをすすめています。
前回の様子はコチラ↓
[第63回の様子]
2021/5/12に第63回を開催した。今週もいつもどおり水曜日開催。
バタバタしていてブログが遅くなってしまった……。
GW明けでみんな来てくれるかなーと思ってたけど、常連のメンバーが揃った。
参加メンバーは自分をいれて4名。
自分はナビゲータ役で参加してブツブツ言っていた。
勉強会本編の内容としては、ひき続きIntroduction to Coroutines and Channelsハンズオンを進めた。第9章Testing coroutines
をタスクの直前まで進めた。
来週でcoroutineハンズオンも終わりかな!その次、何やろう...。
[学んだことや疑問点]
- Introduction to Coroutines and Channels: 9. Testing Coroutines
- GitHubServiceをモックしたcoroutineのテストケースがリポジトリにあるが、以下の問題がある
- 問題1:指定された秒数だけ待つのでテスト実行時間が長い
- 問題2:コードの実行時間は環境によって異なるので実際の実行時間に依存するのはよくない
- よりよい方法として、特別なフレームワークを利用することもできるが、それを学びセットアップするのは複雑である
- やりたいことはシンプル:coroutineを使ったらsuspend関数よりも速くなることの確認、channelを使うとさらに速くなることの確認。
- 仮想時間virtual timeの利用(特別なテスト用のcoroutine dispatcherを使う必要がある):delay()関数を呼び出すとすぐに後続処理が実行されるが、仮想時間は指定された時間だけ進められる
- これにより、実際のテストの実行時間は劇的に削減される(問題1が解消する)
- 仮想時間を利用するにはrunBlocking()をrunBlockingTest()に変更するだけ
- runBlockingTest()を利用するとスコープがTestCoroutineScopeになる:このスコープ中のsuspend関数でdelay()を呼び出すと仮想時間を進めることができる
- TestCoroutineScopeのcurrentTimeプロパティで現在時刻を取得することができる:以下の例を参照
@Test fun testDelayInSuspend() = runBlockingTest { val realStartTime = System.currentTimeMillis() val virtualStartTime = currentTime foo() println("${System.currentTimeMillis() - realStartTime} ms") // ~ 6 ms println("${currentTime - virtualStartTime} ms") // 1000 ms } suspend fun foo() { delay(1000) // auto-advances without delay println("foo") // executes eagerly when foo() is called }
- 仮想時間は厳密にdelay()した分だけ進められるので、仮想時間上で上記のコードの実行時間は1000msになる
- 子供のcoroutineでも仮想時間を利用したければ、同じTestCoroutineDispatcherを利用する必要がある:launch時のコンテクストでDispatchers.Defaultを指定すると仮想時間は利用できなくなる
- 注意:仮想時間の仕組みは実験的なものであり、将来的に変更される可能性がある
- デフォルトではコンパイラの警告がでるので、警告を表示したくない場合は
@OptIn(ExperimentalCoroutinesApi::class)
をつける必要がある - また、コンパイル時のオプションに以下を追加する必要がある
compileTestKotlin { kotlinOptions { freeCompilerArgs += "-Xuse-experimental=kotlin.Experimental" } }
[まとめ]
モブプログラミング・スタイルで、Introduction to Coroutines and Channelsハンズオンを進めた。
テストコードの謎が解明されてすっきり!仮想時間の使い方を理解できた。
今週はプルリクエストなし。