Z tego artykułu dowiesz się, jak uwzględnić interfejsy API udostępniane przez platformę w testach, które oceniają zachowanie poszczególnych fragmentów.
Fragmenty kodu pełnią w aplikacji kontenery wielokrotnego użytku, co pozwala prezentować ten sam układ interfejsu w różnych działaniach i konfiguracjach układu. Ze względu na uniwersalność fragmentów ważne jest, aby potwierdzić, że zapewniają one spójność i oszczędność zasobów. Uwaga:
- Twój fragment nie powinien być zależny od konkretnej aktywności lub fragmentu elementu nadrzędnego.
- Nie twórz hierarchii widoku fragmentu, jeśli dany fragment nie jest widoczny dla użytkownika.
Aby ułatwić skonfigurowanie warunków przeprowadzania tych testów, biblioteka AndroidaX fragment-testing
udostępnia klasę FragmentScenario
do tworzenia fragmentów i zmieniania ich Lifecycle.State
.
Deklarowanie zależności
Aby użyć FragmentScenario
, zdefiniuj artefakt fragment-testing-manifest
w pliku build.gradle
aplikacji za pomocą debugImplementation
, a artefakt fragment-testing
za pomocą parametru androidTestImplementation
, jak w tym przykładzie:
Odlotowy
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") }
Przykłady testów na tej stronie wykorzystują asercje z bibliotek Espresso i Truth. Informacje o innych dostępnych bibliotekach testowania i potwierdzania znajdziesz w artykule Konfigurowanie projektu na potrzeby testu AndroidX.
Tworzenie fragmentu
FragmentScenario
udostępnia te metody uruchamiania fragmentów w testach:
launchInContainer()
do testowania interfejsu użytkownika fragmentu.FragmentScenario
dołącza fragment do kontrolera głównego widoku aktywności. W przeciwnym razie pole zawierające dane o aktywności jest puste.launch()
do testowania bez użycia interfejsu użytkownika fragmentu.FragmentScenario
łączy ten typ fragmentu do pustego działania, które nie ma głównego widoku.
Po uruchomieniu jednego z tych typów fragmentów FragmentScenario
ustawia testowany fragment do określonego stanu. Domyślny stan to RESUMED
, ale można to zmienić za pomocą argumentu initialState
. Stan RESUMED
wskazuje, że fragment jest aktywny i widoczny dla użytkownika. Informacje o elementach interfejsu możesz ocenić za pomocą testów interfejsu Espresso.
Poniższe przykłady kodu pokazują, jak uruchomić fragment przy użyciu poszczególnych metod:
PrzykładlaunchInContainer()
@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() – przykład
@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, dodając niestandardowy FragmentFactory
do metod launchInContainer()
lub launch()
.
@RunWith(AndroidJUnit4::class)
class MyTestSuite {
@Test fun testEventFragment() {
val someDependency = TestDependency()
launchFragmentInContainer {
EventFragment(someDependency)
}
...
}
}
Więcej informacji o korzystaniu z elementu FragmentFactory
do określania zależności od fragmentów znajdziesz w artykule o menedżerze fragmentów.
Przenieś fragment do nowego stanu
W testach interfejsu aplikacji zwykle wystarczy uruchomić testowany fragment i rozpocząć testowanie od stanu RESUMED
. Jednak w bardziej szczegółowych testach jednostkowych możesz też ocenić zachowanie fragmentu podczas przechodzenia z jednego stanu cyklu życia do innego. Aby określić stan początkowy, przekaż 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 działanie zawierające Twój fragment zmienia swój stan z dowolnego powodu.
Poniższy przykład uruchamia fragment testowy w stanie INITIALIZED
, a następnie przenosi 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, na którym brakuje zasobów, system może zniszczyć aktywność zawierającą Twój fragment. W takiej sytuacji aplikacja musi odtworzyć fragment, gdy użytkownik do niego wróci.
Aby zasymulować tę sytuację, zadzwoń pod numer recreate()
:
@RunWith(AndroidJUnit4::class)
class MyTestSuite {
@Test fun testEventFragment() {
val scenario = launchFragmentInContainer<EventFragment>()
scenario.recreate()
...
}
}
FragmentScenario.recreate()
niszczy fragment i jego hosta, a następnie tworzy je. Gdy klasa FragmentScenario
odtworzy testowany fragment, fragment powraca do stanu cyklu życia sprzed jego zniszczenia.
Interakcja z fragmentami interfejsu
Aby aktywować działania interfejsu dla testowanego fragmentu, używaj dopasowań widoku Espresso do interakcji z elementami w widoku danych:
@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 chcesz wywołać metodę dla samego fragmentu, np. odpowiedzieć na wybór opcji z menu opcji, możesz to zrobić bezpiecznie, uzyskując odniesienie do fragmentu za pomocą polecenia FragmentScenario.onFragment()
i przekazując je w FragmentAction
:
@RunWith(AndroidJUnit4::class)
class MyTestSuite {
@Test fun testEventFragment() {
val scenario = launchFragmentInContainer<EventFragment>()
scenario.onFragment { fragment ->
fragment.myInstanceMethod()
}
}
}
Testuj działania w oknie dialogowym
FragmentScenario
obsługuje też testowanie fragmentów okien dialogowych. Chociaż fragmenty okien zawierają elementy interfejsu, ich układ znajduje się w osobnym oknie, a nie w samym działaniu. Dlatego do testowania fragmentów okien używaj FragmentScenario.launch()
.
Ten przykład testuje 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())
}
}