Interoperabilità

Compose si integra con i framework di test comuni.

Interoperabilità con Espresso

In un'app ibrida, puoi trovare i componenti di Compose all'interno delle gerarchie di visualizzazione e le visualizzazioni all'interno dei composable di Compose (tramite il composable AndroidView).

Non sono necessari passaggi speciali per la corrispondenza di entrambi i tipi. Abbina le visualizzazioni con onView di Espresso e gli elementi 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 di visualizzazione per i test di interoperabilità di Compose

Limitare le ricerche di Compose a visualizzazioni specifiche

Quando esegui la migrazione di UI complesse a Compose, potresti riscontrare elementi Compose identici nidificati all'interno di più View 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 e restituire l'errore "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 View Android specifica.

Utilizza l'API onRootWithViewInteraction nella regola di test. Questa funzione accetta un ViewInteraction Espresso, consentendoti di sfruttare Espresso per isolare una visualizzazione container specifica ed eseguire interazioni di composizione esclusivamente all'interno di quella gerarchia con ambito.

Interagire con una voce di 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 con Compose a questa riga. Vengono ignorati gli elementi di composizione identici in tutte le altre righe.

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

Risolvere l'ambiguità nei ViewPager

Quando in memoria sono presenti più frammenti con layout di composizione identici contemporaneamente, puoi limitare la ricerca all'ID View radice del frammento specifico per evitare ambiguità nella 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 composable sono accessibili da UiAutomator solo tramite i loro descrittori pratici (testo visualizzato, descrizione dei contenuti e così via). Se vuoi accedere a qualsiasi 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 composable che non hanno altri handle unici, come i composable scorrevoli (ad esempio, LazyColumn).

Attiva la proprietà semantica una sola volta in alto nella gerarchia dei composable per assicurarti che tutti i composable 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 elemento componibile con Modifier.testTag(tag) può essere accessibile con l'uso 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

  • Testare le app su Android: la pagina di destinazione principale dei test Android offre una visione più ampia delle tecniche e delle nozioni di base sui test.
  • Nozioni di base sui test: scopri di più sui concetti fondamentali alla base del test di un'app per Android.
  • Test locali: puoi eseguire alcuni test localmente, sulla tua workstation.
  • Test strumentati: è buona norma eseguire anche test strumentati. 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 molti dispositivi a disposizione degli utenti, devi eseguire test per diverse dimensioni dello schermo.
  • Espresso: sebbene sia destinato alle UI basate sulla visualizzazione, la conoscenza di Espresso può comunque essere utile per alcuni aspetti dei test di Compose.