Interoperabilidade

O Compose se integra a frameworks de teste comuns.

Interoperabilidade com o Espresso

Em um app híbrido, é possível encontrar componentes do Compose dentro de hierarquias de visualização e visualizações dentro dos elementos do Compose (com o elemento combinável AndroidView).

Não há etapas específicas que sejam necessárias para fazer correspondência a nenhum dos tipos. A correspondência de visualizações é feita usando o método onView do Espresso e os elementos do Compose com ComposeTestRule.

@Test
fun androidViewInteropTest() {
    // Check the initial state of a TextView that depends on a Compose state.
    Espresso.onView(withText("Hello Views")).check(matches(isDisplayed()))
    // Click on the Compose button that changes the state.
    composeTestRule.onNodeWithText("Click here").performClick()
    // Check the new value.
    Espresso.onView(withText("Hello Compose")).check(matches(isDisplayed()))
}

Adicionar semântica no escopo da visualização para testes de interoperabilidade do Compose

Limitar pesquisas do Compose a visualizações específicas

Ao migrar interfaces complexas para o Compose, você pode encontrar elementos iguais do Compose aninhados em várias visualizações tradicionais do Android, como em um RecyclerView ou um ViewPager. Nesses cenários, uma pesquisa padrão do Compose como onNodeWithText("Save") pode falhar com um erro "Vários nós encontrados".

Em vez de modificar o código de produção para injetar tags de teste dinâmicas e distinguir esses elementos, você pode restringir o teste do Compose diretamente a uma visualização específica do Android.

Use a API onRootWithViewInteraction na sua regra de teste. Essa função aceita um ViewInteraction do Espresso, permitindo que você use o Espresso para isolar uma View de contêiner específica e realizar interações do Compose exclusivamente dentro dessa hierarquia com escopo.

Interagir com um item da lista

Se você precisar interagir com um elemento do Compose em uma linha RecyclerView específica, use o Espresso para localizar a linha e, em seguida, defina o escopo da interação do Compose para ela. Isso ignora elementos idênticos do Compose em todas as outras linhas.

@Test
fun testComposeButtonInsideRecyclerViewItem() = runComposeUiTest {
    // Scroll to the desired position using Espresso
    Espresso.onView(withId(recyclerViewId))
        .perform(RecyclerViewActions.scrollToPosition<MyViewHolder>(3))

    // Define an Espresso ViewInteraction that uniquely identifies the row
    val rowView = Espresso.onView(
        allOf(
            withId(rootViewId),
            hasDescendant(withText("Item #3"))
        )
    )

    // Scope the Compose search strictly to that specific row View
    onRootWithViewInteraction(rowView)
        .onNode(hasText("Like"))
        .performClick()
}

Resolver ambiguidade em ViewPagers

Quando vários fragmentos com layouts do Compose idênticos estão na memória simultaneamente, você pode restringir a pesquisa ao ID da visualização raiz do fragmento específico para evitar ambiguidade de correspondência.

@Test
fun testComposeButtonInsideViewPagerItem() = runComposeUiTest {
    // Swipe to the desired page using Espresso
    Espresso.onView(withId(viewPagerViewId)).perform(swipeLeft())

    // Identify the specific container view using Espresso
    val fragmentB = Espresso.onView(withId(fragmentRootViewId))

    // The generic text "Save" is now unique within this view scope
    onRootWithViewInteraction(fragmentB)
        .onNode(hasText("Save"))
        .assertIsDisplayed()
}

Interoperabilidade com o UiAutomator

Por padrão, os elementos combináveis podem ser acessados no UiAutomator apenas pelos descritores convenientes (texto mostrado, descrição do conteúdo etc.). Caso queira acessar um elemento combinável que use Modifier.testTag, é necessário ativar a propriedade semântica testTagsAsResourceId para a subárvore do elemento combinável específico. Ativar esse comportamento é útil para elementos combináveis que não têm nenhum outro elemento exclusivo, por exemplo, elementos roláveis como a LazyColumn.

Ative a propriedade semântica apenas uma vez na hierarquia dos elementos combináveis para garantir que todos os elementos combináveis aninhados com Modifier.testTag possam ser acessados pelo UiAutomator.

Scaffold(
    // Enables for all composables in the hierarchy.
    modifier = Modifier.semantics {
        testTagsAsResourceId = true
    }
){
    // Modifier.testTag is accessible from UiAutomator for composables nested here.
    LazyColumn(
        modifier = Modifier.testTag("myLazyColumn")
    ){
        // Content
    }
}

Todos os elementos combináveis com Modifier.testTag(tag) podem ser acessados com By.res(resourceName) usando a mesma tag do resourceName.

val device = UiDevice.getInstance(getInstrumentation())

val lazyColumn: UiObject2 = device.findObject(By.res("myLazyColumn"))
// Some interaction with the lazyColumn.

Outros recursos

  • Testar apps no Android: a página de destino principal de testes do Android oferece uma visão mais ampla dos princípios básicos e das técnicas de teste.
  • Conceitos básicos de testes:saiba mais sobre os principais conceitos por trás do teste de um app Android.
  • Testes locais:é possível executar alguns testes localmente, na sua própria estação de trabalho.
  • Testes instrumentados:é uma boa prática executar também testes instrumentados. Ou seja, testes executados diretamente no dispositivo.
  • Integração contínua:permite integrar seus testes ao pipeline de implantação.
  • Teste diferentes tamanhos de tela:com tantos dispositivos disponíveis para os usuários, é importante testar diferentes tamanhos de tela.
  • Espresso: embora seja destinado a interfaces baseadas em visualização, o conhecimento do Espresso ainda pode ser útil para alguns aspectos dos testes do Compose.