UI Automator は、システムとインストール済みのアプリにまたがるアプリ間の UI 機能テストに適した UI テスト フレームワークです。UI Automator API を使用すると、フォーカスされている Activity
に関係なく、デバイス上の表示要素を操作できるため、テストデバイスで設定メニューやアプリ ランチャーを開くなどの操作を実行できます。テストでは、コンポーネントに表示されるテキストやコンテンツの説明などの便利な記述子を使用して、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-alpha03')
}
Groovy
dependencies {
...
androidTestImplementation 'androidx.test.uiautomator:uiautomator:2.3.0-alpha03'
}
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() メソッドを呼び出して、
UiObject2
オブジェクトを取得し、デバイスに表示されている UI コンポーネント(フォアグラウンドの現在のビューなど)にアクセスします。 UiObject2
メソッドを呼び出して、その UI コンポーネントで実行する特定のユーザー操作をシミュレートします。たとえば、スクロールするには scroll until() を呼び出します。テキスト フィールドを編集する場合は 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 を使用してテスト対象のアプリを起動する方法を示しています。この方法は、電卓アプリのテストにのみ関心があり、ランチャーは対象外の場合に有用です。
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(device.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 の基本的なサンプル。