MagicPodさん主催のイベントモバイルアプリ開発における良いテストコードの考え方に「Kotlin/Androidでテスト駆動開発をはじめよう」という内容で、スピーカーとして参加させていただきました。今回はそのセッションの補足内容となります。テストというテーマは深く、考え方もいろいろあるので、15分のセッションに収めるのは大変なので、補足を書きました。
TDDのやり方
TODOリスト
Kent Beckの「テスト駆動開発」では、TODOリストを作りながらTDDを実践していきます。これは、Greenのフェーズでコードをきれいにする方法を思いついているが、このフェーズではまだ実施できない、のようなときに作るTODOリストです。TDDでは各フェーズでやるべきことが決まっているので、将来に手を付けるべきことを棚上げにしておかないといけません。
そのときにTODOリストとしてメモ書きを残しておくとよいです。頭の中で短期記憶しているだけだと、忘れてしまいそうで、不安になります。TODOリストはその不安を取さってくれます。
内から外へ向かってTDDをやるか、その逆か
TDDには外から内へ向かうやり方と、内から外へのやり方があります。外から内とは、最初に受け入れテストを書き、それを満たすようにUI、ViewModel、データレイヤーように内側へテストを書きながら実装をしてくスタイルです。内から外は逆で、最初にドメインやモデルのテストを書き、データレイヤーからUIへと向かっていきます。
ここで両者メリット・デメリットがありますが、外側のほうが依存関係が多いので、外から内へ向かうやり方だといろいろな依存関係をテストダブルで補ってあげる必要があります。逆方向だと、既に依存ができているので、やりやすいところがあるはずです。本物の依存関係、すなわちプロダクションで使うそのままのRepositoryを使ったりすることもできるでしょう。
セッションではAndroidならではのテストを示したかったのでViewModelを作りましたが、最初に内側を作ったほうが、その点のやりやすさはあると思います。
高速化の重要性
TDDはよくテストを回します。小さなステップのたびに、テストを回します。なので高速化が非常に重要になってきます。
できるだけ実機のテストは避けて通常のJunitのテストだけで賄うのが理想的でしょう。
また、マルチモジュールにしてキャシュを活用したり、テストを並列化したりすると良いかもしれません。それができないなら、変更をプッシュする前は、変わったテストだけ実行するようにするとよいと思います。
TDDの注意点
TDDを試すには簡単な所からやってみることをおすすめします。TDDはどこでもやりやすいわけではなく、適さない場合もあります。
例えば技術やドメインへの理解が少ないうちは、難しいと思います。ある程度コードの予想がつくようになる必要があるからです。
技術やドメインへの理解が少ないときはコードを書いてみながら動くか試すようなことをやると思います。そうしたほうが、知識の定着は早いと思います。また知識が乏しい内はテストがどうなるかの予想も難しい場合があると思います。
TDDは特に既存のコードが有るときに有力だと思います。初めて見るなら既存のコードが有り、別の振る舞いを増やすようなときに、やってみると良いと思います。
またチームでTDDを導入するのは慎重になるべきだと思います。TDDはスタイルであって、人に強要されると反感が起きてしまうかもしれません。チームとしてはあくまで啓蒙活動を留めるというのが良いように思います。
しかしチームにそもそもテストを書く習慣がない、といった事情には話は変わるかもしれません。チーム状況に合わせて、どのような運用をすればよいか考える必要があります。
コードサンプルについて
hiroaki404/tddKotlinにコードサンプルを載せています。
セッションはsecondStepまでの紹介で、thirdStepにローディングのテスト、lastに季節でのクエリーの機能を、もちろんTDDで実装しています。
lastではTurbineのテストでexpectMostRecentItem()を使っています。
こちらを使うとstateFlowの最後の値だけ検証できるようになります。
skipItem()やawaitItem()を使うやり方だと、内部実装に寄ってしまうと思うので、こちらを使うと脆くないテストを書くことができると思っています。
thirdStep以前はawaitItem()を使ったやり方のテストとなっているので注意してください。
Fakeについて
これは深くなるので、また機会がありましたら話すか、記事にしたいと思います。
セッションではViewModelのテストしか紹介する時間がなかった関係上RepositoryはFakeを使いましたが、
実際のクラスを使ったほうがよいこともあります。
ViewModelが複雑になってきた場合にRepositoryにFakeを使い、ViewModelクラスの検証に集中するのが良いと思っています。
Flowのテストについて
Flowの出力をテストから制御する内容を説明しましたが、
あまりやりすぎると内部実装に深く寄ってしまう事があるのではないかと思います。
簡単なところは同期的に値を返すようにして、乱用しすぎないようにしましょう。
ViewModelの実装について
initブロックが良くないという話をしましたが、Googleでもよくないとされています。
また、ViewModelの設計パターンは、こちらの記事も役に立ちます。シリーズでは、やるべきこと、やってはいけないことが書かれています。初回で、initブロックを避けるというテーマが書かれています。
Mastering Android ViewModels: Essential Dos and Don’ts Part 1 🛠️ | by Reza | ProAndroidDev
参考文献について
以上挙げて来ましたがここでもまだ言い足りない部分があります。やはり文献にあたってみることをおすすめします。
共有スライドではハイパーリンクが押せないので、こちらからたどると早いと思います。
- 『テスト駆動開発』KentBeck著 オーム社
- 『Googleのソフトウェアエンジニアリング』 オライリー・ジャパン
- 『単体テストの考え方/使い方』 Vladimir Khorikov著 マイナビ
こちらの三冊はユニットテストを知るうえで必読ではないかと思います。あとの2つは、TDDではなく良いユニットテストを書く考え方の本です。
Principles for Writing Valuable Unit Testsを聞いたメモ #アーキテクチャ - Qiitaには、3冊目の作者がPodcastで語られたことがメモとして書かれてありますが、TDDの話も入っています。
Androidの良いユニットテストとしては、DroidKaigi 2023 - [JA] Androidアプリの良いユニットテストを考える | Nozomi Takuma - YouTubeもかなり参考になります。
Android developer guide
- アプリ アーキテクチャ | Android Developers
-
Android でアプリをテストする | Android Developers
こちらをスライドに記載しましたが、 -
Android アーキテクチャに関する推奨事項 | Android Developers
ここにはテストも含めたベストプラクティスが書かれてあります。
codelabや公式のサンプルは大いに参考になります。
またnow in androidのテストはかなり参考にさせていただいています。
参考にしたリポジトリ
android/nowinandroid
android/architecture-samples
DroidKaigi/conference-app-2023
おわりに
こういう公の場で技術の発表をするのは初めてだったので、良い経験になりました。
大変でしたが、正確性を機するためいろいろな資料に目を通していったり、サンプルを作ったりしました。
その中で学びとなり、テストに関して自信を持つことができたので、やはり技術をアウトプットする機会を持つことは大事だと思いました。
機会があればまた挑戦したいと思います。