Adicionar os testes Espresso e UiAutomator ao aplicativo Surface Duo

O Espresso e o UiAutomator são ambos estruturas de teste fornecidas por bibliotecas AndroidX. O Espresso é usado para localizar e interagir com elementos de interface do usuário em um aplicativo, enquanto o UiAutomator fornece funcionalidade de teste entre aplicativos e acesso a sensores de dispositivo.

Para saber mais sobre essas estruturas de teste, confira estes recursos:

Em seu projeto de Android Studio, você escreverá esses testes de interface do usuário na seção androidTest, em que o arquivo de teste padrão é denominado ExampleInstrumentedTest. Lembre-se de desabilitar animações em seu dispositivo antes de executar os testes de interface do usuário.

Dependências

Adicione as seguintes dependências ao arquivo build.gradle do aplicativo:

android {
    defaultConfig {
        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
    }
}

dependencies {
    testImplementation "junit:junit:4.13"
    androidTestImplementation "androidx.test.ext:junit:1.1.1"
    androidTestImplementation "androidx.test.espresso:espresso-core:3.2.0"
    androidTestImplementation "androidx.test:runner:1.2.0"
    androidTestImplementation "androidx.test:rules:1.2.0"
    androidTestImplementation "androidx.test.uiautomator:uiautomator:2.2.0"
}

Se estiver testando com um modo de exibição da Web, androidTestImplementation "androidx.test.espresso:espresso-web:3.2.0" também deverá ser adicionado à seção de dependências.

Observação

Se estiver usando um arquivo dependencies.gradle separado, confira o repositório de exemplos de aplicativos para obter exemplos de como adicionar as dependências necessárias.

Criar regras para testes

As regras JUnit podem ser adicionadas aos testes para especificar informações adicionais sobre os testes subsequentes. Uma das regras mais comuns usadas é definir uma atividade principal para os testes a serem aplicados, conforme mostrado abaixo:

@RunWith(AndroidJUnit4::class)
class PhotoEditorUITest {
    @get:Rule
    val activityRule = ActivityTestRule(MainActivity::class.java)
}

Escrever testes de tela dupla

Noções básicas

Use a classe UiDevice fornecida pelo UiAutomator para localizar o dispositivo e alterar as respectivas configurações de abrangência/rotação e, em seguida, use o Espresso para verificar se os elementos da interface do usuário se comportam conforme o esperado.

Conforme mostrado no exemplo a seguir, o método de passar o dedo pode ser usado para simular a extensão e a redução. Para tornar seus testes o mais confiável possível, tente agrupar testes que precisam da mesma configuração para que você não precise fazer várias solicitações de extensão e redução.

@Test
fun testSpan() {
    val device = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())

    // Span the app (from the left side)
    device.swipe(675, 1780, 1350, 900, 400)

    // Test that UI element appears
    onView(withId(R.id.yourElement)).check(matches(isDisplayed()))

    // Unspan the app (to the left side)
    // device.swipe(2109, 1780, 675, 900, 200)

    // Test that UI element has disappeared
    // onView(withId(R.id.yourElement)).check(matches(not(isDisplayed())))
}

Outras funções úteis do UiAutomator incluem setOrientationNatural, unfreezeRotation e pressHome. Se você quiser testar a funcionalidade entre aplicativos, também poderá usar o UiAutomator para iniciar outros aplicativos.

Funções auxiliares

Essas funções auxiliares básicas podem ser usadas para simular o comportamento de extensão:

companion object {
    // testing device
    val device: UiDevice = UiDevice.getInstance(getInstrumentation())

    // Swipe constants
    const val leftX: Int = 675          // middle of left screen
    const val rightX: Int = 2109        // middle of right screen
    const val middleX: Int = 1350       // hinge area
    const val bottomY: Int = 1780       // bottom of screen
    const val middleY: Int = 900        // middle of screen
    const val spanSteps: Int = 400      // spanning swipe
    const val unspanSteps: Int = 200    // unspanning swipe
    const val switchSteps: Int = 100    // swipe to switch from one screen to the other
    const val closeSteps: Int = 50      // swipe to close app
}

private fun spanFromLeft() {
    device.swipe(leftX, bottomY, middleX, middleY, spanSteps)
}

private fun unspanToLeft() {
    device.swipe(rightX, bottomY, leftX, middleY, unspanSteps)
}

private fun spanFromRight() {
    device.swipe(rightX, bottomY, middleX, middleY, spanSteps)
}

private fun unspanToRight() {
    device.swipe(leftX, bottomY, rightX, middleY, unspanSteps)
}

private fun switchToLeft() {
    device.swipe(rightX, bottomY, leftX, middleY, switchSteps)
}

private fun switchToRight() {
    device.swipe(leftX, bottomY, rightX, middleY, switchSteps)
}

private fun closeLeft() {
    device.swipe(leftX, bottomY, leftX, middleY, closeSteps)
}

private fun closeRight() {
    device.swipe(rightX, bottomY, rightX, middleY, closeSteps)
}

Para testar se o aplicativo é estendido, defina uma regra que se conecte à atividade principal e use-a para substituir nesta função:

private fun isSpanned(): Boolean {
    onIdle() // wait until layout changes have been fully processed before checking
    return ScreenHelper.isDualMode(activityRule.activity)
}

Para garantir que todas as funções auxiliares funcionem conforme o esperado, adicione import org.hamcrest.CoreMatchers.`is` as iz às instruções de importação e, em seguida, execute o seguinte teste:

/**
 * Runs helper functions and checks that they work as expected
 *
 * @precondition device in portrait mode, no other applications are open
 * (so app by default opens on left screen)
 */
@Test
fun testSpanningHelperFunctions() {
    spanFromLeft()
    assertThat(isSpanned(), iz(true))

    unspanToRight()
    assertThat(isSpanned(), iz(false))

    spanFromRight()
    assertThat(isSpanned(), iz(true))

    unspanToLeft()
    assertThat(isSpanned(), iz(false))

    switchToRight()
    switchToLeft()
    closeLeft()
}

Dicas de solução de problemas

Se o teste de testSpanningHelperFunctions falhar ou se você encontrar outros problemas de teste:

  • Ajuste as constantes de etapa: alguns gestos precisam ser lentos o suficiente para garantir que sejam processados corretamente. Cem etapas demoram aproximadamente 0,5 segundo.
  • Altere as constantes x e y, que representam valores de pixel na tela. Cada tela tem 1350 x 1800 px e a lacuna da dobradiça tem 84 px de largura (dimensões do Surface Duo).
  • Adicione IdlingResources a testes que carregam dados ou executam outras operações assíncronas que podem afetar os testes de interface do usuário.

Resumo

O teste de interface do usuário para aplicativos do Surface Duo pode ser realizado usando uma combinação das estruturas de teste Espresso e UiAutomator. Na maioria dos casos, o método de passar o dedo do UiAutomator é tudo o que você precisa para simular eventos de tela dupla e, em seguida, o Espresso pode ser usado normalmente para interagir com elementos de interface do usuário em seu aplicativo Surface Duo.

Para obter exemplos de teste de interface do usuário em aplicativos do Surface Duo, confira estes recursos: