AndroidでJUnit
前回の記事で作成したプロジェクトのテストケースを作成
そして、テストでハマったことを中心に記事ってみました
するテスト
- Buttonを押してイメージを表示
- イメージをDragAndDrop
- 画面遷移して、遷移したActivityをfinish(これが一番大変だった)
JUnitProjectの作成
※ここではDragAndDropSample
-
- projectを設定すると自動的にTestProjectNameが設定される
- Finish
- 空のTestProjectが生成される
JUnitTestCaseの作成
テストコード作成
■Tip
- パッケージ名はターゲットプロジェクトと同じにする
※同一パッケージはprotectが普通に呼び出せる
- デフォルトコンストラクタを作成
- superを引数付きで呼び出す
- 第1引数:パッケージ名
- 第2引数:ターゲットクラスのクラス型
- Activityを取得するのはgetActivity()
- UIThreadでしか処理できないイベントはAcitivty#runOnUiThread内で実装
- イベントを実行した後はgetInstrumentation().waitForIdleSync();を使ってメインスレッドが戻ってくるまで待機(説明に御幣があるが、多分looperが空になるまで待っていると思われる)
- TouchUtilsを使うとDragAndDropのテストができる
※本当はTouchUtilsについてももっと触れたいのですが、また別の機会にでも・・・
テストを実行する
テストはメソッド単位で実行することができる
実行方法
Outlineビューでメソッドを右クリック→Debug As→Android JUnit Test
■testOnClickShowButton
Imageの表示テスト
■testOnClickButton02
画面遷移のテスト
public void testOnClickButton02() { final DragAndDropSample activity = getActivity(); final Button button = (Button) activity .findViewById(R.id.Button02); activity.runOnUiThread(new Runnable() { public void run() { button.performClick(); } }); getInstrumentation().waitForIdleSync(); }
■testOnTouch()
TouchUtilを使ってドラッグアンドドロップのテスト
水平方向にx+100、y+100だけ動かす。
実際にテストを走らせると本当に勝手に動いているからおもしろい。
//Drag and Drop TouchUtils.dragViewTo(DragAndDropSampleTest.this, target, Gravity.CENTER_HORIZONTAL, activity.currentX + 100, activity.currentY + 100);
【はまったこと】UnitTestが途中で止まる!!!
メソッド単位のテストは成功しても全体を通して実行するとテストが途中で止まる!!
という自体が発生します。
■原因
原因はいろいろありますが、一番多いパターンはAcitivityの遷移
画面が遷移してしまうとUnitテストのターゲット以外のAcitivtyがフォアグランドで動いてしまうのでテストは止まります。
一応、メソッド単位での成功はするのですが、次のテストケースを呼び出せなくなります。
今回のケースだとtestOnClickButton02は終了でも画面が変わったので次のテストケースが呼べない。
なんでこうなるの?
【推測】今Androidが実行しているのはTestアプリです。なのでテストアプリが一番上にいます。そのテストアプリがテスト上でActivityの起動をしました。
↓
その結果、起動したActivityが一番上にくるという残念な状態になっている。(自信ないけどたぶん?知ってる人いたら教えて下さい)
解決策
ようするに一番上のActivityをテストケースからfinishすればいい。
以下のことをすればいける。
- ActivityMonitorを使う!
- 第1引数:監視したいActivityをパッケージ名付クラス名
- 第2引数:よくわかんない
- 第3引数:戻り値があるかないからしい
- getInstrumentation().addMonitor(monitor)で気になるActivityを監視対象に追加!
- getInstrumentation().waitForMonitorWithTimeout(monitor, 2000); で起動したActivityを取得!
- 取得したActivityをfinish
ソース
testOnClickButton02を以下のように修正
public void testOnClickButton02() { ActivityMonitor monitor = new ActivityMonitor("com.dd.sample.Next", null, false); getInstrumentation().addMonitor(monitor); final DragAndDropSample activity = getActivity(); final Button button = (Button) activity .findViewById(R.id.Button02); activity.runOnUiThread(new Runnable() { public void run() { button.performClick(); } }); Activity next = getInstrumentation().waitForMonitorWithTimeout(monitor, 2000); assertEquals(monitor.getHits(),1); if(next != null){ next.finish(); } }
全ソース
package com.dd.sample; import android.app.Activity; import android.app.Instrumentation.ActivityMonitor; import android.test.ActivityInstrumentationTestCase2; import android.test.TouchUtils; import android.view.Gravity; import android.view.View; import android.widget.Button; import android.widget.ImageView; import com.dd.sample.DragAndDropSample; import com.dd.sample.DragAndDropSampleTest; import com.dd.sample.R; public class DragAndDropSampleTest extends ActivityInstrumentationTestCase2<DragAndDropSample> { public DragAndDropSampleTest() { super("com.dd.sample", DragAndDropSample.class); } public void testOnClickShowButton() throws Exception { final DragAndDropSample activity = getActivity(); final ImageView target = (ImageView) activity .findViewById(R.id.ImageView01); final Button button = (Button) activity .findViewById(R.id.Button01); //とりあえず消しとく activity.runOnUiThread(new Runnable() { public void run() { target.setVisibility(View.GONE); } }); getInstrumentation().waitForIdleSync(); Thread.sleep(1000); //Viewを表示 activity.runOnUiThread(new Runnable() { public void run() { button.performClick(); } }); getInstrumentation().waitForIdleSync(); //Viewが表示されているチェック assertEquals(target.getVisibility(), View.VISIBLE); activity.finish(); } public void testOnClickButton02() { ActivityMonitor monitor = new ActivityMonitor("com.dd.sample.Next", null, true); getInstrumentation().addMonitor(monitor); final DragAndDropSample activity = getActivity(); final Button button = (Button) activity .findViewById(R.id.Button02); activity.runOnUiThread(new Runnable() { public void run() { button.performClick(); } }); Activity next = getInstrumentation().waitForMonitorWithTimeout(monitor, 2000); assertEquals(monitor.getHits(),1); if(next != null){ next.finish(); } } /** * イメージをドラッグアンドドロップして消えたイメージを再表示する */ public void testOnTouch() { final DragAndDropSample activity = getActivity(); final ImageView target = (ImageView) activity .findViewById(R.id.ImageView01); //Drag and Drop TouchUtils.dragViewTo(DragAndDropSampleTest.this, target, Gravity.CENTER_HORIZONTAL, activity.currentX + 100, activity.currentY + 100); //Viewが消えてる assertEquals(target.getVisibility(), View.GONE); final Button button = (Button) activity .findViewById(R.id.Button01); try { Thread.sleep(1500); } catch (InterruptedException e) { e.printStackTrace(); } //Viewを表示 activity.runOnUiThread(new Runnable() { public void run() { button.performClick(); } }); //Buttonクリック待ち getInstrumentation().waitForIdleSync(); //Viewが表示された assertEquals(target.getVisibility(), View.VISIBLE); try { Thread.sleep(1500); } catch (InterruptedException e) { e.printStackTrace(); } activity.finish(); } }