Android Eclipse から ユニットテストを実行する

あらためて、Android アプリケーションの開発手順を振り返ってきているが、ちょっとややこしそうで敬遠していたユニットテストのやり方を把握しておこうと思う。

Activity のテスト

デベロッパーズガイドを確認すると、以下のテスト方法が説明されている。

http://developer.android.com/guide/topics/testing/index.html

まずは、Acitivity のテスト方法のアウトラインを理解したいと思う。

http://developer.android.com/guide/topics/testing/activity_testing.html

Android Instrumentation Framework 

Acitivity のテストは、コールバックに基づいた複雑なライフサイクルをもっていて、また、イベントをユーザーインターフェースへ送る必要もあるが、これらを可能にするのは、Android Instrumentation Framework  のようだ。InstrumentationTestCase を基底クラスとしたテストケースを作成することで、Acitivity を操作する方法が提供される。

InstrumentationTestCase の機能

以下のような機能が提供される

  • ライフサイクルの制御 : 開始、停止、破棄
  • 依存性の注入 : Context や Application、Intent のテスト用モック作成
  • ユーザーインターフェース機能 : キーストロークやタッチイベントエミュレート

テストクラスの概要

ActivityInstrumentationTestCase2 および、ActivityUnitTestCase の2つが主に利用するテストクラス

SingleLaunchActivityTestCase は Activity を通常でないモードで使用した場合

テストクラス 概要 モックContext
,Application
モック
Intent
ActivityInstrumentationTestCase2 通常のシステム環境 ×
ActivityUnitTestCase 隔離された環境 ×
※1
SingleLaunchActivityTestCase テスト中、環境が変化しない場合 × ×

※1 Activity.startActivity(Intent) 呼び出しは可

Eclipse から ADT を使ってユニットテストを行う

テスト用のプロジェクトの作成

  • テスト用に別プロジェクトを作成する必要がある
  • テスト用プロジェクトは、Android アプリケーションと同様のディレクトリ構造をもち、同様なファイル、マニフェストファイルなどを持つ
  • マニフェストファイルの <instrumentation> に従い、テスト対象のアプリケーションに接続する
  • Android プロジェクトの新規作成時だけではなく、いつでも作成できる

File - New - Other

Android Test Project を選択し、「New Android Test Project」ダイアログを開く(新規作成の場合、自動で表示される)

 android_test01

New Android Test Project

Test Project Name  に、名前を設定する。任意の名前がつけられるが、対象のプロジェクト + "Test" としておけばよい。

Test Target の「An existing Android project」 にチェックし、Browse で既存のプロジェクトを選択すると、その他の必要項目が自動でセットされる。

今回は、自作の手書きメモアプリを対象としてみた。

 android_test02

テストクラスの作成

作成された、テストパッケージ(今回の例では、info.typea.shujiroid.free.test) 配下にクラスを作成する。

パッケージを右クリックして、New - Class

名前は、テスト対象のクラス名 + "Test" としておけばよい。

基底クラスに、上記3クラスから、適切なクラスを選択する。とりあえず、ActivityInstrumentationTestCase2 としてみる。

 android_test03

テストクラスの実装

ここまでで、自動で生成されたテストクラスには何も実装されていないので、以下のような内容を実装する必要がある。

  • ジェネリック型に対象のActivityを指定する
  • コンストラクタ
  • setUp() メソッド、tearDown() メソッドを必要に応じて。setUp() では、ACTION_MAIN でインテントを呼び出したり、テスト用の環境を構築するのに利用する
package info.typea.shujiroid.free.test;

import info.typea.shujiroid.core.ShujiView;
import info.typea.shujiroid.free.R;
import info.typea.shujiroid.free.ShujiActivity;
import android.test.ActivityInstrumentationTestCase2;
import android.test.TouchUtils;
import android.view.Gravity;

public class ShujiroidTest extends 
              ActivityInstrumentationTestCase2 {
    private ShujiView shujiView;
    
    public ShujiroidTest() {
        super("info.typea.shujiroid.free", ShujiActivity.class);
    }

    @Override
    protected void setUp() throws Exception {
        super.setUp();
        shujiView = (ShujiView) getActivity().findViewById(R.id.shujiview);
    }
    
    public void testDraw() {
        getActivity().runOnUiThread(new Runnable() {
            @Override
            public void run() {
                shujiView.requestFocus();
            }
        });
        TouchUtils.dragViewBy(this, shujiView, Gravity.CENTER, 500, 200);
    }
}

デバッグ対象のアプリケーションに参照設定されるので、Activity はじめ、各クラスが参照可能になっている。また、対象のリソース参照クラスも、そのまま、R から利用できる。

setUp() をオーバーライドし、デバッグ対象アプリケーションの View の参照を取得しておき、テストケース(testDraw) から、タッチイベントを発生させることを試みる。

アプリケーションのユーザーインターフェースオブジェクトにアクセスするにはUIスレッドからアクセスする必要があるため、Activity.runOnUiThread() から、フォーカスをビューに与える

その後、TouchUtils を利用して、画面に描画を行ってみる。

ちなみに、以下のようなユーティリティが存在する。

テストの実行

テストケースクラスのコンテキストメニューから、Run As - Android JUnit Test を選択

android_test04

実機をつないで、上記を実行したところ、以下のように画面中央から、ドラッグイベントが発生し、画面に奇跡が描画された。

 android_test05

結果は、JUnit ビューに表示される

android_test06

なんとかとっかかりがつかめたので、次からはテストファーストできそうだよー