Testare i tuoi frammenti

Questo argomento descrive come includere le API fornite dal framework nei test che valutano il comportamento di ogni frammento.

I frammenti servono come contenitori riutilizzabili all'interno dell'app, consentendoti presentano lo stesso layout dell'interfaccia utente in una serie di attività configurazioni di layout. Data la versatilità dei frammenti, è importante per verificare che forniscano un'esperienza coerente ed efficiente nell'uso delle risorse. Nota:

  • Il frammento non deve dipendere da un'attività principale specifica o .
  • Non devi creare la gerarchia di visualizzazione di un frammento, a meno che quest'ultimo è visibile all'utente.

Per configurare le condizioni necessarie per l'esecuzione di questi test, AndroidX La libreria fragment-testing offre le FragmentScenario per creare frammenti e modificarne Lifecycle.State.

Dichiarazione delle dipendenze

Per utilizzare FragmentScenario, definisci l'artefatto fragment-testing-manifest nel il file build.gradle dell'app con debugImplementation e l'elemento fragment-testing con androidTestImplementation, come mostrato in nell'esempio seguente:

Alla moda

dependencies {
    def fragment_version = "1.8.3"

    debugImplementation "androidx.fragment:fragment-testing-manifest:$fragment_version"

    androidTestImplementation "androidx.fragment:fragment-testing:$fragment_version"
}

Kotlin

dependencies {
    val fragment_version = "1.8.3"

    debugImplementation("androidx.fragment:fragment-testing-manifest:$fragment_version")

    androidTestImplementation("androidx.fragment:fragment-testing:$fragment_version")
}

Gli esempi di test in questa pagina utilizzano le asserzioni del Espresso e librerie Truth. Per informazioni su altre librerie di test e asserzioni disponibili, vedi Configura il progetto per il test AndroidX.

Crea un frammento

FragmentScenario include i seguenti metodi per avviare i frammenti nei test:

  • launchInContainer(), per testare l'interfaccia utente di un frammento. FragmentScenario allega al controller della vista principale di un'attività. Contenuti contenenti attività altrimenti è vuoto.
  • launch(), per i test senza l'interfaccia utente del frammento. FragmentScenario collega questo tipo di frammento a un'attività vuota, che non avere una vista root.

Dopo aver avviato uno di questi tipi di frammenti, FragmentScenario guida la del frammento sottoposto a test in uno stato specificato. Per impostazione predefinita, questo stato è RESUMED, ma puoi sostituirlo con l'argomento initialState. Lo stato RESUMED indica che il frammento è in esecuzione ed è visibile all'utente. Puoi valutare informazioni sui suoi elementi UI utilizzando Espresso UI test.

I seguenti esempi di codice mostrano come avviare il frammento utilizzando ciascun metodo:

Esempio di lancioInContainer()

@RunWith(AndroidJUnit4::class)
class MyTestSuite {
    @Test fun testEventFragment() {
        // The "fragmentArgs" argument is optional.
        val fragmentArgs = bundleOf(selectedListItem to 0)
        val scenario = launchFragmentInContainer<EventFragment>(fragmentArgs)
        ...
    }
}

Esempio di lancio()

@RunWith(AndroidJUnit4::class)
class MyTestSuite {
    @Test fun testEventFragment() {
        // The "fragmentArgs" arguments are optional.
        val fragmentArgs = bundleOf("numElements" to 0)
        val scenario = launchFragment<EventFragment>(fragmentArgs)
        ...
    }
}

Fornisci le dipendenze

Se i tuoi frammenti hanno dipendenze, puoi fornire versioni di test di queste dipendenze fornendo un valore FragmentFactory personalizzato launchInContainer() o launch().

@RunWith(AndroidJUnit4::class)
class MyTestSuite {
    @Test fun testEventFragment() {
        val someDependency = TestDependency()
        launchFragmentInContainer {
            EventFragment(someDependency)
        }
        ...
    }
}

Per ulteriori informazioni sull'utilizzo di FragmentFactory per fornire le dipendenze ai frammenti, vedi Gestore dei frammenti:

Porta il frammento a un nuovo stato

