Przed wysyłką należy przetestować logikę nawigacji w aplikacji, aby sprawdzić, czy działa ona zgodnie z oczekiwaniami.
Komponent Nawigacja obsługuje wszystkie zadania związane z zarządzaniem nawigacją
miejsc docelowych, przekazywania argumentów oraz pracy z
FragmentManager
Te funkcje zostały już dokładnie przetestowane, więc nie trzeba testować
je ponownie w aplikacji. Ważne jest jednak, aby przetestować interakcje
między kodem aplikacji we fragmentach a ich
NavController
W tym przewodniku omawiamy kilka typowych scenariuszy nawigacji i sposoby ich testowania.
Nawigacja po fragmentach testowych
Aby przetestować izolowane interakcje z fragmentami za pomocą NavController
,
Nawigacja w wersji 2.3 i nowszych
TestNavHostController
.
udostępnia interfejsy API do ustawiania bieżącego miejsca docelowego i weryfikowania tylnej części
stos po
NavController.navigate()
operacji.
Możesz dodać do projektu artefakt Test nawigacji, dodając do niego
ta zależność w pliku build.gradle
modułu aplikacji:
Odlotowe
dependencies { def nav_version = "2.7.7" androidTestImplementation "androidx.navigation:navigation-testing:$nav_version" }
Kotlin
dependencies { val nav_version = "2.7.7" androidTestImplementation("androidx.navigation:navigation-testing:$nav_version") }
Załóżmy, że tworzysz quiz. Gra zaczyna się od title_screen i przechodzi do ekranu in_game, gdy użytkownik kliknie reklamę Google Play.
Fragment reprezentujący parametr title_screen może wyglądać tak:
Kotlin
class TitleScreen : Fragment() { override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ) = inflater.inflate(R.layout.fragment_title_screen, container, false) override fun onViewCreated(view: View, savedInstanceState: Bundle?) { view.findViewById<Button>(R.id.play_btn).setOnClickListener { view.findNavController().navigate(R.id.action_title_screen_to_in_game) } } }
Java
public class TitleScreen extends Fragment { @Nullable @Override public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { return inflater.inflate(R.layout.fragment_title_screen, container, false); } @Override public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { view.findViewById(R.id.play_btn).setOnClickListener(v -> { Navigation.findNavController(view).navigate(R.id.action_title_screen_to_in_game); }); } }
Aby przetestować, czy aplikacja prawidłowo przechodzi użytkownika na ekran in_game, gdy:
użytkownik kliknie Odtwórz, test musi sprawdzić, czy fragment
prawidłowo przenosi element NavController
na ekran R.id.in_game
.
Korzystając z połączenia FragmentScenario
, Espresso,
i TestNavHostController
, możesz odtworzyć warunki niezbędne do przetestowania
w tym scenariuszu, jak w przykładzie poniżej:
Kotlin
@RunWith(AndroidJUnit4::class) class TitleScreenTest { @Test fun testNavigationToInGameScreen() { // Create a TestNavHostController val navController = TestNavHostController( ApplicationProvider.getApplicationContext()) // Create a graphical FragmentScenario for the TitleScreen val titleScenario = launchFragmentInContainer<TitleScreen>() titleScenario.onFragment { fragment -> // Set the graph on the TestNavHostController navController.setGraph(R.navigation.trivia) // Make the NavController available via the findNavController() APIs Navigation.setViewNavController(fragment.requireView(), navController) } // Verify that performing a click changes the NavController’s state onView(ViewMatchers.withId(R.id.play_btn)).perform(ViewActions.click()) assertThat(navController.currentDestination?.id).isEqualTo(R.id.in_game) } }
Java
@RunWith(AndroidJUnit4.class) public class TitleScreenTestJava { @Test public void testNavigationToInGameScreen() { // Create a TestNavHostController TestNavHostController navController = new TestNavHostController( ApplicationProvider.getApplicationContext()); // Create a graphical FragmentScenario for the TitleScreen FragmentScenario<TitleScreen> titleScenario = FragmentScenario.launchInContainer(TitleScreen.class); titleScenario.onFragment(fragment -> // Set the graph on the TestNavHostController navController.setGraph(R.navigation.trivia); // Make the NavController available via the findNavController() APIs Navigation.setViewNavController(fragment.requireView(), navController) ); // Verify that performing a click changes the NavController’s state onView(ViewMatchers.withId(R.id.play_btn)).perform(ViewActions.click()); assertThat(navController.currentDestination.id).isEqualTo(R.id.in_game); } }
Ten przykład tworzy i przypisuje instancję TestNavHostController
do tego fragmentu. Następnie narzędzie wykorzystuje interfejs Espresso do obsługi interfejsu i sprawdza, czy
podjęto odpowiednie działanie.
Tak jak w przypadku prawdziwego NavController
, musisz wywołać setGraph
, aby zainicjować
TestNavHostController
. W tym przykładzie testowany fragment to
jest to miejsce, w którym zaczyna się nasz wykres. TestNavHostController
udostępnia
setCurrentDestination
.
która pozwala ustawić bieżące miejsce docelowe (oraz opcjonalnie
argumentów dla tego miejsca docelowego), tak aby NavController
znalazł się w
właściwy stan przed rozpoczęciem testu.
W przeciwieństwie do instancji NavHostController
, której używa NavHostFragment
,
TestNavHostController
nie aktywuje bazowego elementu navigate()
działanie (takie jak FragmentTransaction
, które wykonuje FragmentNavigator
)
gdy wywołujesz navigate()
— aktualizuje tylko stan
TestNavHostController
Testowanie interfejsu NavigationUI z użyciem fragmentu FragmentScenariusz
W poprzednim przykładzie wywołanie zwrotne zostało udostępnione do titleScenario.onFragment()
jest wywoływana po przejściu fragmentu przez swój cykl życia do
RESUMED
.
stanu. Na tym etapie widok fragmentu został już utworzony i dołączony,
więc może być jeszcze za późno w cyklu życia na prawidłowe testowanie. Na przykład podczas używania funkcji
NavigationUI
z wyświetleniami we fragmencie, np. z kontrolowanym elementem Toolbar
według swojego fragmentu, możesz wywołać metody konfiguracji za pomocą NavController
przed
fragment osiągnie stan RESUMED
. Potrzebujesz więc sposobu
TestNavHostController
na wcześniejszym etapie cyklu życia.
Fragment zawierający własny element Toolbar
może być zapisany w taki sposób:
Kotlin
class TitleScreen : Fragment(R.layout.fragment_title_screen) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { val navController = view.findNavController() view.findViewById<Toolbar>(R.id.toolbar).setupWithNavController(navController) } }
Java
public class TitleScreen extends Fragment { public TitleScreen() { super(R.layout.fragment_title_screen); } @Override public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { NavController navController = Navigation.findNavController(view); view.findViewById(R.id.toolbar).setupWithNavController(navController); } }
Tutaj potrzebujemy elementu NavController
utworzonego przed wywołaniem funkcji onViewCreated()
.
Zastosowanie poprzedniego podejścia, czyli onFragment()
, sprawiłoby, że TestNavHostController
zbyt późno w cyklu życia, przez co wywołanie findNavController()
kończy się niepowodzeniem.
FragmentScenario
oferuje
FragmentFactory
.
który może służyć do rejestrowania wywołań zwrotnych dla zdarzeń cyklu życia. Może to spowodować
zostanie połączona z Fragment.getViewLifecycleOwnerLiveData()
, aby otrzymać
wywołanie zwrotne następujące bezpośrednio po onCreateView()
, jak poniżej
przykład:
Kotlin
val scenario = launchFragmentInContainer { TitleScreen().also { fragment -> // In addition to returning a new instance of our Fragment, // get a callback whenever the fragment’s view is created // or destroyed so that we can set the NavController fragment.viewLifecycleOwnerLiveData.observeForever { viewLifecycleOwner -> if (viewLifecycleOwner != null) { // The fragment’s view has just been created navController.setGraph(R.navigation.trivia) Navigation.setViewNavController(fragment.requireView(), navController) } } } }
Java
FragmentScenario<TitleScreen> scenario = FragmentScenario.launchInContainer( TitleScreen.class, null, new FragmentFactory() { @NonNull @Override public Fragment instantiate(@NonNull ClassLoader classLoader, @NonNull String className, @Nullable Bundle args) { TitleScreen titleScreen = new TitleScreen(); // In addition to returning a new instance of our fragment, // get a callback whenever the fragment’s view is created // or destroyed so that we can set the NavController titleScreen.getViewLifecycleOwnerLiveData().observeForever(new Observer<LifecycleOwner>() { @Override public void onChanged(LifecycleOwner viewLifecycleOwner) { // The fragment’s view has just been created if (viewLifecycleOwner != null) { navController.setGraph(R.navigation.trivia); Navigation.setViewNavController(titleScreen.requireView(), navController); } } }); return titleScreen; } });
Dzięki tej metodzie interfejs NavController
jest dostępny przed
Wywoływana jest metoda onViewCreated()
, która pozwala na użycie metod NavigationUI
dla fragmentu
bez awarii.
Testowanie interakcji z wpisami stosu wstecznego
Podczas interakcji z wpisami wstecznymi,
TestNavHostController
umożliwia podłączenie kontrolera do własnego
test LifecycleOwner
, ViewModelStore
i OnBackPressedDispatcher
przez
przy użyciu interfejsów API odziedziczonych
NavHostController
.
Na przykład podczas testowania fragmentu z atrybutem
ViewModel o zakresie do nawigacji,
Musisz zadzwonić
setViewModelStore
.
– TestNavHostController
:
Kotlin
val navController = TestNavHostController(ApplicationProvider.getApplicationContext()) // This allows fragments to use by navGraphViewModels() navController.setViewModelStore(ViewModelStore())
Java
TestNavHostController navController = new TestNavHostController(ApplicationProvider.getApplicationContext()); // This allows fragments to use new ViewModelProvider() with a NavBackStackEntry navController.setViewModelStore(new ViewModelStore())
Powiązane artykuły
- Tworzenie zinstrumentowanych testów jednostkowych - Dowiedz się, jak skonfigurować pakiet testów instrumentowanych i przeprowadzać testy na Androidzie urządzenia.
- Espresso – przetestuj interfejs aplikacji z espresso.
- Reguły JUnit4 w teście AndroidaX – użyj JUnit 4 z bibliotekami Testów AndroidX, które zapewniają większą elastyczność i stały kod wymagany w testach.
- Przetestuj fragmenty aplikacji -
Dowiedz się, jak testować fragmenty aplikacji osobno za pomocą narzędzia
FragmentScenario
. - Konfigurowanie projektu na potrzeby Testu AndroidX – informacje Jak zadeklarować potrzebne biblioteki w plikach projektu aplikacji na potrzeby AndroidaX Testuj.