Test amaçlı gezinme

Uygulamanızın beklediğiniz gibi çalıştığını doğrulamak için ürünü göndermeden önce uygulamanızın gezinme mantığını test etmeniz önemlidir.

Gezinme bileşeni, Google Cloud Platform ile hedefleri iletme, argümanları iletme ve FragmentManager. Bu özellikler zaten titizlikle test edildiğinden test etmeye gerek yoktur bunları uygulamanızda tekrar kullanabilirsiniz. Ancak test edilmesi gereken önemli nokta, parçalarınızdaki uygulamaya özel kod ile bunların NavController. Bu kılavuzda, sık karşılaşılan birkaç gezinme senaryosu ve bunların nasıl test edileceği açıklanmaktadır.

Parça gezinmeyi test et

NavController ile parça etkileşimlerini ayrı ayrı test etmek için Gezinme 2.3 ve üstü sürümler bir TestNavHostController Mevcut hedefi ayarlamak ve gerisini doğrulamak için API'ler sonrasında yığınla NavController.navigate() anlamına gelir.

uygulama modülünüzün build.gradle dosyasında aşağıdaki bağımlılığı gösterilmektedir:

Eski

dependencies {
  def nav_version = "2.8.0"

  androidTestImplementation "androidx.navigation:navigation-testing:$nav_version"
}

Kotlin

dependencies {
  val nav_version = "2.8.0"

  androidTestImplementation("androidx.navigation:navigation-testing:$nav_version")
}

Bir bilgi yarışması oyunu geliştirdiğinizi varsayalım. Oyun şununla başlar: title_screen ve kullanıcı tıkladığında in_game ekranına gider oyna.

title_screen öğesini temsil eden parça şuna benzer şekilde görünebilir:

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

Aşağıdaki işlemler yapıldığında uygulamanın kullanıcıyı in_game ekranına yönlendirip yönlendirmediğini test etmek için kullanıcı Oynat'ı tıklarsa testinizin bu parçanın NavController öğesini R.id.in_game ekranına doğru şekilde taşır.

FragmentScenario, Espresso, ve TestNavHostController özelliklerini test etmek için gerekli koşulları yeniden aşağıdaki örnekte gösterildiği gibi:

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

Yukarıdaki örnek, bir TestNavHostController örneği oluşturur ve bunu atar temsil eder. Daha sonra, kullanıcı arayüzünü çalıştırmak için Espresso'yu kullanır ve işlemi yapılır.

Gerçek bir NavController gibi, başlatmak için setGraph çağrısı yapmanız gerekir TestNavHostController. Bu örnekte, test edilen parça grafiğimizin başlangıç noktasıdır. TestNavHostController, setCurrentDestination geçerli hedefi (ve isteğe bağlı olarak bağımsız değişkenlerini) kullanarak NavController nesnesinin doğru duruma getirin.

NavHostFragment öğesinin kullanacağı NavHostController örneğinden farklı olarak, TestNavHostController, temel navigate() öğesini tetiklemez davranışı (FragmentNavigator tarafından yapılan FragmentTransaction gibi) navigate() çağrıldığında, yalnızca TestNavHostController.

FragmentSenaryo ile NavigationUI'yi test edin

Önceki örnekte, titleScenario.onFragment() adlı kullanıcıya sağlanan geri çağırma , parça, yaşam döngüsünden şuraya taşındıktan sonra çağrılır: RESUMED durumu. Bu ana kadar, parçanın görünümü oluşturulmuş ve eklenmiş, Bu nedenle doğru şekilde test etmek için yaşam döngüsünde çok geç olabilir. Örneğin, Parçanızda görünümlere sahip NavigationUI (ör. Toolbar kontrolünde olanlar) için, kurulumdan önce NavController ile kurulum yöntemlerini çağırabilirsiniz. parça RESUMED durumuna ulaşır. Bu nedenle, ayarlarınızı yönetmenin bir yolunu TestNavHostController daha erken.

Kendi Toolbar öğesine sahip olan bir parça aşağıdaki gibi yazılabilir:

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

Burada, onViewCreated() çağrıldığında oluşturulan NavController öğesine ihtiyacımız var. Önceki onFragment() yaklaşımını kullanmak TestNavHostController çok geç olduğundan findNavController() çağrısı başarısız olabilir.

FragmentScenario şunları sunar: FragmentFactory yaşam döngüsü olayları için geri çağırmaların kaydedilmesinde kullanılan bir arayüz oluşturur. Bu almak için Fragment.getViewLifecycleOwnerLiveData() ile birleştirilir aşağıdaki gibi onCreateView() hemen sonra gelen geri çağırma örnek:

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

Bu teknik kullanıldığında NavController, onViewCreated() çağrılır ve parçanın NavigationUI yöntemleri kullanmasına izin verilir hale getirir.

Geri yığın girişleriyle etkileşimleri test etme

Geri yığın girişleriyle etkileşimde bulunurken, TestNavHostController, kumandayı kendi cihazınıza bağlamanıza olanak tanır. LifecycleOwner, ViewModelStore ve OnBackPressedDispatcher özelliklerini test etmek için devraldığı API'leri kullanarak NavHostController.

Örneğin, navigasyon kapsamlı ViewModel, araman lazım setViewModelStore TestNavHostController tarihinde:

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