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:
Groovy
dependencies { def nav_version = "2.9.5" androidTestImplementation "androidx.navigation:navigation-testing:$nav_version" }
Kotlin
dependencies { val nav_version = "2.9.5" 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.