Androidオールスターズでテストの話をしました

Androidオールスターズでテストの話をしました

スクラムエンジニアリンググループの片渕です。

先日、AndroidオールスターズというイベントでAndroidのテストに関してお話させて頂きました。

当日のレポートは以下のサイトに詳しくまとまっています。

話した事

以下の様な構成で話をしました。

  • なぜテストを書く必要があるのか
  • どこをどうテストするのか
  • 各テストフェーズで利用するライブラリとそのTips

スライドをUPしていますので、以下はスライドを補完する内容となります。

なぜテストを書くのか

テストを書くのは行儀の良いエンジニアであれば当たり前、と言ってしまえばそれまでですが、組織ではアクションに対するゴールの共通認識がないと個人に依存する部分が増え最終的に破綻する事がよくあります。

実際に、開発者に「なぜテストを自動化すべきなのか」と聞くと人によって答えが違う事はよくあるので、少なくとも関係者間では認識を合わせておく必要があります。

今回、私はゴールに関して以下の様な定義をしました。

  • コードの内部品質を向上させる事
  • コードの変更に対して開発者がアグレッシブになれる事
  • QA作業の反復分を自動化し人間しかできないテストに集中する事

どこをどうテストするのか

Android Frameworkには AndroidTestCaseProviderTestCaseActivityInstrumentationTestCase2 など多くのテスト用クラスが存在していますが、これらの使い方をマスターしアプリ内の全コンポーネントをテストするというのは現実的ではありません。かといって手当たり次第テストを書いても個人によって成果物がバラついてしまいうまくないでしょう。

今回は、Google社内で使用されているSmallTest、MediumTest、LargeTestという用語になぞらえ、以下の用にテストすべき範囲とライブラリを選定しました。実際にこれらはアノテーションとしてFrameworkに組み込まれており、adbコマンドで特定のアノテーションがついたInstrumentation Testのみを実行させる事ができます(Instrumentation TestなのでRobolectricのテストを実行する事はできません)。

  • @SmallTest
    • 純粋なビジネスロジックをテストする。対象はUtilやHelper、Model及びデータ取得の為のリポジトリ層
    • ライブラリ: Robolectric3, JUnit4, Mockito, AssertJ
  • @MediumTest
    • UIロジックやユーザーのアクション及びそれに伴って変化するViewの状態をテストする。対象はCustom ViewやActivity/FragmentもしくはPresenter
    • ライブラリ: Espresso2, AndroidTestCase, Mockito, Spoon, assertj-android
  • @LargeTest
    • 「あるシナリオが実行できるか」という観点でテストする。何をアサートするかは一律に定義できないので画面のスクリーンショットを撮って人間が確認する方が無難。リグレッションテストも兼ねる。
    • ライブラリ: Appium, calabash-android, UI automator

アジャイルテストにおける4象限が入っていなかったり他にも色々と甘い所はありますが、「全てを自動化する事はできない」という立場に基づいてこれらを定義しています。

また、今回選定したライブラリ以外にも様々なライブラリが存在しています。下記のリポジトリにまとめてみましたので宜しければご覧ください。

ライブラリのTips

Robolectric 3

大分前から事実上Unitテストのデファクトとなっているrobolectricですが、Gradleで実行するのに専用のプラグインが必要だったりと一部扱いづらい所がありました。しかし3.0のリリースに伴いtestCompileで依存性を宣言するだけでよくなり、Android Studioで実行するのも簡単になりました

便利なrobolectricですが、一点気をつけるべき所としてはShadowクラスにテストを依存させすぎないようにしましょう。

robolectricは内部にShadowクラスという独自実装を持っており、テスト実行時にJava assistを使ってAndroid APIの中身を書き換えるというトリックを使っています。なのでshadowオブジェクトを利用してActivityのライフサイクルイベントを呼んだりIntentを発火させるなどのコードを書いてしまうとそれだけrobolectirc自体への依存度が高くなってしまいます。また、JNIを使用しているアプリとは相性が悪い事なども報告されています。

正しいアプローチとしては、IntentやBundleに値が入っているかなど簡単なケースの確認に止め、それに伴いプロダクトコードの粒度も細かくしていくのが良いと思います。いずれAndroidチームがUnitテストを正式にサポートした際に最小のコストで乗り移れるようにしておきましょう(正確にはgralde plugin1.1から既にサポートされていますが、APIの中身は空実装になっているのでまだ実用的ではないです)。

また、ボイラープレートなコードに関してはannotation processingを利用して自動生成するようにしましょう。例えば面倒なParcelableに関してはauto-parcelを使う事で都度コードを書かなくてもよくなります。

Espresso 2

2.0のリリースに伴いサポートライブラリの一部となったEspressoですが、当初の機能に加えwebviewのsupportやintents、idlingモジュールの追加など人知れず機能追加が進められています。ドキュメントが全く無いのが難点なのですが、使いこなせれば非常に便利です。

また、変更に強いテストを書くポイントとしては、

  • Viewを特定するコードは各テストケースに散在させず1つのメソッドにまとめる事で修正範囲を絞る
  • 外部環境に依存する箇所(APIレスポンスや日付など)に関してはDIで注入できるような設計にしておき、Mockitoやモックサーバーを使って固定する

などの取り組みが効果的です。

ただ、UIテストに関しては費用対効果が難しい所もあるので、サービスの品質レベルに応じてどの程度書くのかを決めるのが現実的だと思っています。

まとめ

今回の発表では特に新しい事を紹介しているわけではなく、既存のライブラリや暗黙知を再整理しているにすぎません。

ではなぜこんな事をしたかというと、以前Architecting Androidという記事を読んだ際にAndoridの設計に関して頭の中でバラバラになっていた知識が1つに繋がっていく感覚を得た為、テストに関しても同等のものを用意する事で少しでもテストを書きやすい環境作りに貢献したいと思ったからです。

「どこまでテストすべし」という事を一律で定義する事は非常に困難ですが、簡単なUnitテストなど今できる事から少しずつ始めていく事が持続的な開発力向上への一歩ではないかと感じています。

尚、今回の発表では時間が押してしまい語れなかった部分があるので、kyobashi.dexにてお話しようと思っています。八重洲/京橋周辺(勿論どこからでもwelcome!)のAndroid開発者の皆様、ご参加をお待ちしています。