UI Automator は、システムアプリとインストール済みアプリにまたがるアプリ間の UI 機能テストに適した UI テスト フレームワークです。UI Automator API を使用すると、どの Activity
がフォーカスされているかにかかわらず、デバイス上の表示要素を操作できるため、テストデバイスで設定メニューやアプリ ランチャーを開くなどのオペレーションが可能になります。テストでは、UI コンポーネントやそのコンテンツ説明に表示されるテキストなど、使い勝手のよい記述子を使用して UI コンポーネントを検索できます。
UI Automator テスト フレームワークは、インストルメンテーション ベースの API であり、AndroidJUnitRunner
テストランナーと連携して機能します。テストコードがターゲット アプリの細かい内部実装に依存しておらず、不透明なボックス スタイルの自動化テストの作成に適しています。
UI Automator テスト フレームワークの主要な機能は次のとおりです。
- ターゲット デバイスの状態情報を取得してオペレーションを実行する API。詳細については、デバイスの状態にアクセスするをご覧ください。
- 複数のアプリにまたがる UI テストをサポートする API。詳細については、UI Automator API をご覧ください。
デバイスの状態にアクセスする
UI Automator テスト フレームワークには、ターゲット アプリが実行されているデバイスにアクセスしてオペレーションを実行するための UiDevice
クラスが用意されています。このクラスのメソッドを呼び出すことにより、その時点でのデバイスの向きや表示サイズなどのデバイス プロパティにアクセスできます。UiDevice
クラスでは、次のアクションも実行できます。
- デバイスの回転を変更する。
- 「音量大」などのハードウェア キーを押す。
- 「戻る」ボタン、ホームボタン、メニューボタンをクリックする。
- 通知シェードを開く。
- 現在のウィンドウのスクリーンショットを撮る。
たとえば、ホームボタンの押下をシミュレートするには、UiDevice.pressHome()
メソッドを呼び出します。
UI Automator API
UI Automator API を使用すると、ターゲットとするアプリの詳細な実装を把握していなくても、堅牢なテストを作成することが可能になります。以下の API により、複数のアプリにまたがる UI コンポーネントをキャプチャして操作できます。
UiObject2
: デバイス上に表示される UI 要素を表します。BySelector
: UI 要素の照合条件を指定します。By
:BySelector
を簡潔に構築します。Configurator
: UI Automator テストを実行するための主要なパラメータを設定できます。
たとえば、次のコードは、デバイスで Gmail アプリを開くテスト スクリプトの作成方法を示しています。
Kotlin
device = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()) device.pressHome() val gmail: UiObject2 = device.findObject(By.text("Gmail")) // Perform a click and wait until the app is opened. val opened: Boolean = gmail.clickAndWait(Until.newWindow(), 3000) assertThat(opened).isTrue()
Java
device = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()); device.pressHome(); UiObject2 gmail = device.findObject(By.text("Gmail")); // Perform a click and wait until the app is opened. Boolean opened = gmail.clickAndWait(Until.newWindow(), 3000); assertTrue(opened);
UI Automator をセットアップする
UI Automator を使用して UI テストを作成する前に、AndroidX Test 用にプロジェクトをセットアップするの説明に従って、テスト ソースコードの場所とプロジェクトの依存関係を構成してください。
Android アプリ モジュールの build.gradle
ファイルで、UI Automator ライブラリへの依存関係の参照を設定する必要があります。
Kotlin
dependencies {
...
androidTestImplementation("androidx.test.uiautomator:uiautomator:2.3.0")
}
Groovy
dependencies {
...
androidTestImplementation 'androidx.test.uiautomator:uiautomator:2.3.0'
}
UI Automator テストを最適化するには、最初にターゲット アプリの UI コンポーネントを検査して、それらがアクセス可能であることを確認する必要があります。最適化のヒントについては、次の 2 つのセクションで説明します。
デバイス上の UI を検査する
テストを設計する前に、デバイスに表示される UI コンポーネントを検査します。UI Automator テストでこれらのコンポーネントにアクセスできるようにするには、これらのコンポーネントに表示テキストのラベルと android:contentDescription
値の両方またはいずれかが存在することを確認します。
uiautomatorviewer
ツールは、レイアウト階層を検査し、デバイスのフォアグラウンドに表示される UI コンポーネントのプロパティを確認するのに便利な視覚的インターフェースを提供します。この情報により、UI Automator できめ細かいテストを作成することが可能になります。たとえば、特定の表示プロパティに対応する UI セレクタを作成できます。
uiautomatorviewer
ツールを起動するには:
- 物理デバイスでターゲット アプリを起動します。
- デバイスを開発マシンに接続します。
- ターミナル ウィンドウを開いて、
<android-sdk>/tools/
ディレクトリに移動します。 - 次のコマンドでツールを実行します。
$ uiautomatorviewer
アプリの UI プロパティを表示するには:
uiautomatorviewer
インターフェースで、[Device Screenshot] ボタンをクリックします。- 左側のパネルのスナップショットにカーソルを合わせると、
uiautomatorviewer
ツールによって特定された UI コンポーネントが表示されます。プロパティは右下のパネルにリストされ、レイアウト階層は右上のパネルに表示されます。 - 必要に応じて、[Toggle NAF Nodes] ボタンをクリックして、UI Automator からアクセスできない UI コンポーネントを表示できます。こうしたコンポーネントについては、限られた情報しか参照できない可能性があります。
Android が提供する UI コンポーネントの一般的なタイプについては、ユーザー インターフェースをご覧ください。
アクティビティのユーザー補助機能を確認する
UI Automator テスト フレームワークは、Android のユーザー補助機能を実装したアプリのパフォーマンスを向上させます。View
型の UI 要素か、SDK の View
のサブクラスを使用する場合、これらのクラスはすでにユーザー補助サポートを実装しているため、改めて実装する必要はありません。
ただし、一部のアプリでは、カスタム UI 要素を使用して、より豊かなユーザー エクスペリエンスを提供できます。そのような要素は、自動的にはユーザー補助サポートを提供しません。SDK 以外の View
のサブクラスのインスタンスがアプリに含まれている場合は、次の手順を実施して、それらの要素にユーザー補助機能を追加してください。
- ExploreByTouchHelper を拡張する具象クラスを作成します。
- setAccessibilityDelegate() を呼び出して、新しいクラスのインスタンスを特定のカスタム UI 要素に関連付けます。
カスタムビュー要素にユーザー補助機能を追加するためのガイダンスについては、アクセス可能なカスタムビューの構築をご覧ください。Android のユーザー補助に関する一般的なおすすめの方法については、アプリのユーザー補助機能を強化するをご覧ください。
UI Automator テストクラスを作成する
UI Automator テストクラスは、JUnit 4 テストクラスと同じ方法で作成する必要があります。JUnit 4 テストクラスの作成方法と、JUnit 4 アサーションおよびアノテーションの使用方法の詳細については、インストゥルメント化単体テストクラスを作成するをご覧ください。
テストクラス定義の先頭に @RunWith(AndroidJUnit4.class) アノテーションを追加します。また、AndroidX Test で提供される AndroidJUnitRunner クラスを、デフォルトのテストランナーとして指定する必要があります。この手順の詳細については、デバイスまたはエミュレータで UI Automator テストを実行するをご覧ください。
UI Automator テストクラスに、次のプログラミング モデルを実装します。
- getInstance() メソッドを呼び出し、引数として Instrumentation オブジェクトを渡すことにより、テスト対象のデバイスにアクセスするための
UiDevice
オブジェクトを取得します。 - findObject() メソッドを呼び出して、デバイスに表示される UI コンポーネント(フォアグラウンドの現在のビューなど)にアクセスするための
UiObject2
オブジェクトを取得します。 UiObject2
メソッドを呼び出して、その UI コンポーネントで実行する特定のユーザー インタラクションをシミュレートします。たとえば、scrollUntil() を呼び出してスクロールし、setText() を呼び出してテキスト フィールドを編集します。必要に応じてステップ 2 とステップ 3 の API を繰り返し呼び出すことにより、複数の UI コンポーネントまたは一連のユーザー アクションを含む複雑なユーザー インタラクションをテストできます。- ユーザー インタラクションが実行された後、UI に予期される状態または動作が反映されていることを確認します。
上記の手順については、以降のセクションで詳しく説明します。
UI コンポーネントにアクセスする
UiDevice
オブジェクトは、デバイスの状態にアクセスして操作するための主要な方法です。テストでは、UiDevice
メソッドを呼び出して、現在のデバイスの向きや表示サイズなど、さまざまなプロパティの状態をチェックできます。テストでは、UiDevice
オブジェクトを使用して、デバイスレベルのアクション(デバイスに特定の回転をさせる、ハードウェアの D-pad ボタンを押す、ホームボタンやメニューボタンを押すなど)を実行できます。
デバイスのホーム画面からテストを始めることをおすすめします。ホーム画面(またはデバイスで選択したその他のスタート地点)から、UI Automator API が提供するメソッドを呼び出して、特定の UI 要素を選択および操作できます。
次のコード スニペットは、テストで UiDevice
のインスタンスを取得して、ホームボタンの押下をシミュレートする方法を示しています。
Kotlin
import org.junit.Before import androidx.test.runner.AndroidJUnit4 import androidx.test.uiautomator.UiDevice import androidx.test.uiautomator.By import androidx.test.uiautomator.Until ... private const val BASIC_SAMPLE_PACKAGE = "com.example.android.testing.uiautomator.BasicSample" private const val LAUNCH_TIMEOUT = 5000L private const val STRING_TO_BE_TYPED = "UiAutomator" @RunWith(AndroidJUnit4::class) @SdkSuppress(minSdkVersion = 18) class ChangeTextBehaviorTest2 { private lateinit var device: UiDevice @Before fun startMainActivityFromHomeScreen() { // Initialize UiDevice instance device = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()) // Start from the home screen device.pressHome() // Wait for launcher val launcherPackage: String = device.launcherPackageName assertThat(launcherPackage, notNullValue()) device.wait( Until.hasObject(By.pkg(launcherPackage).depth(0)), LAUNCH_TIMEOUT ) // Launch the app val context = ApplicationProvider.getApplicationContext<Context>() val intent = context.packageManager.getLaunchIntentForPackage( BASIC_SAMPLE_PACKAGE).apply { // Clear out any previous instances addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK) } context.startActivity(intent) // Wait for the app to appear device.wait( Until.hasObject(By.pkg(BASIC_SAMPLE_PACKAGE).depth(0)), LAUNCH_TIMEOUT ) } }
Java
import org.junit.Before; import androidx.test.runner.AndroidJUnit4; import androidx.test.uiautomator.UiDevice; import androidx.test.uiautomator.By; import androidx.test.uiautomator.Until; ... @RunWith(AndroidJUnit4.class) @SdkSuppress(minSdkVersion = 18) public class ChangeTextBehaviorTest { private static final String BASIC_SAMPLE_PACKAGE = "com.example.android.testing.uiautomator.BasicSample"; private static final int LAUNCH_TIMEOUT = 5000; private static final String STRING_TO_BE_TYPED = "UiAutomator"; private UiDevice device; @Before public void startMainActivityFromHomeScreen() { // Initialize UiDevice instance device = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()); // Start from the home screen device.pressHome(); // Wait for launcher final String launcherPackage = device.getLauncherPackageName(); assertThat(launcherPackage, notNullValue()); device.wait(Until.hasObject(By.pkg(launcherPackage).depth(0)), LAUNCH_TIMEOUT); // Launch the app Context context = ApplicationProvider.getApplicationContext(); final Intent intent = context.getPackageManager() .getLaunchIntentForPackage(BASIC_SAMPLE_PACKAGE); // Clear out any previous instances intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK); context.startActivity(intent); // Wait for the app to appear device.wait(Until.hasObject(By.pkg(BASIC_SAMPLE_PACKAGE).depth(0)), LAUNCH_TIMEOUT); } }
この例では、@SdkSuppress(minSdkVersion = 18) ステートメントにより、UI Automator フレームワークの要件に従って、Android 4.3(API レベル 18)以上のデバイスでのみテストが実行されるようにしています。
findObject()
メソッドを使用して、特定のセレクタ条件に一致するビューを表す UiObject2
を取得します。必要に応じて、アプリテストの他の部分で作成した UiObject2
インスタンスを再利用できます。UI Automator テスト フレームワークは、テストが UiObject2
インスタンスを使用して UI 要素をクリックするかプロパティをクエリするたびに、その時点で一致する表示を検索することにご注意ください。
次のスニペットは、アプリの [Cancel] ボタンと [OK] ボタンを表す UiObject2
インスタンスをテストで構築する方法を示しています。
Kotlin
val okButton: UiObject2 = device.findObject( By.text("OK").clazz("android.widget.Button") ) // Simulate a user-click on the OK button, if found. if (okButton != null) { okButton.click() }
Java
UiObject2 okButton = device.findObject( By.text("OK").clazz("android.widget.Button") ); // Simulate a user-click on the OK button, if found. if (okButton != null) { okButton.click(); }
セレクタを指定する
アプリの特定の UI コンポーネントにアクセスするには、By
クラスを使用して BySelector
インスタンスを作成します。BySelector
は、表示された UI の特定の要素に対するクエリを表します。
一致する要素が複数見つかった場合、レイアウト階層内で最初に一致した要素がターゲット UiObject2
として返されます。BySelector
を構築する際は、複数のプロパティを連結して検索を絞り込むことができます。一致する UI 要素が見つからない場合は、null
が返されます。
hasChild()
メソッドまたは hasDescendant()
メソッドを使用して、複数の BySelector
インスタンスをネストできます。たとえば、次のコード例は、テキスト プロパティを持つ子 UI 要素を持つ最初の ListView
を検索する方法を示しています。
Kotlin
val listView: UiObject2 = device.findObject( By.clazz("android.widget.ListView") .hasChild( By.text("Apps") ) )
Java
UiObject2 listView = device.findObject( By.clazz("android.widget.ListView") .hasChild( By.text("Apps") ) );
セレクタの条件としてオブジェクトの状態を指定する方法が適切な場合もあります。たとえば、すべてのチェック済み要素のリストを選択してチェックを解除するには、引数を true に設定して checked()
メソッドを呼び出します。
アクションを実行する
テストで UiObject2
オブジェクトを取得したら、UiObject2
クラスのメソッドを呼び出して、そのオブジェクトが表す UI コンポーネントでユーザー インタラクションを実行できます。次のようなアクションの指定が可能です。
click()
: UI 要素の表示境界の中心をクリックします。drag()
: このオブジェクトを任意の座標にドラッグします。setText()
: 編集可能フィールドの内容をクリアした後、フィールドにテキストを設定します。一方、clear()
メソッドは、編集可能フィールドの既存のテキストをクリアします。swipe()
: 指定された方向にスワイプ アクションを実行します。scrollUntil()
:Condition
またはEventCondition
が満たされるまで、指定された方向にスクロール アクションを実行します。
UI Automator テスト フレームワークでは、getContext()
で Context オブジェクトを取得することにより、シェルコマンドを使用せずに Intent を送信したり Activity を起動したりすることができます。
次のスニペットは、テストで Intent を使用してテスト対象のアプリを起動する方法を示しています。このアプローチは、電卓アプリのテストのみが重要で、アプリを起動するランチャーは無視してよい場合に役立ちます。
Kotlin
fun setUp() { ... // Launch a simple calculator app val context = getInstrumentation().context val intent = context.packageManager.getLaunchIntentForPackage(CALC_PACKAGE).apply { addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK) } // Clear out any previous instances context.startActivity(intent) device.wait(Until.hasObject(By.pkg(CALC_PACKAGE).depth(0)), TIMEOUT) }
Java
public void setUp() { ... // Launch a simple calculator app Context context = getInstrumentation().getContext(); Intent intent = context.getPackageManager() .getLaunchIntentForPackage(CALC_PACKAGE); intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK); // Clear out any previous instances context.startActivity(intent); device.wait(Until.hasObject(By.pkg(CALC_PACKAGE).depth(0)), TIMEOUT); }
結果を検証する
InstrumentationTestCase は TestCase を拡張しているため、標準の JUnit Assert メソッドを使用して、アプリ内の UI コンポーネントが期待される結果を返すかどうかをテストできます。
次のスニペットは、テストで電卓アプリのボタンの位置を特定して順にクリックし、正しい結果が表示されることを確認する方法を示しています。
Kotlin
private const val CALC_PACKAGE = "com.myexample.calc" fun testTwoPlusThreeEqualsFive() { // Enter an equation: 2 + 3 = ? device.findObject(By.res(CALC_PACKAGE, "two")).click() device.findObject(By.res(CALC_PACKAGE, "plus")).click() device.findObject(By.res(CALC_PACKAGE, "three")).click() device.findObject(By.res(CALC_PACKAGE, "equals")).click() // Verify the result = 5 val result: UiObject2 = device.findObject(By.res(CALC_PACKAGE, "result")) assertEquals("5", result.text) }
Java
private static final String CALC_PACKAGE = "com.myexample.calc"; public void testTwoPlusThreeEqualsFive() { // Enter an equation: 2 + 3 = ? device.findObject(By.res(CALC_PACKAGE, "two")).click(); device.findObject(By.res(CALC_PACKAGE, "plus")).click(); device.findObject(By.res(CALC_PACKAGE, "three")).click(); device.findObject(By.res(CALC_PACKAGE, "equals")).click(); // Verify the result = 5 UiObject2 result = device.findObject(By.res(CALC_PACKAGE, "result")); assertEquals("5", result.getText()); }
デバイスまたはエミュレータで UI Automator テストを実行する
UI Automator テストは、Android Studio またはコマンドラインから実行できます。プロジェクトで、デフォルトのインストゥルメンテーション ランナーとして AndroidJUnitRunner
を指定してください。
その他の例
システム UI を操作する
次のコード スニペットに示すように、UI Automator は、アプリ外のシステム要素など、画面上のすべての要素を操作できます。
Kotlin
// Opens the System Settings. device = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()) device.executeShellCommand("am start -a android.settings.SETTINGS")
Java
// Opens the System Settings. device = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()); device.executeShellCommand("am start -a android.settings.SETTINGS");
Kotlin
// Opens the notification shade. device = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()) device.openNotification()
Java
// Opens the notification shade. device = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()); device.openNotification();
Kotlin
// Opens the Quick Settings shade. device = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()) device.openQuickSettings()
Java
// Opens the Quick Settings shade. device = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()); device.openQuickSettings();
Kotlin
// Get the system clock. device = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()) UiObject2 clock = device.findObject(By.res("com.android.systemui:id/clock")) print(clock.getText())
Java
// Get the system clock. device = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()); UiObject2 clock = device.findObject(By.res("com.android.systemui:id/clock")); print(clock.getText());
遷移を待つ

画面遷移には時間がかかり、その所要時間を予測することはできません。そのため、UI Automator は操作の実行後に待機する必要があります。UI Automator には、このための複数の方法が用意されています。
UiDevice.performActionAndWait(Runnable action, EventCondition<U> condition, long timeout)
: たとえば、ボタンをクリックして新しいウィンドウが表示されるまで待つには、device.performActionAndWait(() -> button.click(), Until.newWindow(), timeout)
を呼び出します。UiDevice.wait(Condition<Object, U> condition, long timeout)
: たとえば、デバイスに特定のUiObject2
が存在するまで待機するには、device.wait(Until.hasObject(By.text("my_text")), timeout);
を呼び出します。UiObject2.wait(@NonNull Condition<Object, U> condition, long timeout)
: たとえば、チェックボックスがオンになるまで待機するには、checkbox.wait(Until.checked(true), timeout);
を呼び出します。UiObject2.clickAndWait(@NonNull EventCondition<U> condition, long timeout)
: たとえば、ボタンをクリックして新しいウィンドウが表示されるまで待つには、button.clickAndWait(Until.newWindow(), timeout);
を呼び出します。UiObject2.scrollUntil(@NonNull Direction direction, @NonNull Condition<Object, U> condition)
: たとえば、新しいオブジェクトが表示されるまで下にスクロールするには、object.scrollUntil(Direction.DOWN, Until.hasObject(By.text('new_obj')));
を呼び出します。UiObject2.scrollUntil(@NonNull Direction direction, @NonNull EventCondition<U> condition)
: たとえば、一番下までスクロールするには、object.scrollUntil(Direction.DOWN, Until.scrollFinished(Direction.DOWN));
を呼び出します。
次のコード スニペットは、UI Automator を使用して、遷移を待機する performActionAndWait()
メソッドを使用して、システム設定でサイレント モードをオフにする方法を示しています。
Kotlin
@Test @SdkSuppress(minSdkVersion = 21) @Throws(Exception::class) fun turnOffDoNotDisturb() { device = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()) device.performActionAndWait({ try { device.executeShellCommand("am start -a android.settings.SETTINGS") } catch (e: IOException) { throw RuntimeException(e) } }, Until.newWindow(), 1000) // Check system settings has been opened. Assert.assertTrue(device.hasObject(By.pkg("com.android.settings"))) // Scroll the settings to the top and find Notifications button var scrollableObj: UiObject2 = device.findObject(By.scrollable(true)) scrollableObj.scrollUntil(Direction.UP, Until.scrollFinished(Direction.UP)) val notificationsButton = scrollableObj.findObject(By.text("Notifications")) // Click the Notifications button and wait until a new window is opened. device.performActionAndWait({ notificationsButton.click() }, Until.newWindow(), 1000) scrollableObj = device.findObject(By.scrollable(true)) // Scroll down until it finds a Do Not Disturb button. val doNotDisturb = scrollableObj.scrollUntil( Direction.DOWN, Until.findObject(By.textContains("Do Not Disturb")) ) device.performActionAndWait({ doNotDisturb.click() }, Until.newWindow(), 1000) // Turn off the Do Not Disturb. val turnOnDoNotDisturb = device.findObject(By.text("Turn on now")) turnOnDoNotDisturb?.click() Assert.assertTrue(device.wait(Until.hasObject(By.text("Turn off now")), 1000)) }
Java
@Test @SdkSuppress(minSdkVersion = 21) public void turnOffDoNotDisturb() throws Exception{ device = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()); device.performActionAndWait(() -> { try { device.executeShellCommand("am start -a android.settings.SETTINGS"); } catch (IOException e) { throw new RuntimeException(e); } }, Until.newWindow(), 1000); // Check system settings has been opened. assertTrue(device.hasObject(By.pkg("com.android.settings"))); // Scroll the settings to the top and find Notifications button UiObject2 scrollableObj = device.findObject(By.scrollable(true)); scrollableObj.scrollUntil(Direction.UP, Until.scrollFinished(Direction.UP)); UiObject2 notificationsButton = scrollableObj.findObject(By.text("Notifications")); // Click the Notifications button and wait until a new window is opened. device.performActionAndWait(() -> notificationsButton.click(), Until.newWindow(), 1000); scrollableObj = device.findObject(By.scrollable(true)); // Scroll down until it finds a Do Not Disturb button. UiObject2 doNotDisturb = scrollableObj.scrollUntil(Direction.DOWN, Until.findObject(By.textContains("Do Not Disturb"))); device.performActionAndWait(()-> doNotDisturb.click(), Until.newWindow(), 1000); // Turn off the Do Not Disturb. UiObject2 turnOnDoNotDisturb = device.findObject(By.text("Turn on now")); if(turnOnDoNotDisturb != null) { turnOnDoNotDisturb.click(); } assertTrue(device.wait(Until.hasObject(By.text("Turn off now")), 1000)); }
参考情報
Android テストで UI Automator を使用する方法の詳細については、次のリソースをご覧ください。
リファレンス ドキュメント:
サンプル
- BasicSample: UI Automator の基本的なサンプル。