Closed22

(Deprecated) Advanced Android in Kotlin 05.1:Testing Basics

どっことどっこと

とりあえず、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

何者じゃい...

どっことどっこと

これか...?
https://qiita.com/yuta-10112022/items/def2df90f8dd19101e26

内容的に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出している人がいた!素敵!
https://github.com/google-developer-training/advanced-android-testing/pull/298

ただ、残念なことにこのcodelab自体がdeprecated なこと、リポジトリがreadonlyになっていることから、それ以降の進展はされていなかった。

その人のリポジトリ・ブランチは以下。
https://github.com/Dmitriy1892/advanced-android-testing/tree/updating/starter_code

うおーうごいたー!

どっことどっこと

6. Task: Writing your first test まで手を動かした。
https://developer.android.com/codelabs/advanced-android-kotlin-training-testing-basics#5

タイトル通り、既存の実装をベースにテストコードを書いてみましょう!という感じだった。
テストコードを初めて書く人には有用だけど、この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
https://developer.android.com/codelabs/advanced-android-kotlin-training-testing-basics#7

お、 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
https://developer.android.com/codelabs/advanced-android-kotlin-training-testing-basics#8

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な内容から徐々に難しくしていく具合だと取り組みやすいなあという印象。

このスクラップは4ヶ月前にクローズされました