gradle-android-test-plugin+Robolectricでテスト

2014-02-16T00:00:00+00:00 Android Java robolectric

参考: http://starzero.hatenablog.com/entry/2013/12/22/201149

gradle-android-test-pluginがいつの間にかDEPRECATEDになっているっていうのを知らずに(ry まぁ他に手段ってのもなかなかなものなので、とりあえずRobolectric演習的に使ってみる

以前、「DialogFragmentのテスト」っていうのを書いてるけど、これをRobolectricでやるにはどんな感じなのか的な事をやってみた

その前に

上記参考先にも書いてますが、どうもRobolectric2.2だと上手く行かない模様なので事前事項として一応書いておく。無論、Robolectric2.3-SNAPSHOTではAPI Level 19はまだサポートされていない模様

構造

├── app
│   ├── build.gradle
│   └── src
│       ├── main
│       │   ├── AndroidManifest.xml
│       │   ├── java
│       │   │   └── sample
│       │   │       └── test
│       │   │           └── MainActivity.java
│       │   └── res
│       │       ├── drawable-hdpi
│       │       ├── drawable-ldpi
│       │       ├── drawable-mdpi
│       │       ├── drawable-xhdpi
│       │       ├── drawable-xxhdpi
│       │       └── values
│       │           └── strings.xml
│       └── test
│           └── java
│               └── sample
│                   └── test
│                       ├── MainActivityTest.java
│                       └── RobolectricGradleTestRunner.java
├── build.gradle
├── libraries
│   └── sample_library
│       ├── build.gradle
│       └── src
│           └── main
│               ├── AndroidManifest.xml
│               ├── java
│               │   └── sample
│               │       └── test
│               │           └── SampleDialogFragment.java
│               └── res
│                   └── layout
│                       └── dialog.xml
└── settings.gradle

RobolectricGradleTestRunner.javaはgithubからポチってくる

settings.gradleは

include ":app"
include ":libraries:sample_library"

な感じだけなので(ry

AndroidManifest.xmlではAPI Level19がサポートされてないので指定しない

build.gradle

buildscript {
    repositories {
        mavenCentral()

        maven {
            url "https://oss.sonatype.org/content/repositories/snapshots"
        }
    }

    dependencies {
        classpath "com.android.tools.build:gradle:0.8.+"
        classpath "com.squareup.gradle:gradle-android-test-plugin:0.9.1-SNAPSHOT"
    }
}

allprojects {
    repositories {
        mavenCentral()

        // Robolectric2.3-SNAPSHOTを取ってくるのに必要
        maven {
            url "https://oss.sonatype.org/content/repositories/snapshots"
        }
    }
}

libraries/sample_library/build.gradle

apply plugin: "android-library"

dependencies {
    compile "com.google.android:support-v4:r7"
}

android {
    compileSdkVersion 18
    buildToolsVersion "19.0.1"
}

libraries/sample_library/src/main/java/sample/test/SampleDialogFragment.java

package sample.test;

import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.DialogInterface;
import android.os.Bundle;
import android.os.Handler;
import android.support.v4.app.DialogFragment;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.EditText;
import android.widget.Toast;

/**
R.layout.dialog
<?xml version="1.0" ?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <EditText
        android:id="@+id/dialog_text"
        android:layout_width="fill_parent"
        android:layout_height="match_parent" />

</LinearLayout>
 */

public class SampleDialogFragment extends DialogFragment
    implements DialogInterface.OnClickListener {

    private Handler mHandler = new Handler();
    private Activity mActivity;
    private View mView;

    @Override
    public void onCreate(Bundle bundle) {
        super.onCreate(bundle);
        mActivity = getActivity();
        mView = mActivity.getLayoutInflater().inflate(R.layout.dialog, null);
    }

    @Override
    public Dialog onCreateDialog(Bundle bundle) {
        AlertDialog.Builder builder = new AlertDialog.Builder(mActivity);
        builder.setView(mView);
        builder.setPositiveButton("OK", this);

        return builder.create();
    }

    @Override
    public void onClick(DialogInterface dialog, int which) {
        mHandler.postDelayed(
            new Thread() {
                @Override
                public void run() {
                    final EditText editor = (EditText)mView.findViewById(R.id.dialog_text);
                    Toast.makeText(
                        mActivity,
                        editor.getText().toString(),
                        Toast.LENGTH_LONG
                    ).show();
                }
            },
            3000
        );
    }
}

適当にDialogを出してそこで入力させたテキストをToastで出すだけ。つまりテスト要件も

  • DialogFragmentを取って
  • Dialog取って
  • EditText取って (ついでに値ぶち込んで)
  • Positive Buttonクリックさせて
  • Toastが出るか

app/build.gradle

apply plugin: "android"
apply plugin: "android-test"

dependencies {
    compile project(":libraries:sample_library")
    compile "com.actionbarsherlock:actionbarsherlock:4.4.0@aar"

    testCompile "junit:junit:4.11"
    testCompile "org.hamcrest:hamcrest-all:1.3"
    testCompile "org.robolectric:robolectric:2.3-SNAPSHOT"
}

android {
    compileSdkVersion 18
    buildToolsVersion "19.0.1"
}

app/src/main/java/sample/test/MainActivity.java

package sample.test;

import android.os.Bundle;
import android.support.v4.app.DialogFragment;

import com.actionbarsherlock.app.SherlockFragmentActivity;

public class MainActivity extends SherlockFragmentActivity {
    @Override
    protected void onCreate(Bundle bundle) {
        super.onCreate(bundle);
        DialogFragment fragment = new SampleDialogFragment();
        fragment.show(getSupportFragmentManager(), "sample_dialog_fragment");
    }
}

まーただDialogFragmentをshowしているだけ。ここまでがアプリ側で、これからこれをRobolectricでテストする

app/src/test/java/sample/test/MainActivityTest.java

※さっきも書いたけどRobolectricGradleTestCaseポチってくるの忘れずに

package sample.test;

import android.app.AlertDialog;
import android.content.DialogInterface;
import android.support.v4.app.FragmentManager;
import android.widget.Button;
import android.widget.EditText;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.Robolectric;
import org.robolectric.shadows.ShadowToast;

import static org.junit.Assert.*;
import static org.hamcrest.Matchers.*;

@RunWith(RobolectricGradleTestRunner.class)
public class MainActivityTest {

    @Test
    public void test1() {
        MainActivity activity = Robolectric.buildActivity(MainActivity.class)
            .create()
            .start()
            .get();

        // DialogFragmentをポチってくる
        FragmentManager fm = activity.getSupportFragmentManager();
        SampleDialogFragment fragment = (SampleDialogFragment)fm.findFragmentByTag("sample_dialog_fragment");
        assertThat(fragment, notNullValue());

        // Dialogをポチってくる
        AlertDialog dialog = (AlertDialog)fragment.getDialog();
        assertThat(dialog, notNullValue());

        // EditTextをポチってきてテキストを設定しておく
        EditText dialogTextView = (EditText)dialog.findViewById(R.id.dialog_text);
        assertThat(dialogTextView, notNullValue());
        dialogTextView.setText("hoge@gradle-robolectric");

        // Positive Buttonをポチってクリックさせてやる
        Button positiveButton = dialog.getButton(DialogInterface.BUTTON_POSITIVE);
        positiveButton.performClick();

        // 実体はShadowLooper.idleMainLooper(interval);
        // Android標準テスト方式で言えばwaitForIdleSync的なAPIなんじゃないかと
        Robolectric.idleMainLooper(5000);

        // わざとテストをfailさせてる
        assertThat(ShadowToast.getTextOfLatestToast(), is("fuga"));
    }
}

終わり。普通にtestタスク動かすと

ってな感じで設定したテキストが反映された結果のToastが取得できている模様

再度言っておくけど、gradle-android-test-plugin自体はDEPRECATEDになっているので

んまぁRobolectric演習的な感じでやってみたっていう事で本ネタ終了

追記

以下で書いてるコードは https://github.com/kinjouj/my-gradle-android-project のdemoブランチに置いてあります

supervisord (4) - eventlistener - gulpでmocha-phantomjs+jscoverage