Closed16

(Deprecated) Advanced Android in Kotlin 05.2: Introduction to Test Doubles and Dependency Injection

どっことどっこと

3. Concept: Testing Strategy
https://developer.android.com/codelabs/advanced-android-kotlin-training-testing-test-doubles/#2

テストコードを実装するにあたっての戦略的内容。
Unit Test Integration Test E2E Test のピラミッドは他のテストに関する記事でも頻繁に説明される内容だよね。そしてこれらのテストコードが適切に実装できるかどうかは、アプリのアーキテクチャにかかっているというのもよくある話。

  1. エンジニアは安心して修正・変更したい
  2. (でも動作確認は面倒)
  3. テストコードが書ける環境でいたい
    という経緯がアーキテクチャ(MVC,MVP,MVVM)やクリーンアーキテクチャの概念, TDDやDDDが話題に上がってきたひとつの要因と認識している。

End to end testing will be covered in the next lesson.

頼むで。

どっことどっこと

4. Task: Make a Fake Data Source
https://developer.android.com/codelabs/advanced-android-kotlin-training-testing-test-doubles/#3

Fakeなデータソースを用意して、そっちを参照させよう。という話。
少なくても Unit Test は外部との機能に依存させたくないので、Fake Mock を使ってテスト対象からは外部機能にアクセスしているけど、実際にはテスト用のデータを返すようにすることで、依存関係を気にせず本当にテストしたい対象だけをテストする、という狙い。
(これができるのはアーキテクチャ通りに実装できている、という前提があるんだけどね。)

どっことどっこと

一旦Fakeなデータソースを用意する方針で作業が進みそう。
(あー mockito 使いたい...)

どっことどっこと

5. Task: Write a Test Using Dependency Injection
https://developer.android.com/codelabs/advanced-android-kotlin-training-testing-test-doubles/#4

ここからは、前の工程で作った Fake なデータソースを Repository に置き換える(注入する)ことでテストコードを実行するときは外部機能に依存しないようにする方針かな。


うひゃー。DataSourceの初期化がinitで実装されているのかー!Remoteのほうも直接参照しているし、悲しみの依存関係。。。で、これを直しつつ、って感じかな。


Step 3: Write DefaultTasksRepository getTasks() Test

suspend関数によるテストをするときの解消する内容。

さて dependencies に追記する時間... これのせいでビルドが通らなくなるかもだから不安だわ。


案の定うまくいかなかったので、 ここだけ答えを見てバージョンを整える。
そして runBlockingTest が deprecatedされてて悲しみが垣間見える

どっことどっこと

で、そのためには実クラスに依存させないよう定義する必要がある。つまり、DefaultTaskRepository に依存させないよう定義する必要がある。DefaultTaskRepository に依存するとその実装に依存してしまうわけで、それにより外部に依存することなるから。
ではどうするかというと、 interface として定義してあげる。って話。クリーンアーキテクチャの話題になるとよく出てくる右下の矢印(依存関係)の話。

For your test double, use runBlocking. runBlocking, which is a closer simulation of what a "real" implementation of the repository would do, and it's preferable for Fakes, so that their behavior more closely matches the real implementation.
When you're in test classes, meaning classes with @Test functions , use runBlockingTest to get deterministic behavior.

runBlocking は テストダブル(偽装する側の実装)で、 runBlockingTest はそれを呼び出すテストコード側でそれぞれ使うのが良いとのこと。

どっことどっこと

Add the following gradle dependencies.

junit:junit—JUnit, which is necessary for writing basic test statements.
androidx.test:core—Core AndroidX test library
kotlinx-coroutines-test—The coroutines testing library
androidx.fragment:fragment-testing—AndroidX test library for creating fragments in tests and changing their state.

ライブラリの追加が必要らしい。

どっことどっこと

Launch a fragment from a test

Creates a task.
Creates a Bundle, which represents the fragment arguments for the task that get passed into the fragment).
The launchFragmentInContainer function creates a FragmentScenario, with this bundle and a theme.

なるほど。 navigation を使って Fragment の遷移処理を実装していると 引数 が渡せないから
launchFragmentInContainer を使ってスタイルと引数を指定しつつFragmentを呼び出すのか。

どっことどっこと

Your test both needs to load up the TaskDetailFragment (which you've done) and assert the data was loaded correctly. Why is there no data? This is because you created a task, but you didn't save it to the repository.

おおん? ってそうか。 Task オブジェクトは作ったものの、実際に渡しているのは id だけなのか。
Task まるごと渡せばいいじゃん。と思ってしまうのは心にしまっておく)

どっことどっこと

9. Task: Make a ServiceLocator

https://developer.android.com/codelabs/advanced-android-kotlin-training-testing-test-doubles/#8

ViewModel での Repository の依存関係を解消するために Repository群を管理する ServiceLocator を追加する内容。
FragmentViewModelFactory.create してもらいたいので、Fragment側から参照できる Application に参照口を用意して、そこからViewModelに注入していく流れ。

Step 4. Prepare your ServiceLocator for Tests
Mark the setter for tasksRepository as @VisibleForTesting. This annotation is a way to express that the reason the setter is public is because of testing.

あー。あくまでテスト用であることを 表現 するだけで、setterを秘匿することはできないのか。。

どっことどっこと

後続のステップを確認した。
ServiceLocatorを使ったテスト時の依存関係の解消方法が学べた。
特に、Espressoを使ったUIテストにおいても、VieeModelが参照するリポジトリをFakeなものに差し替えておけば、特定のケースで期待した画面表示になるかの確認ができるとわかった。

どっことどっこと

UIテストにおいて、ServiceLocator実装するのは正直しんどいので、そこらへんをHilt(Dagger)に頑張ってもらうのが良さそうってのが今の流行りなのかな。

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