Interoperabilità

Compose si integra con i framework di test più comuni.

Interoperabilità con Espresso

In un'app ibrida, puoi trovare i componenti di Compose all'interno delle gerarchie di oggetti View e gli oggetti View all'interno dei componibili di Compose (tramite il componibile AndroidView).

Non sono necessari passaggi speciali per trovare corrispondenze con entrambi i tipi. Puoi abbinare le visualizzazioni con onView di Espresso e gli elementi di Compose con 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()))
}

Aggiungi la semantica con ambito View per i test di interoperabilità di Compose

Limita l'ambito delle ricerche di Compose a visualizzazioni specifiche

Durante la migrazione di UI complesse a Compose, potresti riscontrare elementi Compose identici nidificati all'interno di più visualizzazioni Android tradizionali, ad esempio all'interno di un RecyclerView o di un ViewPager. In questi scenari, una ricerca standard di Compose come onNodeWithText("Save") potrebbe non riuscire con un errore "Sono stati trovati più nodi".

Anziché modificare il codice di produzione per inserire tag di test dinamici per distinguere questi elementi, puoi limitare l'ambito del test di Compose direttamente a una visualizzazione Android specifica.

Utilizza l'API onRootWithViewInteraction sulla regola di test. Questa funzione accetta un ViewInteraction Espresso, consentendoti di sfruttare Espresso per isolare una View contenitore specifica ed eseguire interazioni di Compose esclusivamente all'interno di questa gerarchia con ambito.

Interagire con un elemento dell'elenco

Se devi interagire con un elemento Compose all'interno di una riga RecyclerView specifica, utilizza Espresso per individuare la riga, quindi limita l'interazione di Compose a questa riga. In questo modo, gli elementi di composizione identici in tutte le altre righe vengono ignorati.

@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()
}

Risolvi l'ambiguità nei ViewPager

Quando più frammenti con layout di Compose identici sono in memoria contemporaneamente, puoi limitare la ricerca all'ID della visualizzazione principale del frammento specifico per evitare ambiguità di corrispondenza.

@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()
}

Interoperabilità con UiAutomator

Per impostazione predefinita, i componibili sono accessibili da UiAutomator solo tramite i relativi descrittori pratici (testo visualizzato, descrizione dei contenuti e così via). Se vuoi accedere a un componibile che utilizza Modifier.testTag, devi attivare la proprietà semantica testTagsAsResourceId per il sottoalbero del componibile specifico. L'attivazione di questo comportamento è utile per i componibili che non hanno un altro handle univoco, come i componibili scorrevoli (ad esempio, LazyColumn).

Attiva la proprietà semantica una sola volta in alto nella gerarchia dei composabili per assicurarti che tutti i composabili nidificati con Modifier.testTag siano accessibili da 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
    }
}

Qualsiasi componibile con Modifier.testTag(tag) può essere accessibile con l'utilizzo di By.res(resourceName) utilizzando lo stesso tag di resourceName.

val device = UiDevice.getInstance(getInstrumentation())

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

Risorse aggiuntive

  • Testa le app su Android: la pagina di destinazione principale dei test su Android offre una visione più ampia dei concetti fondamentali e delle tecniche di test.
  • Concetti fondamentali sul test di app Android: scopri di più sui concetti di base alla base del test di un'app per Android.
  • Test locali: puoi eseguire alcuni test localmente, sulla tua workstation.
  • Test di instrumentazione: è buona norma eseguire anche i test di instrumentazione. ovvero test eseguiti direttamente sul dispositivo.
  • Integrazione continua: l'integrazione continua ti consente di integrare i test nella pipeline di deployment.
  • Testa diverse dimensioni dello schermo:con così tanti dispositivi a disposizione degli utenti, devi eseguire test per diverse dimensioni dello schermo.
  • Espresso: sebbene sia destinato alle UI basate su View, la conoscenza di Espresso può comunque essere utile per alcuni aspetti dei test di Compose.