Androidアプリサンプルにおける単体テストのテストスコープについて調べてみた

[単体テストの考え方/使い方](https://www.amazon.co.jp/%E5%8D%98%E4%BD%93%E3%83%86%E3%82%B9%E3%83%88%E3%81%AE%E8%80%83%E3%81%88%E6%96%B9-%E4%BD%BF%E3%81%84%E6%96%B9-Vladimir-Khorikov/dp/4839981728/ref=sr_1_1?adgrpid=146552030392&dib=eyJ2IjoiMSJ9.DwAMKN29hHMmZWAvUlEn2A3QQK1nnfD3KmHUkW3juAQ_ko6ILz9DBpmcn0ixQ2dfiBbmX0ZazRUq5oLHyFTvS_wKTCY5Jv43bFltrJUsbQ59eF3_BuWZK1d0xsq5AGgSSB1jngr9nXYSL6QSwTleRavEbTzlgXYngUMLtI4wn_UlndTJrNc2hn0gSqzDfzrjKaqerPTtlLgx6EeeSY8vOWTEnJ-gfi_GeFeBiUODeJ5iqH9wIvoZo3ycGUJGBoXV.equosaUSpavfSklcQMFoxnDeRbaMLdYJDmawYhbqcfE&dib_tag=se&hvadid=665685951506&hvdev=c&hvlocphy=1009263&hvnetw=g&hvqmt=e&hvrand=10827772891216186784&hvtargid=kwd-1959808738956&hydadcr=27487_14701027&jp-ad-ap=0&keywords=%E5%8D%98%E4%BD%93%E3%83%86%E3%82%B9%E3%83%88%E3%81%AE%E8%80%83%E3%81%88%E6%96%B9%E4%BD%BF%E3%81%84%E6%96%B9&qid=1717798822&sr=8-1)を読み、単体テストをめぐる２つの考え方を整理しました。その２つの考え方は「ロンドン学派」と「古典学派」と呼ばれるものですが、詳細はさておき、「単体テスト」の「単体」の意味が大きく異なります。

本当は根底にある思想を説明する必要がありますが、ここでは誤解を恐れずに言うとその違いとは、「１つのクラス」か、「一つの振る舞い」の違いとなります。前者がロンドン学派です。書籍では古典学派が推されていて、「１つのクラス」にとらわれないべきとあります。

しかし私が見てきたテストは「１つのクラス」を対象としたものが多く、また１つのクラスを対象にすると問題の特定が早くなるメリットがあります。筆者はそのメリットはテストを作ったり保守したりするコストに比べれば小さいとしています。確かにそうなんだろうなとう気はしますが、直ちに切り替えられるかというと、もう一つ切り札となる根拠がほしいところでした。

ということで今回はAndroidのサンプルリポジトリを観察して、それぞれのサンプルがどういったテスト戦略を取っているか確認していきました。

## サンプル
確認したサンプルは下記のものです。
- [DroidKaigi/conference\-app\-2023](https://github.com/DroidKaigi/conference-app-2023)
- [android/nowinandroid](https://github.com/android/nowinandroid)
- [android/sunflower](https://github.com/android/sunflower)
- [android/architecture\-samples](https://github.com/android/architecture-samples)

下記調査内容ですが、コードのすべてを見たわけではありませんので、誤った箇所があればご指摘お願いします。

## Droidkaigi2023
こちらは特殊というか単体テストはなく、結合テスト、というかスクリーンショットテストで構成されています。
テストダブルはフェイクをAPIに使い、それ以外は実際のクラスを使っています。UI、ViewModel、Repositoryまで本物を使い、忠実度が非常に高いです。
（なのでスコープとしては、UI〜Repositoryになります）

スクリーンショットテストなので通常のUIテストに比べて保守コストも低く、リグレッションの検知の能力も高そうです。
しかし実業務だとUIの変更がたびたび発生するのでそのあたり事情が違ってくるように思います。機能も複雑になってくるとスクリーンショットテストだけだと心もとない気がします。

単体テストがないというのはそれがなくてもこのアプリの条件下であれば十分機能するからでしょうか？それともスクリーンショットテストがあれば単体テストなくても大丈夫なのでしょうか？（自分がスクリーンショットをちゃんと活用できておらず、判断できないです）
単体テストがないのは理由があると思いますので、機会があれば中の人に話を伺いたいです。

## now in android
UIテストはしないようにとあります。かわりにスクリーンショットテストは実施しています。
ViewModelのテストにはFakeのRepositoryを入れているので、スコープはViewModel単体となります。
RepositoryのテストにもFakeのデータソースを入れていて、Repositoryもクラス単体のテストになっています。


## sunflower
ViewModelのテストに、RepositoryやDAOに本物を使っているので、スコープはViewModel〜DAOとなります。
構成がかなり単純なので参考にしかならないです。
composeのtestも行っていますがViewModelとの結合はないです。

## architecture samples
少し古いリポジトリですが[compose\-samples](https://github.com/android/compose-samples)だと、あまりいい例が見当たらなかったのでこちら。
ViewModelのテストにFakeRepositoryを差し込んでいるので、スコープはViewModelのみです。
Repositoryのテストも行っており、DataSourceもFakeを使っているので、クラス単体です。

## [Android アーキテクチャに関する推奨事項  \|  Android Developers](https://developer.android.com/topic/architecture/recommendations?hl=ja#testing)
こちら推奨事項を見ると、ViewModelの単体テスト、リポジトリとデータソースの単体テストはやるべきと書かれています。
この記事でいうスコープを完全に読み取ることはできませんが、おそらくViewModelはクラス単体の意味で推奨されているように思います。

## ではどうするか？
調査して考えたことは、やはりプロジェクトによって最適なテスト戦略が変わるといことでした。
単純なプロジェクトなら広いスコープでよく、各クラスの責務が複雑になってきたらクラス単体でやる価値はあると思いますし、そうなっているサンプルもあります。
しかし書籍を読み終わった私は、多少複雑なプロジェクトになったとしてもクラス単体でなくもっとスコープをもう少し広げるべきだと考えています。

個人的には、実業務においてはUIは変わりやすいのでスクリーンショットを撮る結合テストで主にハッピーパスを確認し、
単体テストはViewModel〜Repositoryのスコープにして、フェイクのデータソースを入れてテストするのが良いように思います。しかしもうちょっと試さないとなんともいえません。ViewModelやRepositoryが複雑になってきたらクラス単体におきかえてもよさそうです。
(もちろんutilityやドメインなどのテストは別途単体テストしないといけないです)
あと、ナビゲーションテストも有用ですね。

もしなにかご意見があればご気軽にコメントください🙇