UI Automator é um framework do Google para testes automatizados de UI em aplicações Android que opera ao nível do sistema e pode interagir com elementos da interface além de uma única aplicação. Ao contrário do Espresso, o UI Automator não está vinculado ao processo de uma aplicação específica: ele pode abrir diálogos do sistema, a cortina de notificações e alternar entre aplicações. De acordo com Google Android Developers, o UI Automator utiliza o Accessibility Service padrão para aceder à árvore de UI do dispositivo.
Principais conclusões
UI Automator é um framework para testes funcionais de UI no Android que opera ao nível do sistema operativo. Ele fornece uma API para aceder a qualquer elemento no ecrã do dispositivo, independentemente da aplicação a que pertence — incluindo a barra de estado do sistema, diálogos de permissões, o ecrã inicial e aplicações de terceiros. Isto torna-o indispensável para testar cenários que vão além de uma única aplicação.
Arquitetonicamente, o UI Automator utiliza o Accessibility Service — o mesmo serviço usado pelo TalkBack, Switch Access e outras ferramentas de acessibilidade. Através deste serviço, o framework obtém a árvore completa de componentes de UI do ecrã atual e permite executar ações sobre eles: tocar, deslizar, introduzir texto e premir longamente.
O UI Automator apareceu pela primeira vez no Android 4.3 (API 18) e desde então faz parte da Android Testing Support Library como a ferramenta oficial do Google para testes entre aplicações. No AndroidX Test, está disponível como um artefacto separado androidx.test.uiautomator:uiautomator versão 2.3.0 (2024), que suporta todas as versões do Android desde API 18.
Princípio de funcionamento: O UI Automator baseia-se na digitalização da árvore de acessibilidade do ecrã atual. Quando o método findObject(selector) é chamado, o framework percorre a hierarquia de Views, encontra o primeiro elemento que corresponde às condições do UiSelector e retorna um UiObject — um proxy para interagir com a View real.
Um teste típico de UI Automator começa obtendo uma instância de UiDevice, que representa o dispositivo físico. O UiDevice fornece métodos para encontrar elementos, gerir pressões de botões (Home, Back, Recent), rodar o ecrã e tirar capturas de ecrã. Depois de encontrar um elemento através do UiSelector, as ações são executadas no UiObject.
No exemplo abaixo, o teste abre a aplicação Definições, encontra o item “Bateria” por texto e toca nele. O UI Automator não requer lançar uma Activity — funciona com qualquer ecrã no dispositivo, incluindo aplicações de terceiros.
val device = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())
// Abrir ecrã de definições
device.pressHome()
device.wait(Until.hasObject(UiSelector().text("Definições")), 2000)
// Encontrar o item "Bateria" e tocar
val batteryItem = device.findObject(
UiSelector().text("Bateria")
)
batteryItem.clickAndWait(Until.newWindow(), 3000)
UiDevice é a classe principal para interagir com o dispositivo. Fornece métodos para encontrar elementos, simular pressões de botões físicos (Home, Back, Menu, Volume), gerir energia, tirar capturas de ecrã e aguardar estados específicos do ecrã. O UiDevice é criado uma vez por teste e reutilizado para todas as operações.
UiSelector é uma API fluente para encontrar elementos de UI. Ao contrário do Espresso ViewMatchers, o UiSelector não requer compilação — as condições de pesquisa são formadas através de uma cadeia de métodos: text(), className(), description(), resourceId(), index(). Múltiplas condições são combinadas automaticamente através de AND lógico.
| Método UiSelector | Propósito |
|---|---|
| text(String) | Pesquisar por texto exato do elemento |
| textContains(String) | Pesquisar por correspondência parcial de texto |
| resourceId(String) | Pesquisar por ID de recurso (ex., com.example:id/button) |
| className(String) | Pesquisar por nome da classe da View |
| description(String) | Pesquisar por content-description |
| childSelector(selector) | Pesquisar um elemento filho dentro de um contentor |
Quando vários elementos no ecrã partilham o mesmo texto, o UiSelector permite combinar critérios: encontrar um contentor por ID, depois dentro dele — um elemento por texto e classe. Isto garante a identificação única do componente desejado. O método childSelector reduz o âmbito da pesquisa a um contentor específico, acelerando a navegação na árvore de UI.
val scrollView = device.findObject(
UiSelector().resourceId("android:id/list")
)
// Dentro da lista, encontrar o elemento com texto "Wi-Fi"
val wifiItem = scrollView.findObject(
UiSelector().text("Wi-Fi")
wifiItem.click()
Os testes entre aplicações (cross-application) são a principal funcionalidade pela qual o UI Automator é escolhido. O framework pode alternar entre aplicações, testar login OAuth através do navegador, verificar diálogos do sistema (permissões, seletor de aplicações) e interagir com a barra de estado do sistema, painel de notificações e ecrã de bloqueio.
Um cenário típico de teste cross-app: a aplicação abre um navegador para autorização OAuth, o utilizador introduz o seu nome de utilizador e palavra-passe, e o navegador redireciona de volta para a aplicação. O UI Automator alterna entre processos, encontra os campos de entrada no navegador, preenche-os e toca em “Iniciar sessão”.
// A aguardar o aparecimento do navegador
device.wait(Until.hasObject(
UiSelector().packageName("com.android.chrome")
), 5000)
// A procurar campo de introdução de email no navegador
val emailField = device.findObject(
UiSelector().className("android.widget.EditText").instance(0)
)
emailField.text = "user@example.com"
O UI Automator pode verificar e fechar diálogos do sistema — permissões de localização, notificações, acesso a ficheiros. Isto é criticamente importante para testar cenários de primeiro lançamento quando o sistema solicita várias permissões sequencialmente. Sem o UI Automator, estes cenários não podem ser automatizados porque os diálogos do sistema não pertencem ao processo da aplicação.
A escolha entre UI Automator e Espresso depende do cenário de teste. O Espresso é otimizado para testar uma única aplicação com sincronização automática e mínimo boilerplate. O UI Automator é adequado para cenários onde é necessário interagir com o sistema, navegador ou múltiplas aplicações.
| Critério | UI Automator | Espresso |
|---|---|---|
| Âmbito | Dispositivo inteiro, múltiplas aplicações | Uma única aplicação |
| Sincronização | Manual (espera, pausa) | Automática (Idling Resource) |
| Velocidade | Mais lento (acesso através do serviço) | Mais rápido (funciona dentro do processo) |
| UI do Sistema | Suporta (Notificações, Definições Rápidas) | Não suporta |
| Precisão de pesquisa | UiSelector por atributos | ViewMatchers por tipo e hierarquia |
| Estabilidade | Menor (depende de temporização) | Maior (espera automática) |
Na prática, estes frameworks são frequentemente usados em conjunto: o Espresso cobre os testes de UI da aplicação principal com alta estabilidade, enquanto o UI Automator é utilizado para cenários que vão além dos limites da aplicação — login OAuth, permissões do sistema, trabalho com Share Intent. Esta combinação proporciona a máxima cobertura de UI com custos mínimos de manutenção de testes.
A integração do UI Automator é feita adicionando uma dependência ao build.gradle. O framework faz parte do AndroidX Test e não requer permissões adicionais no manifesto — o acesso ao Accessibility Service é configurado automaticamente quando o teste instrumentado é lançado.
A configuração mínima inclui o artefacto uiautomator e o executor de testes padrão AndroidJUnitRunner. Os testes UI Automator são colocados no diretório src/androidTest e executados num emulador ou dispositivo físico com Android API 18+.
dependencies {
androidTestImplementation("androidx.test.uiautomator:uiautomator:2.3.0")
androidTestImplementation("androidx.test.ext:junit:1.2.1")
androidTestImplementation("androidx.test:runner:1.6.1")
}
Para obter uma instância UiDevice, utiliza-se InstrumentationRegistry.getInstrumentation(). O UiDevice deve ser criado uma vez no método setUp() e reutilizado em todos os testes da classe para conservar os recursos do dispositivo. É importante notar que o UiDevice não é thread-safe — todas as operações devem ser executadas no mesmo thread do método de teste. Criar um novo UiDevice em cada teste gera sobrecarga e retarda a execução. Recomenda-se criar o UiDevice uma vez no método beforeClass e reutilizá-lo para todos os testes da classe de teste.
Ao contrário do Espresso, o UI Automator não tem sincronização automática. Para aguardar o aparecimento de elementos, utiliza-se o método UiDevice.wait(condition, timeout) com um objeto Until: Until.findObject(selector), Until.hasObject(selector), Until.gone(selector). Sem esperas adequadas, os testes tornam-se instáveis devido a condições de corrida — um elemento pode não ter aparecido no ecrã no momento da pesquisa. Recomenda-se definir um tempo limite de pelo menos 3–5 segundos para estabilidade.
Perguntas frequentes
UI Automator opera ao nível do Accessibility Service e pode interagir com qualquer aplicação. O Espresso funciona dentro do processo de uma única aplicação e utiliza sincronização automática com o thread de UI. O UI Automator é melhor para cenários entre aplicações, enquanto o Espresso é melhor para testes estáveis de uma única aplicação.
Sim, o UI Automator funciona em todos os dispositivos com Android API 18+. Não requer acesso root — utiliza o Accessibility Service padrão, que é ativado através do Instrumentation quando os testes são lançados.
O UI Automator utiliza o Accessibility Service para obter a árvore completa de componentes de UI do ecrã atual. Depois, o UiSelector percorre esta árvore e encontra elementos pelos critérios especificados: texto, classe, ID, content-description ou uma combinação destes.
Sim, o método UiDevice.takeScreenshot(storePath) permite tirar uma captura de ecrã do ecrã atual e guardá-la num ficheiro. Isto é útil para depuração: quando um teste falha, pode guardar a captura de ecrã e analisar o estado do ecrã.
O UI Automator não tem sincronização automática, por isso os testes são sensíveis à temporização. Se uma animação não terminou ou uma View ainda não foi renderizada, o findObject pode não encontrar o elemento. A solução é usar UiDevice.wait() com um tempo limite suficiente.
Resumo
O conjunto de ferramentas UI Automator cobre todos os cenários-chave de testes entre aplicações e é o padrão para automação Android ao nível do sistema.
Vamos desenvolver um aplicativo móvel chave na mão
A IT Sectr cria aplicativos para iOS e Android para startups e empresas desde 2017. Nós vamos aconselhá-lo e propor a melhor solução.
Leia também