JUnitを使ってAndroidの自動テスト(1)

初めまして。Androidチームの@knqyf263です。
新人なもので、技術的には大したことを書けないのですが、自分が調べていて役に立ったことを書いていきます。
まだまだ初心者なので、当たり前じゃんみたいなことも書きます。
色々なサイトを見て調べているので、それらのまとめみたいな感じになると思います。
参考にさせていただいたサイトは本当に参考にしまくりました。ありがとうございます。
時には怪しいこと書くかもしれませんが、お許しください。


今回のテーマとしては、
JUnitを使ってAndroidの自動テストをしよう(Android JUnit Test)
ということです。まぁタイトル通りです。
自分のメモみたいな部分もあるので基本的なことから書いていきます。

JUnitとは

Javaで開発されたプログラムにおいてユニットテスト単体テスト)の自動化を行うためのフレームワークである。(Wikipediaより)
簡単にいえば、テストを楽にしてくれる便利なものくらいなノリかなと。
メリットを挙げるなら
・何度でも実行しなおせる
・誰でも同じテストを行えるようになる。
あたりが大きそうです。
ましてや手動でテストするのは本当につらいです。
ついこないだ端末ごとのチェックで、色々な端末にアプリを入れてひたすら手で確認するという作業を行ったのですが、地獄でした。
これが最初に一度テストコードを作成すれば、あとは自動でできるとは本当に便利。
操作する人による差異も出ないし、テストコードがしっかりしていれば品質証明にもなります。

JUnitを使ってみよう

どんな感じかをつかむためにも、一回JUnitを使ってみたいと思います。
まず、EditTextを置くだけのアプリを作ってJUnitでテストします。
新しくプロジェクトを作成します。なお、今後日本語化されたEclipseを用います。
「ファイル」→「新規」→「Androidプロジェクト」と進み、

・プロジェクト名:EditText
・ビルド・ターゲット:Android 2.2(API Level 8) をチェック
・アプリケーション名:EditText
・パッケージ名:com.glpgs.edittext
・Create Activity:(チェックを入れる) MainActivity
・Min SDK Version:8

と入力します。
さらに、ここで「完了」を押さず、「次へ」を押します。(下図参照)

その後の画面で「Create a Test Project」にチェックし、完了を押します。(下図参照)

Androidアプリケーション用のプロジェクト「EditText」と、テスト用プロジェクト「EditTextTest」が作成されます。

プロジェクトEditTextの編集

MainActivity.javaに関しては簡単のためとりあえず放置します。

main.xmlにEditTextを置きます。一応コードは以下。

main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    >
<EditText android:text="EditText" android:layout_height="wrap_content" android:layout_width="match_parent" android:id="@+id/editText1"></EditText>
</LinearLayout>

テスト用プロジェクトEditTextTestの編集

「EditTextTest」プロジェクトに、テストケースを作成します。
まず、パッケージエクスプローラの「EditTextTest」の上で右クリック→「新規」→「クラス」と進みます。
それで表示される画面で、

・パッケージ:com.glpgs.edittext.test
・名前:TestCase
スーパークラス:ActivityInstrumentationTestCase2

のように入力し、「完了」を押します。(下図参照)

すると、自動で

package com.glpgs.edittext.test;

import ActivityInstrumentationTestCase2;

public class TestCase extends ActivityInstrumentationTestCase2<T> {

}

のようなクラスが作成されます。このとき、エラーが出ていると思いますが、気にしないでください。
ここで注意なのですが、自分の場合自動で挿入されていたのは、上に書いたように

import ActivityInstrumentationTestCase2;

でしたが、

import android.test.ActivityInstrumentationTestCase2;

のようにしないとエラーが出ます。
最初の設定でちゃんと書けばいいとは思うのですが、これでできるのでこうなった人は直しておいてください。

まだエラーが消えていないと思うので直します。
ActivityInstrumentationTestCase2<T>の「T」を「テストを行いたいクラス名(今回はMainActivity)に置き換えます。
今度はMainActivityの部分でエラーが出ていると思うので、MainActivityにカーソルを合わせて、「'MainActivity'」をインポートします」を選択します。
さらにTestCaseでもエラーが出ていると思うので、同様にカーソルを合わせて「コンストラクター‘TestCase(String,Class)’を追加します」を選択します。

