(Deprecated) Advanced Android in Kotlin 05.1:Testing Basics
Deprecatedにされていたので、早めに実施する。
とりあえず、gradleが古そうなので更新しないといけなそうだな
わーgradleをアップデートしてリビルドしてもエラーがでるー
e: java.lang.IllegalAccessError: superclass access check failed: class org.jetbrains.kotlin.kapt3.base.javac.KaptJavaCompiler (in unnamed module @0x68387653) cannot access class com.sun.tools.javac.main.JavaCompiler (in module jdk.compiler) because module jdk.compiler does not export com.sun.tools.javac.main to unnamed module @0x68387653
何者じゃい...
これか...?
内容的にJDKやライブラリのバージョンに原因があると思い。1つ1つコンパイルして確認しましたが、どれも違っており原因はKotlinのバージョンが1.9.0だったことが原因でした。
1.9.0 -> 1.9.21に変更することで無事コンパイルできました。
Kotlin1.9.0で必ずこのエラーが起きるわけでは無いことと、Kotlin version を変えたら毎回Clrean Projectしなければいけなくて解決に時間がかかりました。
ほう...こちらのバージョンは
ext.kotlinVersion = '1.6.21'
なので、1.6.21なのかな。ちょっと試してみよう。
エラーの内容が変わった
> Inconsistent JVM-target compatibility detected for tasks 'compileDebugJavaWithJavac' (1.8) and 'kaptGenerateStubsDebugKotlin' (21).
はいはい。JVM,JVMっと。
ローカル環境のJAVAが 17 なのでこちらに合わせる形で修正してみよう
android におまじないを追加
compileOptions {
sourceCompatibility JavaVersion.VERSION_17
targetCompatibility JavaVersion.VERSION_17
}
kotlinOptions {
jvmTarget = "17"
}
今度はKaptで怒られた。
> A failure occurred while executing org.jetbrains.kotlin.gradle.internal.KaptWithoutKotlincTask$KaptExecutionWorkAction
> java.lang.reflect.InvocationTargetException (no error message)
なにもんじゃい
おそらくプロジェクト内で使っている Room 関連のアノテーションかな。
おそらくTaskクラスのコンストラクタがいい感じに認識されていないせいで、その辺を扱っているところが軒並み怒られているような気がする。
JVM直してもあとからあとからエラーがでて心萎えた
で、誰かこの状況を改善していないかなぁと思ってテストコードを管理しているリポジトリを漁ったら
素敵なことに「直したからこれマージしてくれ!」とPR出している人がいた!素敵!
ただ、残念なことにこのcodelab自体がdeprecated なこと、リポジトリがreadonlyになっていることから、それ以降の進展はされていなかった。
その人のリポジトリ・ブランチは以下。
うおーうごいたー!
あー疲れたー
6. Task: Writing your first test
まで手を動かした。
タイトル通り、既存の実装をベースにテストコードを書いてみましょう!という感じだった。
テストコードを初めて書く人には有用だけど、このCodelabのタイトルAdvancedなんだけど...
まあなんでも最初の入りってやつがあるよね。
きっとこのあと涙を流して「さすがAdvancedや...」と考えることがあるんだろう。
7. Task: Writing more tests
The code for the getActiveAndCompletedStats as written has a bug.
お、そうだな。 listが 空だと「0で割るな💢」で例外吐くし、そもそもnullだとNPEだし。
テストを書かせるコードだから仕方ないけど、「それなら引数をnullableにするな💢」と思ってしまうのは心に留めておく。(留まっていない)
To fix the code and write tests, you'll use test driven development.
テスト駆動開発の説明かな。
この辺はスキップしてOKそうなので、コピペで。
8. Task: Setting up a ViewModel Test with AndroidX Test
お、 ViewModel
のテストか。ええね。
Following the same steps you did for StatisticsUtilTest, in this step, you create a test file for TasksViewModelTest.
テストコードの作成方法は、通常のユニットテストと同じ感じで良さそう。
testImplementation "org.robolectric:robolectric:$robolectricVersion"
テストコードはRobolectric で動作させるのか...
robolectric の version catalogがなさそうだったので取り急ぎ以下を追加。
testImplementation("org.robolectric:robolectric:4.4")
で、ニョロが出るから add version catalog でいい感じになるっぽい
9. Task: Writing Assertions for LiveData
LiveData
に関するテストか。いいね。
To test LiveData it's recommended you do two things:
Use InstantTaskExecutorRule
Ensure LiveData observation
InstantTaskExecutorRule
なるRuleが出てきた。RuleはActivityを毎回起動するやつしか知らないから、個人的にはNewFaceだ。
this rule runs all Architecture Components-related background jobs in the same thread so that the test results happen synchronously, and in a repeatable order. When you write tests that include testing LiveData, use this rule!
アーキテクチャ コンポーネント関連のバックグラウンド ジョブをすべて同じスレッドで実行し、テスト結果が同期的に、かつ繰り返し可能な順序で得られるようにします。
めっちゃ便利やん。
dependencies
に追加してあげれば、他は気にせず利用できるのかな。
testImplementation "androidx.arch.core:core-testing:$archTestingVersion"
↑を入れたら、問題なく InstantTaskExecutorRule
にアクセスできるようになった。
ちなバージョンは 2.2.0
Step 2. Add the LiveDataTestUtil.kt Class
When you use LiveData, you commonly have an activity or fragment ( LifecycleOwner) observe the LiveData.
This poses a problem: in your TasksViewModel test, you don't have an activity or fragment to observe your LiveData.
お、そうだな。ユニットテストコードの宿命...
To get around this, you can use the observeForever method, which ensures the LiveData is constantly observed, without needing a LifecycleOwner. When you observeForever, you need to remember to remove your observer or risk an observer leak.
observeForever
というものを使うらしい。こいつはLifeCycleOwner
無しで LiveDataを常に監視するための関数らしいけど、ちゃんと削除?しないとリークするからね?とのこと。
テストコードを見るとたしかにobserverの制御関係なしに同スレッドvalueを確認できているので、これができるのがobserveForeverのおかげなのね。
ただ、このままいくとテストコードにはボイラープレート(似たような実装)がたくさん必要になるから、LiveDataTestUtilクラスを作って、ここに共通化してまとめちゃおうぜ!というのがStep2の趣旨ぽい。
確かに、このテストのためだけに15行程度の実装をして、LiveData使うところに全部これを仕込むのはかなり大変なので、それが二行に収まるなら共通化としての価値は十分にある。
と、書いてある通りに突き進んだところテストがパスされなくなった。
どうやらJavaのバージョンが原因ぽいが、もう頑張る気力がないのでここからは精読する程度で進める。
なお、↑でのリポジトリでは、コード完成系のブランチもあるようなのでそれを後ほど確認する。
10. Task: Writing multiple ViewModel tests
前半は9. のおさらいで自分でテストコードを実装してみる内容。
後半は @Before
を書けば、毎回 ViewModel
初期化する実装をしなくていいね!という内容。
という感じで以上かな。
Deprecatedにされてしまったのが非常に残念に思えるほど良いCodeLabだった。
Advancedとはいえ、いきなり超難易度をだされたら流石に困るのでまずはBasicな内容から徐々に難しくしていく具合だと取り組みやすいなあという印象。
メモ:
このCodelabの答え的なブランチ:/practice/unit_testing_with_android