UI Automator 테스트 프레임워크는 사용자 앱과 시스템 앱과 상호작용하는 UI 테스트를 빌드하기 위한 API 세트를 제공합니다.
최신 UI Automator 테스트 소개
UI Automator 2.4에서는 Android용 UI 테스트 작성을 간소화하는 간소화된 Kotlin 친화적인 도메인 특정 언어 (DSL)가 도입되었습니다. 이 새로운 API 노출 영역은 술어 기반 요소 찾기와 앱 상태에 대한 명시적 제어에 중점을 둡니다. 이를 사용하여 유지관리 가능하고 안정적인 자동 테스트를 만드세요.
UI Automator를 사용하면 앱 프로세스 외부에서 앱을 테스트할 수 있습니다. 이렇게 하면 축소 기능이 적용된 출시 버전을 테스트할 수 있습니다. UI Automator는 Macrobenchmark 테스트를 작성할 때도 유용합니다.
최신 접근 방식의 주요 기능은 다음과 같습니다.
- 더 깔끔하고 표현력이 뛰어난 테스트 코드를 위한 전용
uiAutomator
테스트 범위 - 명확한 술어로 UI 요소를 찾는
onElement
,onElements
,onElementOrNull
와 같은 메서드 - 조건부 요소
onElement*(timeoutMs: Long = 10000)
의 기본 제공 대기 메커니즘 waitForStable
및waitForAppToBeVisible
과 같은 명시적 앱 상태 관리- 다중 창 테스트 시나리오를 위해 접근성 창 노드와 직접 상호작용
- 시각적 테스트 및 디버깅을 위한 기본 제공 스크린샷 기능과
ResultsReporter
프로젝트 설정
최신 UI Automator API를 사용하려면 프로젝트의 build.gradle.kts
파일을 업데이트하여 최신 종속 항목을 포함하세요.
Kotlin
dependencies {
...
androidTestImplementation("androidx.test.uiautomator:uiautomator:2.4.0-alpha05")
}
Groovy
dependencies {
...
androidTestImplementation "androidx.test.uiautomator:uiautomator:2.4.0-alpha05"
}
핵심 API 개념
다음 섹션에서는 최신 UI Automator API의 핵심 개념을 설명합니다.
uiAutomator 테스트 범위
uiAutomator { ... }
블록 내에서 모든 새 UI Automator API에 액세스합니다. 이 함수는 테스트 작업을 위한 간결하고 유형 안전한 환경을 제공하는 UiAutomatorTestScope
를 만듭니다.
uiAutomator {
// All your UI Automator actions go here
startApp("com.example.targetapp")
onElement { textAsString() == "Hello, World!" }.click()
}
UI 요소 찾기
프레디케이트와 함께 UI Automator API를 사용하여 UI 요소를 찾습니다. 이러한 술어를 사용하면 텍스트, 선택 상태, 포커스 상태, 콘텐츠 설명과 같은 속성의 조건을 정의할 수 있습니다.
onElement { predicate }
: 기본 제한 시간 내에 술어와 일치하는 첫 번째 UI 요소를 반환합니다. 일치하는 요소를 찾지 못하면 함수에서 예외가 발생합니다.// Find a button with the text "Submit" and click it onElement { textAsString() == "Submit" }.click() // Find a UI element by its resource ID onElement { id == "my_button_id" }.click() // Allow a permission request watchFor(PermissionDialog) { clickAllow() }
onElementOrNull { predicate }
:onElement
와 비슷하지만, 함수가 제한 시간 내에 일치하는 요소를 찾지 못하면null
를 반환합니다. 예외가 발생하지는 않습니다. 선택적 요소에 이 메서드를 사용하세요.val optionalButton = onElementOrNull { textAsString() == "Skip" } optionalButton?.click() // Click only if the button exists
onElements { predicate }
: 하나 이상의 UI 요소가 지정된 술어와 일치할 때까지 기다린 후 일치하는 모든 UI 요소의 목록을 반환합니다.// Get all items in a list Ui element val listItems = onElements { className == "android.widget.TextView" && isClickable } listItems.forEach { it.click() }
다음은 onElement
호출 사용에 관한 몇 가지 도움말입니다.
중첩된 요소의
onElement
호출 연결:onElement
호출을 연결하여 상위-하위 계층 구조에 따라 다른 요소 내에서 요소를 찾을 수 있습니다.// Find a parent Ui element with ID "first", then its child with ID "second", // then its grandchild with ID "third", and click it. onElement { id == "first" } .onElement { id == "second" } .onElement { id == "third" } .click()
밀리초를 나타내는 값을 전달하여
onElement*
함수의 시간 제한을 지정합니다.// Find a Ui element with a zero timeout (instant check) onElement(0) { id == "something" }.click() // Find a Ui element with a custom timeout of 10 seconds onElement(10_000) { textAsString() == "Long loading text" }.click()
UI 요소와 상호작용
클릭을 시뮬레이션하거나 수정 가능한 필드에 텍스트를 설정하여 UI 요소와 상호작용합니다.
// Click a Ui element
onElement { textAsString() == "Tap Me" }.click()
// Set text in an editable field
onElement { className == "android.widget.EditText" }.setText("My input text")
// Perform a long click
onElement { contentDescription == "Context Menu" }.longClick()
앱 상태 및 감시자 처리
앱의 수명 주기를 관리하고 테스트 중에 표시될 수 있는 예기치 않은 UI 요소를 처리합니다.
앱 수명 주기 관리
API는 테스트 대상 앱의 상태를 제어하는 방법을 제공합니다.
// Start a specific app by package name. Used for benchmarking and other
// self-instrumenting tests.
startApp("com.example.targetapp")
// Start a specific activity within the target app
startActivity(SomeActivity::class.java)
// Start an intent
startIntent(myIntent)
// Clear the app's data (resets it to a fresh state)
clearAppData("com.example.targetapp")
예상치 못한 UI 처리
watchFor
API를 사용하면 테스트 흐름 중에 표시될 수 있는 권한 대화상자와 같은 예기치 않은 UI 요소의 핸들러를 정의할 수 있습니다. 내부 감시자 메커니즘을 사용하지만 더 많은 유연성을 제공합니다.
import androidx.test.uiautomator.PermissionDialog
@Test
fun myTestWithPermissionHandling() = uiAutomator {
startActivity(MainActivity::class.java)
// Register a watcher to click "Allow" if a permission dialog appears
watchFor(PermissionDialog) { clickAllow() }
// Your test steps that might trigger a permission dialog
onElement { textAsString() == "Request Permissions" }.click()
// Example: You can register a different watcher later if needed
clearAppData("com.example.targetapp")
// Now deny permissions
startApp("com.example.targetapp")
watchFor(PermissionDialog) { clickDeny() }
onElement { textAsString() == "Request Permissions" }.click()
}
PermissionDialog
은 ScopedWatcher<T>
의 예시이며, 여기서 T
은 watchFor
의 블록에 범위로 전달된 객체입니다. 이 패턴을 기반으로 맞춤 감시자를 만들 수 있습니다.
앱 공개 상태 및 안정성 대기
테스트에서 요소가 표시되거나 안정화될 때까지 기다려야 하는 경우가 있습니다. UI Automator는 이를 지원하는 여러 API를 제공합니다.
waitForAppToBeVisible("com.example.targetapp")
는 지정된 패키지 이름이 있는 UI 요소가 맞춤설정 가능한 제한 시간 내에 화면에 표시되기를 기다립니다.
// Wait for the app to be visible after launching it
startApp("com.example.targetapp")
waitForAppToBeVisible("com.example.targetapp")
waitForStable()
API를 사용하여 앱의 UI가 안정적인 것으로 간주되는지 확인한 후 UI와 상호작용합니다.
// Wait for the entire active window to become stable
activeWindow().waitForStable()
// Wait for a specific Ui element to become stable (e.g., after a loading animation)
onElement { id == "my_loading_indicator" }.waitForStable()
고급 기능
다음 기능은 더 복잡한 테스트 시나리오에 유용합니다.
여러 창과 상호작용
UI Automator API를 사용하면 UI 요소와 직접 상호작용하고 UI 요소를 검사할 수 있습니다. 이는 PIP 모드나 화면 분할 레이아웃과 같이 여러 창이 포함된 시나리오에 특히 유용합니다.
// Find the first window that is in Picture-in-Picture mode
val pipWindow = windows()
.first { it.isInPictureInPictureMode == true }
// Now you can interact with elements within that specific window
pipWindow.onElement { textAsString() == "Play" }.click()
스크린샷 및 시각적 어설션
테스트 내에서 직접 전체 화면, 특정 창 또는 개별 UI 요소의 스크린샷을 캡처합니다. 이는 시각적 회귀 테스트 및 디버깅에 유용합니다.
uiautomator {
// Take a screenshot of the entire active window
val fullScreenBitmap: Bitmap = activeWindow().takeScreenshot()
fullScreenBitmap.saveToFile(File("/sdcard/Download/full_screen.png"))
// Take a screenshot of a specific UI element (e.g., a button)
val buttonBitmap: Bitmap = onElement { id == "my_button" }.takeScreenshot()
buttonBitmap.saveToFile(File("/sdcard/Download/my_button_screenshot.png"))
// Example: Take a screenshot of a PiP window
val pipWindowScreenshot = windows()
.first { it.isInPictureInPictureMode == true }
.takeScreenshot()
pipWindowScreenshot.saveToFile(File("/sdcard/Download/pip_screenshot.png"))
}
비트맵용 saveToFile
확장 함수를 사용하면 캡처된 이미지를 지정된 경로에 쉽게 저장할 수 있습니다.
디버깅을 위해 ResultsReporter 사용
ResultsReporter
를 사용하면 스크린샷과 같은 테스트 아티팩트를 Android 스튜디오의 테스트 결과와 직접 연결하여 더 쉽게 검사하고 디버깅할 수 있습니다.
uiAutomator {
startApp("com.example.targetapp")
val reporter = ResultsReporter("MyTestArtifacts") // Name for this set of results
val file = reporter.addNewFile(
filename = "my_screenshot",
title = "Accessible button image" // Title that appears in Android Studio test results
)
// Take a screenshot of an element and save it using the reporter
onElement { textAsString() == "Accessible button" }
.takeScreenshot()
.saveToFile(file)
// Report the artifacts to instrumentation, making them visible in Android Studio
reporter.reportToInstrumentation()
}
이전 UI Automator 버전에서 이전
이전 API 표면으로 작성된 기존 UI Automator 테스트가 있는 경우 다음 표를 참고하여 최신 접근 방식으로 이전하세요.
작업 유형 | 이전 UI Automator 메서드 | 새 UI Automator 메서드 |
---|---|---|
진입점 | UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()) |
uiAutomator { ... } 범위에서 테스트 로직을 래핑합니다. |
UI 요소 찾기 | device.findObject(By.res("com.example.app:id/my_button")) |
onElement { id == "my\_button" } |
UI 요소 찾기 | device.findObject(By.text("Click Me")) |
onElement { textAsString() == "Click Me" } |
유휴 UI 대기 | device.waitForIdle() |
onElement 의 기본 제공 제한 시간 메커니즘을 사용하는 것이 좋습니다. 그렇지 않으면 activeWindow().waitForStable() 을 사용하세요. |
하위 요소 찾기 | 수동으로 중첩된 findObject 호출 |
onElement().onElement() 체이닝 |
권한 대화상자 처리 | UiAutomator.registerWatcher() |
watchFor(PermissionDialog) |