Help us understand the problem. What is going on with this article?

UI Automator 2.0でUIテストをする

More than 1 year has passed since last update.

UI Automatorとは

UI AutomatorはGoogleが開発しているAndroid向け自動UIテストフレームワークです。
2015年3月にバージョンが2.0になりGradleに対応しました。
有名なテストフレームワークのAppiumも中ではUI Automatorを実行しています。

Espressoとの違い

GoogleはテストフレームワークとしてEspressoも開発しています。EspressoとUI Automatorの違いとして最も大きいのは、UI Automatorは複数のアプリをテストできるということです。Espressoはアプリのソースコードに紐付いており、アプリのプロジェクトの中に入れる必要があります。それに対してUI Automatorは自分のアプリはもちろんのこと、設定アプリや自分で作ったツールアプリや他社のアプリ、通知バーなど自由に操作することができます。ソースコードと紐付いていないので、別プロジェクトで管理することもできますし、アプリと同じプロジェクトに含めることもできます。またUI Automatorは2.0からEspressoと併用できるようになったため、自分のアプリはEspresso、他のアプリを操作するときはUI Automatorといった書き方が可能です。

UI AutomatorはAndroid 4.3以上のみをサポートしています。まだまだAndroidでは最新のOSのみを対象とはできない状況なので、通常はAndroid 4.3以上のみを対象とていないことがほとんどかと思います。ここはProductFlavorを分けるかプロジェクトごと分けることで対応します。なおEspressoはAndroid 2.2からサポートしており、設定アプリなどを使わないテストのみであれば、Espressoのほうが幅広くテストを行えます。

設定

UI Automatorは2.0からAndroid Support Libraryの一部となり、build.gradleで簡単に使うことができるようになりました。
まずAndroid SDK ManagerからAndroid Support Repositoryをインストールします。

apply plugin: 'com.android.application'

android {
    compileSdkVersion 25
    buildToolsVersion "25.0.2"

    defaultConfig {
        applicationId "com.phicdy.uiautomator2sample"
        minSdkVersion 14
        targetSdkVersion 25
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
    productFlavors {
        production {
            minSdkVersion 14
        }
        uiTest {
            minSdkVersion 18
        }
    }
}

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    testCompile 'junit:junit:4.12'
    compile 'com.android.support:appcompat-v7:25.2.0'
    compile 'com.android.support:design:25.2.0'
    uiTestCompile 'com.android.support.test:runner:0.5'
    uiTestCompile 'com.android.support.test.uiautomator:uiautomator-v18:2.1.2'
}

testInstrumentationRunnerをandroid.support.test.runner.AndroidJUnitRunnerに指定します。これでJUnit4形式でテストが書けるようになります。productFlavorをアプリ用とUI Automator用で分け、UI Automator用のminSdkVersionを18(Android 4.3)に設定します。今回はuiTestとしました。

最後に実行に必要なライブラリであるcom.android.support.test: runner:0.5とcom.android.support.test.uiautomator:uiautomator-v18:2.1.2をuiTestCompileで読み込みます。

テストを書く時や実行時は、Android Studio上のBuild VariantsをuiTestDebugに変更します。

build_variant

これでUI Automatorの設定は終わりです。

テストを書く

テストはsrc/androidTest以下にJUnit4の書き方で追加していきます。
デフォルトでApplicationTestが入っていますが必要ないので消し、新たにテストを追加します。

package com.phicdy.uiautomator2sample;

import android.content.Context;
import android.content.Intent;
import android.support.test.InstrumentationRegistry;
import android.support.test.filters.SdkSuppress;
import android.support.test.runner.AndroidJUnit4;
import android.support.test.uiautomator.By;
import android.support.test.uiautomator.UiDevice;
import android.support.test.uiautomator.UiObject2;
import android.support.test.uiautomator.Until;

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

import static junit.framework.Assert.assertNotNull;
import static junit.framework.Assert.fail;
import static org.hamcrest.core.Is.is;
import static org.junit.Assert.assertThat;

@RunWith(AndroidJUnit4.class)
@SdkSuppress(minSdkVersion = 18)
public class MainUiTest {

