Android のテストで設計をイイカンジにしよう

Android フレームワークは、あまり強力とはいえないものの、ある程度のテストフレームワークを内包しています。
ベースが JUnit3 なので、記法が古かったりすることもありますが、とりあえず JUnit3 の作法を身に付ければある程度のテストは書くことが出来ます。

# Android のテストケースフレームワーク

Android がもつ各種のコンポーネントをテストするためのフレームワークが有ります。
いろいろな種類がありますが、おそらく最も頻繁に使うのは`AndroidTestCase`でしょう。

## AndroidTestCase

Android でユニットテストをするなら、ほぼ間違いなく使うであろうクラスです。
そのようなテストケースでは、すべてこのクラスを基底に作っていきます。
もう少し具体化して言うと、`Context`に依存するすべてのテストはこのテストクラスを使うことで、イイカンジにテストができるようになります。つまり、テスト用に使う`Context`をこの`AndroidTestCase`がよしなに管理してくれるわけです。

## InstrumentationTestCase

Android フレームワークにおけるもう一つの基底テストクラスです。
こちらは機能テストのためのもので、ボタンを押す、とか、テキストを入力する、と言ったことをテストするために使います。
しかし、何かとクセの強いフレームワークで、かつ何かと出来ないことも多いため、このクラスを純粋に使うことはめったにないでしょう。今や、[Robotium](https://code.google.com/p/robotium/) というもう少しリッチに機能テストが書けるライブラリもあることですし…
通常、このクラスのサブクラスである`ActivityInstrumentationTestCase2`を用いて機能テストを作ります。

## ActivityTestCase

`Activity`をユニットテストするための基底クラスです。が、このクラスを直接サブクラス化してテストを書くことはめったにないでしょう。代わりに、`ActivityUnitTestCase`を使いましょう。
`Activity`を取り扱うための各種の部分をテスト用にエミュレートするフレームワークが含まれています。

## ProviderTestCase2

`ContentProvider`をテストするための基底クラスです。内部で`Context`と`ContentResolver`をモックしており、本番のデータベースとは隔離された場所にテスト用データベースファイルを生成しています。このため、テストを実行したことが本番のアプリに影響を及ぼすことはありません。一方で、ファイル自体は生成され、データベースの操作も実際に行われるので、信頼性の高いテストを記述することが出来ます。

## ServiceTestCase

`Service`をテストするための基底クラスです。`Service`のライフサイクルをテストするにあたって必要な依存の注入をするためのフックポイントが用意されています。

## ApplicationTestCase

`Application`をテストするための基底クラスです。

## LoaderTestCase

`Loader`をテストするための基底クラスです。`Loader`自体は Support Package Library のメンバですが、このテストはそのライブラリに含まれていませんので、注意が必要です。
通常、`Loader`の果たすべき仕事は非同期に実行されますが、テストでは同期実行されるようになります。

## SyncBaseInstrumentation

`ContentProvider`の同期についてテストするための基底クラスです。テストの要求に応じて同期が実行されるかどうかをチェックしますので、同期中に実行される内部のロジックは別途ユニットテストで確認することになります。

# モックフレームワーク

`Perl`のような言語では、そもそもモックするためのフレームワークが無くとも、適当にオブジェクトの振る舞いを書き換えてしまうことが出来るのですが、Java では`Mockito`とかを使わない限りそのような芸当は困難を極めるので、別途モックを生成するためのものが用意されています。

と言っても、`Mockito`ほど強力なものではなく、単純に、Android のコンポーネントクラスを継承して、すべてのメソッドで例外を投げるようなものがほとんどです。

## MockContext

`Context`のモックです。すべてのメソッドが、`UnsupportedOperationException`を投げます。
テストでは必要に応じてメソッドをオーバライドして、そのメソッドが呼ばれた、とか、呼ばれた時の引数チェック、とか、返す値を適当に書き換える、とかの目的で使用します。

## IsolatedContext

本番と隔離する目的で使用される`Context`です。本番の`Context`を使用するのではなく、テスト用のものを使用することで、本番に影響を与えないようにしています。
主には、`Service`や`BroadcastReceiver`の管理や、`Uri`のパーミッションチェックなどがモックされています。

## RenamingDelegatingContext

ファイルへの書き込みをモックする`Context`です。`ProviderTestCase2`でも使用され、実際にデータベースが作成される時のファイル名を書き換えています。
より一般的には、通常のファイル入出力をテスト用のものに差し替える機能を持っています。
が、`SharedPreferences`については差し替えをしてくれませんので、[別途自分でモックする](http://qiita.com/KeithYokoma/items/b81ff2aaf7959258ee7b)必要があります。

## MockContentResolver/MockContentProvider

`ContentResolver`と`ContentProvider`のモックで、通常は両方をセットにして使います。
`ContentProvider`の挙動をモックすることで、テスト用に期待する振る舞いをするようスタブ化するために使います。`ContentProvider`のモックを作った上で、そのモックの`ContentProvider`へアクセスさせるために`MockContentResolver`を設定し、テスト対象のメソッドに引き渡します。

## その他 Mock クラス

`MockApplication`、`MockCursor`、`MockPackageManager`、`MockDialogInterface`、`MockResources`があります。
いずれも、`UnsupportedOperationException`をスローするだけのクラスです。

# Context への依存を設計で管理しよう

何かと怖がられがちな`Context`ですが、しっかりと設計をしておくことで適切な管理を行えば、滅多なことでは事故ることは無いと思います。

## UI のロジックは Helper に切り出す

それこそ、ほぼ手続き的なことしかしないのであれば、ステートレスにロジックを記述しておくことがよいでしょう。
ロジックそのものはステートレスに見える形で記述しますが、その実`View`はそれそのものが状態を持ち管理しているので、`View`の状態チェックとそれに合わせたロジックを書くことにはなろうかと思います。

よく、`Activity`に`private`なメソッドが大量に連なることが有りますが、要するにそれらはヘルパメソッドでしかないので、`Context`と一緒に必要なデータをメソッドに渡すよう設計しておけば、いくらでも UI のロジックを別のクラスに委譲することができます。
このようにしておくことで、`AndroidTestCase`を使うことで UI のロジックのユニットテストを記述できるようになります。

## Service のテストはややこしい

特に、`IntentService`は、その処理の本体は別のスレッドで実行されてしまうので、スレッドの待ち合わせを行う点で非常に面倒なことが多いです。
出来る限り`IntentService`そのものにはロジックを持たせず、使用したいモデルやヘルパのメソッドの呼び出しを行うだけにしておきたいです。そうすれば、少なくともモデルやヘルパのテストが成されれば、その呼び出しを、規約にしたがって行うだけで済むため、テストコストが最小限になります。

## Context のモックをするクラスを活用する

ファイル入出力があるのなら`RenamingDelegatingContext`を使うなど。それ以外にも、自分でモックをするためのコードを書くこともできるので、そういったものをフレームワーク化することで、ほとんどのロジックはユニットテスト可能です。

必要があるなら、`SharedPreferences`や`FragmentManager`、`LoaderManager`、`ActionBar`等のモックも作っておくとよいでしょう（実は[Amalgam](https://github.com/nohana/Amalgam/tree/develop/amalgam/Amalgam/src/main/java/com/amalgam/test/mock)にも入っています）。

# 機能テストを書きたい

## Android フレームワークのユーティリティ

`TouchUtils`を用いることで、タッチ操作をテストからエミュレートすることが出来ます。

`ViewAsserts`を用いることで、`View`の状態を手軽にチェックしアサートすることが出来ます。

また、基本的にテスト対象の`Activity`に対する依存の注入ができるフックポイントが用意されています。これによって、モデルレイヤを差し替えて、表示のロジックが適切に動作するかをチェックできるようになります。

## Android フレームワークに足りないものをテストする

例えば、`Toast`に正しく文字列をセットして表示されたかどうかをテストする手段はありません。テストを実行することで、画面上にも`Toast`が表示されますが、それらを目視確認するよりは、コードで確認出来たほうが楽ですよね。
そういった、かゆいところに手が届かない感じをイイカンジに吸収するものが`Robotium`です。公式には、Android 向けの Selenium という位置づけで作られているようです。

`Robotium`によって、テストから UI を簡単に操作できるようになり、Android フレームワークだけではテストできなかったこともテストできるようになります。

ただし、都度 UI の出力を待ち合わせるため、完全に動作させようとすると、実行時間がかかります。

ユニットテストでなんとかなる部分は、出来る限り`AndroidTestCase`で頑張りたいものです。

# まとめ的な

`Activity`にしても`Service`にしても`BroadcastReceiver`にしても、これらはコントローラであると言うことを前提に、モデルやデータ構造をうまく設計し実装することが、テストをしやすくしてくれますし、コントローラの見通しも良くなります。