これでエラーが消えたと思います。修正版は以下。

package com.glpgs.edittext.test;

import com.glpgs.edittext.MainActivity;
import android.test.ActivityInstrumentationTestCase2;

public class TestCase extends ActivityInstrumentationTestCase2<MainActivity> {

	public TestCase(String pkg, Class<MainActivity> activityClass) {
		super(pkg, activityClass);
		// TODO 自動生成されたコンストラクター・スタブ
	}
}

コンストラクタが1つ追加されましたが、もうひとつ「引数なし」のコンストラクタを追加します。
コンストラクタ内では、親クラスのコンストラクタを呼び出しますが、第1引数は、テストを行いたいパッケージ名、第2引数はテストを行いたいクラス名を指定します。

public TestCase() {
	    super("com.glpgs.edittext", MainActivity.class);
	}

次にテストケースの作成です。
まずメソッド名を決めます。先頭が「test」と付くように決めてください。今回は「testEditText」としました。
今回の仕様では、EditTextには最初"EditText"が入っているはずなので、それを確かめます。
最初にコードを示すと以下の通り。

public void testEditText() {
	EditText    editText;
	Activity    activity;
		
	activity = getActivity();
	editText = (EditText) activity.findViewById(R.id.editText1);
      assertEquals("EditTextの初期値が間違っています", "EditText", editText.getText().toString());
}

最初の数行は、Activityと結び付けて、情報を取得するためのコードなのですが、それはまた次回説明します。
今回重要なのは

    assertEquals("EditTextの初期値が間違っています", "EditText", editText.getText().toString());

の部分です。
assertEqualsとは、同じ値かを比較して、異なる場合はメッセージを表示するメソッドです。
assertEquals(msg, arg1, arg2)→arg1とarg2が同じ値かどうか判別します。値が違う場合はメッセージとしてmsgを表示します。基本型、オブジェクト型の両方を判別できます。
他にもJUnitには様々なメソッドが用意されているので調べてみてください。

一応完成版を載せておきます。

   package com.glpgs.edittext.test;

import com.glpgs.edittext.MainActivity;

import android.app.Activity;
import android.test.ActivityInstrumentationTestCase2;
import android.widget.EditText;

import com.glpgs.edittext.R;

public class TestCase extends ActivityInstrumentationTestCase2<MainActivity> {

	public TestCase(String pkg, Class<MainActivity> activityClass) {
		super(pkg, activityClass);
		// TODO 自動生成されたコンストラクター・スタブ
	}

	public TestCase() {
	    super("com.glpgs.edittext", MainActivity.class);
	}
	
		public void testEditText() {
			EditText    editText;
			Activity    activity;
			
			activity = getActivity();
			editText = (EditText) activity.findViewById(R.id.editText1);
		    assertEquals("EditTextの初期値が間違っています", "EditText", editText.getText().toString());
		}
}

すごく簡単ですが、一応テストが完成したので実行しましょう。
テスト用プロジェクト「EditTextTest」の上で右クリックして、「実行」→「Android JUnit Test」をクリックします。
実機を接続していない場合は、エミュレーターで実行されます。その場合は起動に時間がかかるので少し待ちます。
その後、Eclipseに結果が表示されます。
「緑色」→「テスト成功」
「赤色」→「テスト失敗」
です。「赤色」の場合は自分の思う通りに動いていない(仕様通りでない)ことを意味するので、確認しましょう。
エラーがある場合は、テストケースにエラーがあると思われるのでテストケースを確認してください。

以上でJUnitを用いたAndroidのテストは終了です。
ここに書いたのはあくまで簡単な例なので、これをベースに複雑なテストを書いていきましょう。
次回以降はUIに対するテストや、Androidのcode coverageについて調べて、わかったことを書いていきたいと思います。
長々とすいません。失礼しました。

一応参考に今回作ったプロジェクトをのせておきます。
実行は自己責任でお願いします。
EditText.zip 直
EditTextTest.zip 直

[参考]
Javaの道:JUnit(2.基本操作)
AndroidアプリケーションをJUnitでテストする
Androidアプリケーション Junitテストの作り方 〜その1〜EditTextTest.zip 直