AndroidのAsyncTaskをテストしてみるよ
Androidのテストコード書くのが日課のしかじろうだよ。
AsyncTaskのテストコードの書き方が分からなくて悩んでたけど、解決策が見つかったのでログに残します。意見・指摘とかあればコメントお願いしますね。
AsyncTaskとは
AsyncTaskはAndroidで使う非同期処理のクラスです。重たい処理(例えばHTTPでのDLや画像の変換処理など)をバックグラウンドで実行させるのに向いてます。処理の進行状況をプログレスバーに表示させることもできます。
似た処理にServiceがありますが、Serviceはバックグラウンドで処理を繰り返させる(GPSを監視し続けて特定の条件で通知を出すなど)などが目的なので、Activityから独立しています。
Activityと密接な非同期処理がAsyncTask。独立した非同期処理がServiceって感じで使い分けてます。
Serviceは専用のTestCaseがあるからいいけど、AsyncTaskは無いのでさてどうしたものか。
ロジックのテスト
まずは、非同期とかどうでもいいので、メソッドのロジックのテストがしたいです。でもAsyncTaskの #doInBackground() は protected なので、以下のやり方では動きません。
public class HogeAsyncTaskTest extends AndroidTestCase { public void testDoInBackground(){ HogeAsyncTask task = new HogeAsyncTask(); //protectedなのでコンパイルエラー //int result = task.doInBackground(0); } }
task.execute() しちゃうと非同期処理が走っちゃう。HogeAsyncTask#doInBackground() をpublicにすればいいのですが、テストのためにコードを変えるのはその実装どうよって感じですし、テストコードを同一パッケージ収めるのもどうよ。
んで、色々悩んだ挙句、テスト用にHogeAsyncTask を継承したクラスを作って、そのクラスをテストすることにしました。
public class HogeAsyncTaskTest extends AndroidTestCase { public void testDoInBackground(){ HogeAsyncTaskExt task = new HogeAsyncTaskExt(); int result = task.doInBackground(0); assertEquals(1, result); } //テスト対象と実質同じクラス //だけど、doInBackgroundがpublicになっている。 class HogeAsyncTaskExt extends HogeAsyncTask{ @Override public Integer doInBackground(Integer... params) { return super.doInBackground(params); } } }
これで、doInBackgroundのテストができました。やった。やったよ。
ここまでは、AsyncTaskのテスト方法というより、protectedメソッドのテストのやり方ですね。次は非同期処理のテスト方法です。
非同期のテスト
非同期処理のテストなので、 AsyncTask が終わったのを検知してその結果を調べたいです。でも、AsyncTaskは終わったことを教えてくれません。そこで、CountDownLatch と上記で使った HogeAsyncTask を拡張した HogeAsyncTaskExt を利用してテストします。
public class HogeAsyncTaskTest extends AndroidTestCase { //カウントダウンを管理するクラス。待機中に1回カウントされると処理が再開される。 CountDownLatch signal; @Override protected void setUp() throws Exception { super.setUp(); signal = new CountDownLatch(1); } public void testExecute() throws InterruptedException{ HogeAsyncTaskExt task = new HogeAsyncTaskExt(); task.execute(0); //カウントダウンが終わるまで処理が止まる。 signal.await(); //ここにassertとか書いてね } class HogeAsyncTaskExt extends HogeAsyncTask{ @Override //doInBackground()の後に呼ばれる処理。 protected void onPostExecute(Integer result) { super.onPostExecute(result); //カウントダウンして、テスト処理を再開させる。 signal.countDown(); } } }
カウントを1にセットしてテスト処理を実行して待機。executeの処理が終わったら(onPostExecuteが呼ばれたら)待機状態を解除してassertとかで結果を判定する。
こんな感じのコードでテストすることができました。やったねしかちゃん!