AndroidのInstrumented Unit Testsを試す

試したのはAndroidの公式の以下のページの内容になります。

Building Instrumented Unit Tests | Android Developers

準備

基本的には手順通りにやれば問題なく出来ます。

  • Android Testing Support Libraryをインストール
  • 画面左下のBuild Variantsタブを選択し、Test ArtifactをAndroid Instrumentation Testsに変更しておく
  • app/src/androidTest/javaディレクトリを作成
  • moduleのbuild.gradleにdependenciesを追記
    • 僕の環境だとhamcrestが入っているとダメでした *1
dependencies {
    androidTestCompile 'com.android.support.test:runner:0.3'
    androidTestCompile 'com.android.support.test:rules:0.3'
    // Set this dependency if you want to use Hamcrest matching
    androidTestCompile 'org.hamcrest:hamcrest-library:1.1'
}
  • 同じくmoduleのbuild.gradle内のtestInstrumentationRunnerをAndroidJUnitRunnerに設定する
android {
    defaultConfig {
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
    }
}

実装

テストクラスは以下のように実装します。
Contextは InstrumentationRegistry から取得出来るようです。

package com.example;

import android.content.Context;
import android.support.test.InstrumentationRegistry;
import android.support.test.runner.AndroidJUnit4;

import junit.framework.Assert;

import org.junit.Test;
import org.junit.runner.RunWith;

@RunWith(AndroidJUnit4.class)
public class SampleTest {

    @Test
    public void testSample() {
        // Contextの取得
        Context c = InstrumentationRegistry.getContext();

        Assert.assertEquals(2, 1 + 1);
    }

}

1つ注意したいのはAndroidJUnit4を使用する時はWorkerThreadで動作するようで、Looper.myLooper()がnullになっているので new Handler() 等のコードを通ると内部で以下のエラーが起きます。

Caused by: java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()
at android.os.Handler.<init>(Handler.java:200)
at android.os.Handler.<init>(Handler.java:114)

WebViewなんかも初期化時にHandlerを生成するらしく、普通に生成しようとすると同様のエラーが出て生成出来ません。

対策としては、Handlerを使ってMainThreadで動作するようにしてあげればOKです。

Handler handler = new Handler(Looper.getMainLooper());
handler.post(new Runnable() {
    @Override
    public void run() {
        // ここにテストコードを書く
    }
});

追記

InstrumentationクラスのrunOnMainSyncメソッドを使えば同じようなことが出来るみたいです。こちらだと同期で実行してくれるのでこのRunnableが終わるまで待ってくれるのでテストを書くならこちらの方がやりやすいかもしれません。

InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
    @Override
    public void run() {
        // ここにテストコードを書く
    }
});

実行

実行はJUnitなどを実行する時と同様にテストしたい対象を右クリックしてAndroid TestのRun (ドロイド君のマークのRun) を実行します。

GenyMotionを使用している場合は、初期状態だとデバイスを立ち上げていても勝手にAndroidエミュレータが立ち上がるようになっていると思うので *2 、Run > Edit Configurations からTarget Deviceを Show chooser dialog に設定しておくと良いと思います。

テスト結果はJUnitのLocal Testingと同様にRunタブに表示されます。

*1:JUnit4のLocal Testingと併用していたからかもしれません

*2:僕の環境ではそうでした