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 fungono da contenitori riutilizzabili all'interno dell'app, consentendoti di presentare lo stesso layout dell'interfaccia utente in una serie di attività e configurazioni di layout. Data la versatilità dei frammenti, è importante verificare che forniscano un'esperienza coerente ed efficiente nell'uso delle risorse. Nota:

  • Il frammento non deve dipendere da un'attività o da un frammento specifici.
  • Non devi creare la gerarchia delle visualizzazioni di un frammento a meno che il frammento non sia visibile all'utente.

Per configurare le condizioni per l'esecuzione di questi test, la libreria AndroidX fragment-testing fornisce la classe FragmentScenario per creare frammenti e modificare Lifecycle.State.

Dichiarazione delle dipendenze

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

trendy

dependencies {
    def fragment_version = "1.8.1"

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

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

Kotlin

dependencies {
    val fragment_version = "1.8.1"

    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 delle librerie Espresso e Truth. Per informazioni su altre librerie di test e asserzioni disponibili, consulta Configurare 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 collega il frammento al controller della vista principale di un'attività. In caso contrario, il campo contenente attività è vuoto.
  • launch(), per i test senza l'interfaccia utente del frammento. FragmentScenario allega questo tipo di frammento a un'attività vuota, che non ha una visualizzazione root.

Dopo aver avviato uno di questi tipi di frammenti, FragmentScenario sposta il frammento sottoposto a test a 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 le informazioni sui relativi elementi UI utilizzando i test dell'interfaccia utente di Espresso.

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 le versioni di test di queste dipendenze fornendo un valore FragmentFactory personalizzato ai metodi launchInContainer() o launch().

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

Per maggiori informazioni sull'utilizzo di FragmentFactory per fornire dipendenze ai frammenti, consulta Gestore dei frammenti.

Porta il frammento a un nuovo stato

Nei test dell'interfaccia utente dell'app, in genere è sufficiente avviare il frammento in fase di test e iniziare a testarlo da uno stato RESUMED. Nei test delle unità più granulari, tuttavia, potresti anche valutare il comportamento del frammento durante la transizione da uno stato del ciclo di vita a un altro. Puoi specificare lo stato iniziale passando l'argomento initialState a una qualsiasi delle funzioni 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 in cui il frammento o l'attività contenente il frammento cambia il proprio stato 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 con scarse risorse, il sistema potrebbe eliminare l'attività contenente il frammento. In questa situazione è necessario 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() elimina il frammento e il relativo host e poi li ricrea. Quando la classe FragmentScenario ricrea il frammento sottoposto a test, torna allo stato del ciclo di vita in cui si trovava prima di essere eliminato.

Interazione con i frammenti di UI

Per attivare le azioni dell'interfaccia utente nel frammento sottoposto a test, utilizza i matcher della visualizzazione Espresso per interagire con gli elementi nella visualizzazione:

@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 opzioni, puoi farlo in sicurezza ottenendo un riferimento al frammento utilizzando FragmentScenario.onFragment() e passando 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 il test dei frammenti di dialogo. Sebbene i frammenti di finestre di dialogo contengano elementi UI, il loro layout viene completato in una finestra separata anziché nell'attività stessa. Per questo motivo, utilizza FragmentScenario.launch() per testare i frammenti di finestre 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())
    }
}