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 varietà di attività e configurazioni di layout. Data la versatilità dei frammenti, è importante verificare che offrano un'esperienza coerente ed efficiente nell'uso delle risorse. Nota:

  • Il frammento non deve dipendere da un'attività principale o da un frammento specifico.
  • 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 fragment-testing di AndroidX fornisce la classe FragmentScenario per creare frammenti e modificarne Lifecycle.State.

Dichiarazione delle dipendenze

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

trendy

dependencies {
    def fragment_version = "1.6.2"

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

Kotlin

dependencies {
    val fragment_version = "1.6.2"

    debugImplementation("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 di asserzioni disponibili, consulta la pagina relativa alla configurazione di un progetto per AndroidX Test.

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 di visualizzazione principale di un'attività. Questo campo contenente attività è altrimenti vuoto.
  • launch(), per i test senza l'interfaccia utente del frammento. FragmentScenario allega questo tipo di frammento a un'attività vuota, priva di una vista principale.

Dopo aver avviato uno di questi tipi di frammenti, FragmentScenario indirizza 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 suoi elementi UI utilizzando i test della UI 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)
        ...
    }
}

Fornire dipendenze

Se i frammenti hanno dipendenze, puoi fornire versioni di test di queste dipendenze fornendo un FragmentFactory personalizzato ai metodi 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 dipendenze ai frammenti, consulta Gestione dei frammenti.

Sposta il frammento in 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. Tuttavia, nei test delle unità più granulari, 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 diverso del ciclo di vita, 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 per qualsiasi motivo il proprio stato.

L'esempio seguente avvia un frammento di test in stato INITIALIZED e poi 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 ha poche risorse, il sistema potrebbe eliminare l'attività contenente il frammento. Questa situazione richiede all'app di ricreare il frammento quando l'utente vi torna. 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, quindi li ricrea. Quando la classe FragmentScenario ricrea il frammento in fase di test, il frammento torna allo stato del ciclo di vita in cui si trovava prima di essere eliminato.

Interazione con i frammenti UI

Per attivare le azioni dell'interfaccia utente nel frammento in fase di test, utilizza i matcher di visualizzazione Espresso per interagire con gli elementi della vista:

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

Testa le azioni della finestra di dialogo

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

L'esempio seguente verifica la procedura di chiusura della finestra 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())
    }
}