    @Test
    public void floatingButtonTest() {
        UiDevice device = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());

        // Launch MainActivity
        Context context = InstrumentationRegistry.getContext();
        Intent intent = context.getPackageManager().getLaunchIntentForPackage("com.phicdy.uiautomator2sample");
        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        context.startActivity(intent);

        // Click floating button
        UiObject2 btn = device.wait(Until.findObject(By.res("com.phicdy.uiautomator2sample:id/fab")), 3000);
        if (btn == null) fail("Floating button was not found");
        btn.click();

        UiObject2 snakeBar = device.wait(Until.findObject(By.res("com.phicdy.uiautomator2sample:id/snackbar_text")), 3000);
        assertNotNull(snakeBar);
        assertThat(snakeBar.getText(), is("Replace with your own action"));
    }
}

今回はプロジェクト作成するときにTabbed Activityで作成したデフォルトのアプリのテストを作ります。
テストステップは以下の通りです。

  1. MainActiivtyを起動
  2. FloatingButtonを押す
  3. SnakeBarが出るので文言が"Replace with your own action"であることを確認

テストの初めにUiDeviceのインスタンスを取得します。
UiDeviceは端末の操作をしたり、UiObject2を取得したり、様々な場面で使います。
UiDeviceを管理するクラスを用意してもいいと思います。

UiDevice device = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());

MainActivity起動部分です。UI Automator 2.0ではContextが使えるようになったのでstartActivity()で起動します。
MainActivityはアプリ起動時に起動されるActivityなので、context.getPackageManager().getLaunchIntentForPackage()で対象のパッケージ名を指定して起動しています。

// Launch MainActivity
Context context = InstrumentationRegistry.getContext();
Intent intent = context.getPackageManager().getLaunchIntentForPackage("com.phicdy.uiautomator2sample");
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(intent);

FloatingButtonをクリックしてSnackBarのUiObject2を取得する部分です。

UiObject2 btn = device.wait(Until.findObject(By.res("com.phicdy.uiautomator2sample:id/fab")), 3000);

UI Automator 2.0では各ViewをUiObject2として取得します。主に取得には、UiDevice#findObject()かUiDevice#wait()を使います。
UiDevice#wait()を使うとViewが描画されるまで待ってくれるのでテストの成功率が上がります。

UiDevice#wait()にはSearchConditionとtimeoutを指定します。
SearchConditionはどの条件で待つかを指定します。Untilという便利なクラスがあるので、これを使ってSearchConditionを作ります。今回は特定のUiObject2が出るまで待つので、Unitl.findObject()を使います。Unitl.findObject()にはBySelectorを指定します。BySelectorはByクラスから生成してUiObject2を特定する条件を指定します。条件はresoruse IDやテキスト、クラス名などで指定します。resource IDが確実なので、アプリ側できるだけresource IDを設定します。Android SDKのtoolsにuiautomatorviewerというツールがあるのでこれで簡単にIDやViewの階層構造を確認できます。Android Studio 2.2からはこういった機能がAndroid Studio自体に追加されるようです。

uiautomatorviewer

UiObject2を取得したらnullチェックをしてエラーハンドリングをした後、クリックします。

if (btn == null) fail("Floating button was not found");
btn.click();

クリックするとSnackBarが出るので、テキスト部分を同様に取得します。
最後にテキスト部分のnullチェックと文言を確認します。

UiObject2 snakeBarText = device.wait(Until.findObject(By.res("com.phicdy.uiautomator2sample:id/snackbar_text")), 3000);
assertNotNull(snakeBarText);
assertThat(snakeBarText.getText(), is("Replace with your own action"));

最後に

UI Automator 2.0を使うことで、UIテストが書けました。
今回は1つのアプリだけを対象としたテストでしたが、例えば設定アプリを起動して機内モードをオンにし、エラーダイアログが出るか確認するといったテストもUI Autoamtor 2.0では可能です。
UIテストを書くことでCIでのリグレッションテストや手動テストの削減ができるので、少しずつ書いていきたいです。

今回のサンプルプロジェクトはこちらです。
以上です。

Why do not you register as a user and use Qiita more conveniently?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away