Nei test dell'interfaccia utente dell'app, di solito è sufficiente lanciare il frammento in corso e inizia a testarlo da uno stato RESUMED. In modo più granulare test delle unità; tuttavia, potresti anche valutare il comportamento del frammento durante la transizione da uno stato del ciclo di vita a un altro. Puoi specificare stato iniziale passando l'argomento initialState a uno qualsiasi degli Funzioni di launchFragment*().

Per portare il frammento a uno stato del ciclo di vita diverso, chiama moveToState() Questo metodo supporta i seguenti stati come argomenti: CREATED, STARTED, RESUMED e DESTROYED. Questo metodo simula una situazione dove il frammento o l'attività contenente il frammento ne modifica la per qualsiasi motivo.

L'esempio seguente avvia un frammento di test nello stato INITIALIZED e quindi lo sposta nello stato RESUMED:

@RunWith(AndroidJUnit4::class)
class MyTestSuite {
    @Test fun testEventFragment() {
        val scenario = launchFragmentInContainer<EventFragment>(
            initialState = Lifecycle.State.INITIALIZED
        )
        // EventFragment has gone through onAttach(), but not onCreate().
        // Verify the initial state.
        scenario.moveToState(Lifecycle.State.RESUMED)
        // EventFragment moves to CREATED -> STARTED -> RESUMED.
        ...
    }
}

Ricrea il frammento

Se la tua app è in esecuzione su un dispositivo che sta per esaurire le risorse, il sistema potrebbe distruggere l'attività contenente il frammento. Questa situazione richiede che l'app ricrei il frammento quando l'utente vi ritorna. Per simulare questa situazione, chiama recreate():

@RunWith(AndroidJUnit4::class)
class MyTestSuite {
    @Test fun testEventFragment() {
        val scenario = launchFragmentInContainer<EventFragment>()
        scenario.recreate()
        ...
    }
}

FragmentScenario.recreate() distrugge il frammento e il suo host e poi li ricrea. Quando La classe FragmentScenario ricrea il frammento sottoposto a test, ovvero torna allo stato del ciclo di vita precedente all'eliminazione.

Interazione con i frammenti di UI

Per attivare le azioni dell'interfaccia utente nel frammento sottoposto a test, utilizza Corrispondenza visualizzazione espresso per interagire con gli elementi visualizzati:

@RunWith(AndroidJUnit4::class)
class MyTestSuite {
    @Test fun testEventFragment() {
        val scenario = launchFragmentInContainer<EventFragment>()
        onView(withId(R.id.refresh)).perform(click())
        // Assert some expected behavior
        ...
    }
}

Se devi chiamare un metodo sul frammento stesso, ad esempio rispondendo a una selezione nel menu delle opzioni, puoi farlo in sicurezza ottenendo riferimento al frammento utilizzando FragmentScenario.onFragment() e passando attraverso un FragmentAction:

@RunWith(AndroidJUnit4::class)
class MyTestSuite {
    @Test fun testEventFragment() {
        val scenario = launchFragmentInContainer<EventFragment>()
        scenario.onFragment { fragment ->
            fragment.myInstanceMethod()
        }
    }
}

Azioni della finestra di dialogo di test

FragmentScenario supporta anche i test frammenti di dialogo. Anche se i frammenti di dialogo presentano elementi UI, il loro layout viene inserito in una finestra separata, anziché piuttosto che nell'attività stessa. Per questo motivo, utilizza FragmentScenario.launch() per testare i frammenti di finestra di dialogo.

L'esempio seguente verifica il processo di chiusura delle finestre di dialogo:

@RunWith(AndroidJUnit4::class)
class MyTestSuite {
    @Test fun testDismissDialogFragment() {
        // Assumes that "MyDialogFragment" extends the DialogFragment class.
        with(launchFragment<MyDialogFragment>()) {
            onFragment { fragment ->
                assertThat(fragment.dialog).isNotNull()
                assertThat(fragment.requireDialog().isShowing).isTrue()
                fragment.dismiss()
                fragment.parentFragmentManager.executePendingTransactions()
                assertThat(fragment.dialog).isNull()
            }
        }

        // Assumes that the dialog had a button
        // containing the text "Cancel".
        onView(withText("Cancel")).check(doesNotExist())
    }
}