O UI Automator é um framework de testes de IU adequado para testes funcionais de IU entre apps instalados e do sistema. As APIs do UI Automator permitem interagir
com elementos visíveis em um dispositivo, independentemente de qual Activity
está em
foco. Assim, é possível realizar operações como abrir o menu de configurações
ou o Acesso rápido aos apps em um dispositivo de teste. Seu teste pode procurar um componente de IU usando descritores convenientes, como o texto exibido nesse componente ou a descrição do conteúdo relacionada.
O framework de testes UI Automator é uma API baseada em instrumentação e funciona com o executor de testes AndroidJUnitRunner
. Ele é adequado para programar testes automatizados no estilo caixa opaca, em que o código do teste não se baseia em detalhes internos de implementação do app de destino.
Os principais recursos do framework de testes do UI Automator incluem:
- Uma API para recuperar informações de estado e realizar operações no dispositivo de destino. Para mais informações, consulte Como acessar o estado do dispositivo.
- APIs compatíveis com testes de IU entre apps. Para mais informações, consulte APIs do UI Automator.
Como acessar o estado do dispositivo
O framework de testes UI Automator oferece uma classe UiDevice
para acessar e realizar operações no dispositivo em que o app de destino está sendo executado. Você pode chamar os métodos dessa classe para acessar propriedades do dispositivo, como orientação atual ou tamanho da tela. A classe UiDevice
também permite realizar as seguintes
ações:
- Mudar a rotação do dispositivo.
- Pressionar teclas de hardware, como "Aumentar volume".
- Pressionar os botões "Voltar", "Início" ou "Menu".
- Abrir a aba de notificações.
- Fazer uma captura de tela da janela atual.
Por exemplo, para simular o pressionamento do botão "Início", chame o método UiDevice.pressHome()
.
APIs do UI Automator
As APIs do UI Automator permitem programar testes robustos sem conhecer os detalhes de implementação do app de destino. Você pode usar essas APIs para capturar e manipular componentes da interface em vários apps:
UiObject2
: representa um elemento da interface visível no dispositivo.BySelector
: especifica critérios para correspondência de elementos da interface.By
: criaBySelector
de maneira concisa.Configurator
: permite definir parâmetros importantes para executar testes do UI Automator.
Por exemplo, o código a seguir mostra como programar um script de teste que abre um app do Gmail no dispositivo:
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);
Configurar o UI Automator
Antes de criar seu teste de UI com o UI Automator, configure o local do código-fonte do teste e as dependências do projeto, conforme descrito em Configurar projetos para o AndroidX Test.
No arquivo build.gradle
do módulo do app para Android, é preciso definir uma referência de dependência para a biblioteca do UI Automator:
Kotlin
dependencies {
...
androidTestImplementation("androidx.test.uiautomator:uiautomator:2.3.0")
}
Groovy
dependencies {
...
androidTestImplementation "androidx.test.uiautomator:uiautomator:2.3.0"
}
Para otimizar os testes do UI Automator, inspecione primeiro os componentes de IU do app de destino e verifique se é possível acessá-los. Essas dicas de otimização são descritas nas duas seções a seguir.
Inspecionar a IU em um dispositivo
Antes de criar seu teste, inspecione os componentes de IU visíveis no
dispositivo. Para garantir que os testes do UI Automator possam acessar esses componentes, verifique se eles têm rótulos de texto visíveis, valores android:contentDescription
ou ambos.
A ferramenta uiautomatorviewer
oferece uma interface visual conveniente para inspecionar a hierarquia de layouts e ver as propriedades dos componentes de IU visíveis no primeiro plano do dispositivo. Essas informações permitem criar testes mais detalhados por meio do UI Automator. Por exemplo, é possível criar um seletor de IU
que corresponda a uma propriedade visível específica.
Para iniciar a ferramenta uiautomatorviewer
:
- Abra o app de destino em um dispositivo físico.
- Conecte o dispositivo à máquina de desenvolvimento.
- Abra uma janela de terminal e navegue até o diretório
<android-sdk>/tools/
. - Execute a ferramenta com este comando:
$ uiautomatorviewer
Para ver as propriedades de IU do app:
- Na interface
uiautomatorviewer
, clique no botão Device Screenshot. - Passe o cursor do mouse sobre o snapshot no painel esquerdo para ver os componentes de IU identificados pela ferramenta
uiautomatorviewer
. As propriedades são listadas no painel inferior direito, e a hierarquia de layouts no painel superior direito. - Também é possível clicar no botão Toggle NAF Nodes para ver os componentes de IU a que o UI Automator não tem acesso. Somente informações limitadas podem ser disponibilizadas para esses componentes.
Para saber mais sobre os tipos comuns de componentes de IU oferecidos pelo Android, consulte Interface do usuário.
Garantir que a atividade esteja acessível
O framework de testes UI Automator apresenta um desempenho melhor nos apps que implementaram recursos de acessibilidade do Android. Quando você usa elementos de interface do tipo View
ou
uma subclasse de View
do SDK, não é necessário implementar compatibilidade
de acessibilidade, porque essas classes já fazem isso.
No entanto, alguns apps usam elementos personalizados de UI para oferecer uma experiência mais detalhada ao usuário.
Esses elementos não oferecem compatibilidade automática com acessibilidade. Se o app
contiver instâncias de uma subclasse de View
que não seja do SDK, adicione
recursos de acessibilidade a esses elementos seguindo estas
etapas:
- Crie uma classe concreta que estenda ExploreByTouchHelper.
- Chame setAccessibilityDelegate() para associar uma instância da nova classe a um elemento de IU personalizado específico.
Para mais orientações sobre como adicionar recursos de acessibilidade a elementos de visualização personalizados, consulte Criar visualizações personalizadas acessíveis. Para saber mais sobre práticas recomendadas gerais para acessibilidade no Android, consulte Como tornar apps mais acessíveis.
Criar uma classe de teste do UI Automator
Sua classe de testes do UI Automator deve ser programada da mesma maneira que uma classe de teste do JUnit 4. Para saber mais sobre como criar classes de teste do JUnit 4 e usar declarações e anotações do JUnit 4, consulte Criar uma classe de teste de unidade de instrumentação.
Adicione a anotação @RunWith(AndroidJUnit4.class) no início da definição da classe de teste. Também é preciso especificar a classe AndroidJUnitRunner, que o AndroidX Test oferece como executor de testes padrão. Essa etapa é descrita em mais detalhes em Executar testes do UI Automator em um dispositivo ou emulador.
Implemente o seguinte modelo de programação na classe de teste do UI Automator:
- Receba um objeto
UiDevice
para acessar o dispositivo que você quer testar chamando o método getInstance() e transmitindo um objeto Instrumentation como argumento. - Use um objeto
UiObject2
para acessar um componente de UI exibido no dispositivo (por exemplo, a visualização atual em primeiro plano), chamando o método findObject(). - Simule uma interação específica do usuário para executar nesse componente de IU chamando um método
UiObject2
. Por exemplo, chame scrollUntil() para rolar e setText() para editar um campo de texto. Você pode chamar as APIs nas etapas 2 e 3 várias vezes conforme necessário para testar interações mais complexas do usuário que envolvem diversos componentes de IU ou sequências de ações. - Verifique se a interface reflete o estado ou o comportamento esperado quando essas interações são realizadas.
Essas etapas são abordadas com mais detalhes nas seções abaixo.
Acessar componentes de IU
O objeto UiDevice
é a principal maneira de acessar e manipular o estado do dispositivo. Nos testes, você pode chamar métodos UiDevice
para verificar o estado de várias propriedades, como a orientação atual ou o tamanho da tela.
Seu teste pode usar o objeto UiDevice
para realizar ações no nível do dispositivo,
como forçar o dispositivo a uma rotação específica, pressionar os botões físicos do
botão direcional e os botões "Início" e "Menu".
Uma prática recomendada é iniciar o teste na tela inicial do dispositivo. Na tela inicial (ou em outro local inicial escolhido no dispositivo), chame os métodos disponibilizados pela API do UI Automator para selecionar e interagir com elementos de interface específicos.
O snippet de código a seguir mostra como o teste pode usar uma instância de UiDevice
e simular o pressionamento do botão "Início":
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);
}
}
No exemplo, a instrução @SdkSuppress(minSdkVersion = 18) ajuda a garantir que os testes sejam executados apenas em dispositivos com o Android 4.3 (API nível 18) ou posterior, conforme exigido pelo framework UI Automator.
Use o método findObject()
para recuperar um UiObject2
que representa
uma visualização correspondente a um determinado critério de seleção. É possível reutilizar as instâncias UiObject2
que você criou em outras partes do teste do app, conforme necessário.
O framework de teste do UI Automator pesquisa uma correspondência na exibição atual sempre que o teste usa uma instância UiObject2
para clicar em um elemento de IU ou consultar uma propriedade.
O snippet a seguir mostra como o teste pode criar instâncias UiObject2
que representem um botão "Cancelar" e um botão "OK" em um app.
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();
}
Especificar um seletor
Para acessar um componente de interface específico em um app, use a classe By
para construir uma instância BySelector
. BySelector
representa uma consulta para elementos específicos na interface exibida.
Se mais de um elemento correspondente for encontrado, o primeiro na hierarquia de layout será retornado como o destino UiObject2
. Ao criar um BySelector
, você pode encadear várias propriedades para refinar a pesquisa. Se nenhum elemento de UI correspondente for encontrado, um null
será retornado.
Você pode usar o método hasChild()
ou hasDescendant()
para aninhar várias instâncias BySelector
. Por exemplo, o exemplo de código a seguir mostra
como o teste pode especificar uma pesquisa para encontrar o primeiro ListView
que
tem um elemento de IU filho com a propriedade de texto.
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")
)
);
Pode ser útil especificar o estado do objeto nos seus critérios de seleção. Por exemplo, se você quer selecionar uma lista de todos os elementos marcados para desmarcá-los, chame o método checked()
com o argumento definido como "true".
Realizar ações
Depois que o teste tiver um objeto UiObject2
, você poderá chamar os métodos da classe UiObject2
para realizar interações do usuário no componente de IU representado por esse objeto. É possível especificar ações como:
click()
: clica no centro dos limites visíveis do elemento de IU.drag()
: arrasta esse objeto para coordenadas arbitrárias.setText()
: define o texto em um campo editável depois de limpar o conteúdo do campo. Por outro lado, o métodoclear()
limpa o texto existente em um campo editável.swipe()
: executa a ação de deslizar na direção especificada.scrollUntil()
: executa a ação de rolagem na direção especificada até queCondition
ouEventCondition
seja atendido.
A estrutura de testes do UI Automator permite enviar uma Intent ou iniciar uma
Activity sem usar comandos do shell, recebendo um objeto Context
por meio de getContext()
.
O snippet a seguir mostra como o teste pode usar uma Intent para iniciar o app em teste. Essa abordagem é útil quando você tem interesse apenas em testar o app de calculadora e não se importa com a tela de início.
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);
}
Verificar resultados
O InstrumentationTestCase estende o TestCase. Assim, você pode usar métodos Assert padrão do JUnit para testar se os componentes de UI no app retornam os resultados esperados.
O snippet a seguir mostra como o teste pode localizar vários botões em um app de calculadora, clicar neles em ordem e verificar se o resultado correto é mostrado.
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());
}
Executar testes do UI Automator em um dispositivo ou emulador
Você pode executar testes do UI Automator no Android Studio ou na linha de comando. Especifique AndroidJUnitRunner
como o executor de instrumentação padrão no projeto.
Mais exemplos
Interagir com a interface do sistema
O UI Automator pode interagir com tudo na tela, incluindo elementos do sistema fora do app, conforme mostrado nos snippets de código a seguir:
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());
Aguardar transições

