Testowanie fragmentów

Z tego artykułu dowiesz się, jak uwzględniać w testach interfejsy API udostępniane przez platformę, które oceniają zachowanie każdego fragmentu.

Fragmenty służą jako kontenery wielokrotnego użytku w aplikacji, dzięki czemu możesz prezentować ten sam układ interfejsu w przypadku różnych działań i konfiguracji układu. Ze względu na wszechstronność fragmentów warto sprawdzić, czy zapewniają one spójne i oszczędne korzystanie z zasobów. Uwaga:

  • Twój fragment nie powinien być zależny od określonej aktywności nadrzędnej ani fragmentu.
  • Nie należy tworzyć hierarchii widoków danego fragmentu, chyba że jest on widoczny dla użytkownika.

Aby ułatwić skonfigurowanie warunków przeprowadzania tych testów, biblioteka fragment-testing AndroidaX udostępnia klasę FragmentScenario do tworzenia fragmentów i zmieniania Lifecycle.State.

Deklarowanie zależności

Aby użyć FragmentScenario, zdefiniuj artefakt fragment-testing w pliku build.gradle aplikacji za pomocą parametru debugImplementation, jak w tym przykładzie:

Odlotowy

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

Przykłady testowania na tej stronie wykorzystują asercje z bibliotek Espresso i Truth. Informacje o innych dostępnych bibliotekach testowania i asercji znajdziesz w artykule o konfigurowaniu projektu na potrzeby testu AndroidX.

Utwórz fragment

FragmentScenario stosuje te metody uruchamiania fragmentów w testach:

  • launchInContainer() do testowania interfejsu użytkownika fragmentu. FragmentScenario dołącza fragment do kontrolera widoku głównego aktywności. W przeciwnym razie ta aktywność jest pusta.
  • launch(), do testowania bez interfejsu użytkownika fragmentu. FragmentScenario dołącza ten typ fragmentu do pustej aktywności, która nie ma widoku głównego.

Po uruchomieniu jednego z tych typów fragmentów FragmentScenario przenosi poddany fragment do określonego stanu. Domyślnie ten stan to RESUMED, ale można go zastąpić za pomocą argumentu initialState. Stan RESUMED wskazuje, że fragment jest aktywny i widoczny dla użytkownika. Informacje o elementach interfejsu możesz oceniać za pomocą testów interfejsu Espresso.

W przykładach poniżej pokazujemy, jak uruchomić fragment przy użyciu poszczególnych metod:

przykład uruchamiania InContainer()

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

przykład uruchomienia()

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

Podaj zależności

Jeśli Twoje fragmenty zawierają zależności, możesz udostępnić testowe wersje tych zależności, podając niestandardowy atrybut FragmentFactory w metodzie launchInContainer() lub launch().

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

Więcej informacji o używaniu polecenia FragmentFactory do określania zależności do fragmentów znajdziesz w artykule Menedżer fragmentów.

Przeprowadź fragment do nowego stanu

W testach interfejsu aplikacji zwykle wystarczy uruchomić testowany fragment i rozpocząć testowanie od stanu RESUMED. W przypadku bardziej szczegółowych testów jednostkowych możesz też ocenić zachowanie fragmentu podczas przechodzenia z jednego stanu cyklu życia do innego. Stan początkowy możesz określić, przekazując argument initialState do dowolnej funkcji launchFragment*().

Aby przekierować fragment do innego stanu cyklu życia, wywołaj moveToState(). Ta metoda obsługuje jako argumenty te stany: CREATED, STARTED, RESUMED i DESTROYED. Ta metoda symuluje sytuację, w której fragment lub aktywność zawierająca fragment zmienia swój stan z dowolnego powodu.

Poniższy przykład powoduje uruchomienie fragmentu testowego w stanie INITIALIZED, a następnie przeniesienie go do stanu 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.
        ...
    }
}

Odtwórz fragment

Jeśli aplikacja działa na urządzeniu z małą ilością zasobów, system może zniszczyć aktywność zawierającą fragment. W takiej sytuacji aplikacja musi odtworzyć fragment, gdy użytkownik powróci do niego. Aby zasymulować taką sytuację, wywołaj recreate():

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

FragmentScenario.recreate() niszczy fragment i jego host, a następnie je odtwarza. Gdy klasa FragmentScenario odtworzy testowany fragment, powróci do stanu cyklu życia, w którym znajdował się przed zniszczeniem.

Interakcja z fragmentami interfejsu

Aby aktywować działania interfejsu użytkownika w testowanym fragmencie, użyj mechanizmów dopasowywania widoku Espresso do interakcji z elementami w widoku:

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

Jeśli musisz wywołać metodę w samym fragmencie, na przykład odpowiedzieć na wybór w menu opcji, możesz zrobić to bezpiecznie, uzyskując odwołanie do fragmentu za pomocą FragmentScenario.onFragment() i przekazując FragmentAction:

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

Przetestuj działania w oknie dialogowym

FragmentScenario obsługuje też fragmenty okna dialogowego do testowania. Choć fragmenty okien zawierają elementy interfejsu, ich układ jest zapełniany w osobnym oknie, a nie w samej aktywności. Z tego powodu do testowania fragmentów okien użyj FragmentScenario.launch().

Ten przykład pozwala przetestować proces zamykania okna:

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