O framework de testes do UI Automator oferece um conjunto de APIs para criar testes de IU que interagem com apps de usuário e do sistema.
Introdução aos testes modernos do UI Automator
O UI Automator 2.4 apresenta uma linguagem específica do domínio (DSL) simplificada e compatível com Kotlin que facilita a criação de testes de interface para Android. Essa nova plataforma de API se concentra na descoberta de elementos com base em predicados e no controle explícito dos estados do app. Use para criar testes automatizados mais confiáveis e fáceis de manter.
O UI Automator permite testar um app de fora do processo dele. Isso permite testar versões de lançamento com minificação aplicada. O UI Automator também ajuda na criação de testes de macrobenchmark.
Os principais recursos da abordagem moderna incluem:
- Um escopo de teste
uiAutomator
dedicado para um código de teste mais limpo e expressivo. - Métodos como
onElement
,onElements
eonElementOrNull
para encontrar elementos de interface com predicados claros. - Mecanismo de espera integrado para elementos condicionais
onElement*(timeoutMs: Long = 10000)
- Gerenciamento explícito do estado do app, como
waitForStable
ewaitForAppToBeVisible
. - Interação direta com nós de janela de acessibilidade para cenários de teste com várias janelas.
- Recursos integrados de captura de tela e um
ResultsReporter
para testes visuais e depuração.
Configurar seu projeto
Para começar a usar as APIs modernas do UI Automator, atualize o arquivo
build.gradle.kts
do projeto para incluir a dependência mais recente:
Kotlin
dependencies {
...
androidTestImplementation("androidx.test.uiautomator:uiautomator:2.4.0-alpha05")
}
Groovy
dependencies {
...
androidTestImplementation "androidx.test.uiautomator:uiautomator:2.4.0-alpha05"
}
Principais conceitos da API
As seções a seguir descrevem os conceitos principais da API UI Automator moderna.
O escopo de teste do uiAutomator
Acesse todas as novas APIs do UI Automator no bloco uiAutomator { ... }
. Essa função cria um UiAutomatorTestScope
que fornece um ambiente conciso
e com segurança de tipo para suas operações de teste.
uiAutomator {
// All your UI Automator actions go here
startApp("com.example.targetapp")
onElement { textAsString() == "Hello, World!" }.click()
}
Encontrar elementos da interface
Use as APIs do UI Automator com predicados para localizar elementos da interface. Esses predicados permitem definir condições para propriedades como texto, estado selecionado ou em foco e descrição do conteúdo.
onElement { predicate }
: retorna o primeiro elemento da interface que corresponde ao predicado em um tempo limite padrão. A função gera uma exceção se não encontrar um elemento correspondente.// 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 }
: semelhante aonElement
, mas retornanull
se a função não encontrar nenhum elemento correspondente dentro do tempo limite. Isso não gera uma exceção. Use esse método para elementos opcionais.val optionalButton = onElementOrNull { textAsString() == "Skip" } optionalButton?.click() // Click only if the button exists
onElements { predicate }
: aguarda até que pelo menos um elemento da interface corresponda ao predicado especificado e retorna uma lista de todos os elementos correspondentes.// Get all items in a list Ui element val listItems = onElements { className == "android.widget.TextView" && isClickable } listItems.forEach { it.click() }
Confira algumas dicas para usar chamadas onElement
:
Encadeie chamadas
onElement
para elementos aninhados: você pode encadear chamadasonElement
para encontrar elementos dentro de outros elementos, seguindo uma hierarquia pai-filho.// 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()
Especifique um tempo limite para funções
onElement*
transmitindo um valor que representa milissegundos.// 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()
Interagir com elementos da interface
Interaja com elementos da interface simulando cliques ou definindo texto em campos editáveis.
// 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()
Processar estados e observadores de apps
Gerencie o ciclo de vida do app e processe elementos inesperados da interface que podem aparecer durante os testes.
Gerenciamento do ciclo de vida do app
As APIs oferecem maneiras de controlar o estado do app em teste:
// 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")
Gerenciar interfaces inesperadas
A API watchFor
permite definir manipuladores para elementos inesperados da interface,
como caixas de diálogo de permissão, que podem aparecer durante o fluxo de teste. Isso
usa o mecanismo de observador interno, mas oferece mais flexibilidade.
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
é um exemplo de ScopedWatcher<T>
, em que T
é o objeto transmitido como um escopo para o bloco em watchFor
. É possível criar observadores personalizados com base nesse padrão.
Aguardar a visibilidade e a estabilidade do app
Às vezes, os testes precisam esperar que os elementos fiquem visíveis ou estáveis. O UI Automator oferece várias APIs para ajudar nisso.
O waitForAppToBeVisible("com.example.targetapp")
aguarda que um elemento da interface com
o nome do pacote especificado apareça na tela dentro de um tempo limite personalizável.
// Wait for the app to be visible after launching it
startApp("com.example.targetapp")
waitForAppToBeVisible("com.example.targetapp")
Use a API waitForStable()
para verificar se a interface do app é considerada estável
antes de interagir com ela.
// 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()
Recursos avançados
Os recursos a seguir são úteis para cenários de teste mais complexos.
Interagir com várias janelas
As APIs do UI Automator permitem interagir diretamente com elementos da interface e inspecioná-los. Isso é especialmente útil para cenários que envolvem várias janelas, como o modo picture-in-picture (PiP) ou layouts de tela dividida.
// 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()
Capturas de tela e declarações visuais
Capture capturas de tela da tela inteira, de janelas específicas ou de elementos individuais da interface diretamente nos testes. Isso é útil para testes e depuração de regressão visual.
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"))
}
A função de extensão saveToFile
para Bitmap simplifica o salvamento da imagem
capturada em um caminho especificado.
Usar o ResultsReporter para depuração
O ResultsReporter
ajuda você a associar artefatos de teste, como capturas de tela, diretamente aos resultados dos testes no Android Studio para facilitar a inspeção e a depuração.
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()
}
Migrar de versões mais antigas do UI Automator
Se você tiver testes do UI Automator escritos com interfaces de API mais antigas, use a tabela a seguir como referência para migrar para a abordagem moderna:
Tipo de ação | Método antigo do UI Automator | Novo método do UI Automator |
---|---|---|
Ponto de entrada | UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()) |
Envolva a lógica de teste no escopo uiAutomator { ... } . |
Encontrar elementos da interface | device.findObject(By.res("com.example.app:id/my_button")) |
onElement { id == "my\_button" } |
Encontrar elementos da interface | device.findObject(By.text("Click Me")) |
onElement { textAsString() == "Click Me" } |
Aguardar a UI inativa | device.waitForIdle() |
Prefira o mecanismo de tempo limite integrado do onElement . Caso contrário, activeWindow().waitForStable() . |
Encontrar elementos filhos | Chamadas findObject aninhadas manualmente |
onElement().onElement() encadeamento |
Processar caixas de diálogo de permissões | UiAutomator.registerWatcher() |
watchFor(PermissionDialog) |