As transições de tela podem levar tempo, e prever a duração delas não é confiável. Por isso, o UI Automator precisa esperar depois de realizar operações. O UI Automator oferece vários métodos para isso:
UiDevice.performActionAndWait(Runnable action, EventCondition<U> condition, long timeout)
: por exemplo, para clicar em um botão e esperar até que uma nova janela apareça, chamedevice.performActionAndWait(() -> button.click(), Until.newWindow(), timeout)
.UiDevice.wait(Condition<Object, U> condition, long timeout)
: por exemplo, para aguardar até que haja um determinadoUiObject2
no dispositivo, chamedevice.wait(Until.hasObject(By.text("my_text")), timeout);
.UiObject2.wait(@NonNull Condition<Object, U> condition, long timeout)
: por exemplo, para esperar até que uma caixa de seleção seja marcada, chamecheckbox.wait(Until.checked(true), timeout);
.UiObject2.clickAndWait(@NonNull EventCondition<U> condition, long timeout)
: por exemplo, para clicar em um botão e esperar até que uma nova janela apareça, chamebutton.clickAndWait(Until.newWindow(), timeout);
UiObject2.scrollUntil(@NonNull Direction direction, @NonNull Condition<Object, U> condition)
: por exemplo, para rolar para baixo até que um novo objeto apareça, chameobject.scrollUntil(Direction.DOWN, Until.hasObject(By.text('new_obj')));
.UiObject2.scrollUntil(@NonNull Direction direction, @NonNull EventCondition<U> condition)
: por exemplo, para rolar até a parte de baixo, chameobject.scrollUntil(Direction.DOWN, Until.scrollFinished(Direction.DOWN));
O snippet de código a seguir mostra como usar o UI Automator para desativar o modo Não
perturbe nas configurações do sistema usando o método performActionAndWait()
que
aguarda transições:
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)); }
Outros recursos
Para saber mais sobre o uso do UI Automator em testes do Android, consulte os recursos a seguir.
Documentação de referência:
Amostras
- BasicSample (em inglês): exemplo básico do UI Automator.