Fragmente testen

In diesem Thema wird beschrieben, wie Sie von Frameworks bereitgestellte APIs in Tests einbinden, um das Verhalten der einzelnen Fragmente zu bewerten.

Fragmente dienen als wiederverwendbare Container in Ihrer App, mit denen Sie das gleiche Benutzeroberflächenlayout in einer Vielzahl von Aktivitäten und Layoutkonfigurationen präsentieren können. Angesichts der Vielseitigkeit von Fragmenten ist es wichtig, zu überprüfen, ob sie eine einheitliche und ressourceneffiziente Nutzung bieten. Beachten Sie Folgendes:

  • Das Fragment sollte nicht von einer bestimmten übergeordneten Aktivität oder einem Fragment abhängig sein.
  • Sie sollten die Ansichtshierarchie eines Fragments nur dann erstellen, wenn es für den Nutzer sichtbar ist.

Zum Festlegen der Bedingungen für diese Tests stellt die AndroidX-fragment-testing-Bibliothek die Klasse FragmentScenario zum Erstellen von Fragmenten und Ändern ihrer Lifecycle.State bereit.

Abhängigkeiten deklarieren

Wenn Sie FragmentScenario verwenden möchten, definieren Sie das Artefakt fragment-testing-manifest in der Datei build.gradle Ihrer Anwendung mit debugImplementation und das Artefakt fragment-testing mit androidTestImplementation, wie im folgenden Beispiel gezeigt:

Groovig

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")
}

Für die Testbeispiele auf dieser Seite werden Assertions aus den Espresso- und Truth-Bibliotheken verwendet. Informationen zu anderen verfügbaren Test- und Assertion-Bibliotheken finden Sie unter Projekt für AndroidX Test einrichten.

Fragment erstellen

FragmentScenario enthält die folgenden Methoden zum Starten von Fragmenten in Tests:

  • launchInContainer() zum Testen der Benutzeroberfläche eines Fragments. FragmentScenario hängt das Fragment an den Root-Ansicht-Controller einer Aktivität an. Andernfalls ist diese Aktivität leer.
  • launch() zum Testen ohne die Benutzeroberfläche des Fragments. FragmentScenario hängt diese Art von Fragment an eine leere Aktivität an, die keine Stammansicht hat.

Nach dem Start eines dieser Fragmenttypen bringt FragmentScenario das zu testende Fragment in einen bestimmten Zustand. Standardmäßig ist dieser Status RESUMED. Sie können dies jedoch mit dem Argument initialState überschreiben. Der Status RESUMED gibt an, dass das Fragment ausgeführt wird und für den Nutzer sichtbar ist. Sie können Informationen zu den UI-Elementen mithilfe von Espresso-UI-Tests auswerten.

Die folgenden Codebeispiele zeigen, wie Sie das Fragment mit den einzelnen Methoden starten:

launchInContainer()-Beispiel

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

launch()-Beispiel

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

Abhängigkeiten angeben

Wenn Ihre Fragmente Abhängigkeiten haben, können Sie Testversionen dieser Abhängigkeiten bereitstellen, indem Sie eine benutzerdefinierte FragmentFactory für die Methoden launchInContainer() oder launch() angeben.

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

Weitere Informationen zur Verwendung von FragmentFactory zum Bereitstellen von Abhängigkeiten zu Fragmenten finden Sie unter Fragmentmanager.

Fragment in einen neuen Zustand versetzen

In den UI-Tests Ihrer Anwendung reicht es normalerweise aus, das zu testende Fragment zu starten und mit dem Test im Status RESUMED zu beginnen. Bei detaillierteren Einheitentests können Sie jedoch auch das Verhalten des Fragments beim Übergang von einem Lebenszyklusstatus in einen anderen bewerten. Den Anfangszustand geben Sie an, indem Sie das Argument initialState an eine der launchFragment*()-Funktionen übergeben.

Rufen Sie moveToState() auf, um dem Fragment einen anderen Lebenszyklusstatus zuzuweisen. Diese Methode unterstützt die folgenden Status als Argumente: CREATED, STARTED, RESUMED und DESTROYED. Diese Methode simuliert eine Situation, in der das Fragment oder die Aktivität, die Ihr Fragment enthält, seinen Status aus irgendeinem Grund ändert.

Im folgenden Beispiel wird ein Testfragment mit dem Status INITIALIZED gestartet und dann in den Status RESUMED versetzt:

@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.
        ...
    }
}

Fragment neu erstellen

Wenn Ihre Anwendung auf einem Gerät mit wenig Ressourcen ausgeführt wird, zerstört das System möglicherweise die Aktivität, die Ihr Fragment enthält. In diesem Fall muss Ihre Anwendung das Fragment neu erstellen, wenn der Nutzer zu ihm zurückkehrt. Rufen Sie recreate() auf, um diese Situation zu simulieren:

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

FragmentScenario.recreate() zerstört das Fragment und seinen Host und erstellt sie dann neu. Wenn die Klasse FragmentScenario das zu testende Fragment neu erstellt, kehrt das Fragment in den Lebenszyklusstatus zurück, in dem es sich vor dem Löschen befand.

Mit UI-Fragmenten interagieren

Verwenden Sie Espressoaufrufes-Matcher, um UI-Aktionen im zu testenden Fragment auszulösen. So können Sie mit Elementen in der Ansicht interagieren:

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

Wenn Sie eine Methode für das Fragment selbst aufrufen müssen, z. B. auf eine Auswahl im Optionsmenü, können Sie dies sicher tun. Rufen Sie dazu mit FragmentScenario.onFragment() einen Verweis auf das Fragment ab und übergeben Sie ein FragmentAction-Objekt:

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

Dialogfeldaktionen testen

FragmentScenario unterstützt auch das Testen von Dialogfragmenten. Obwohl Dialogfeldfragmente UI-Elemente enthalten, wird ihr Layout in einem separaten Fenster dargestellt und nicht in der Aktivität selbst. Verwenden Sie daher FragmentScenario.launch(), um Dialogfragmente zu testen.

Im folgenden Beispiel wird der Prozess zum Schließen des Dialogfelds getestet:

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