Uygulamanızın beklediğiniz gibi çalıştığını doğrulamak için göndermeden önce uygulamanızın gezinme mantığını test etmeniz önemlidir.
Gezinme bileşeni; hedefler arasında gezinmeyi yönetme, bağımsız değişkenler geçirme ve FragmentManager
ile çalışma işlemlerinin tamamını gerçekleştirir.
Bu özellikler zaten titizlikle test edildiğinden uygulamanızda tekrar test etmenize gerek yoktur. Ancak test etmeniz önemli olan, parçalarınızdaki uygulamaya özel kod ile bunların NavController
arasındaki etkileşimlerdir.
Bu kılavuzda, sık karşılaşılan birkaç gezinme senaryosu ve bunların nasıl test edileceği adım adım açıklanmaktadır.
Parçada gezinmeyi test et
Gezinme 2.3 ve sonraki sürümler, NavController
ile parça etkileşimlerini ayrı ayrı test etmek amacıyla, geçerli hedefi ayarlamak ve NavController.navigate()
işlemlerinden sonra arka yığını doğrulamak için API'ler sağlayan bir TestNavHostController
sunar.
Uygulama modülünüzün build.gradle
dosyasına aşağıdaki bağımlılığı ekleyerek Gezinme Testi yapısını projenize ekleyebilirsiniz:
Groovy
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") }
Bir bilgi yarışması oluşturduğunuzu varsayalım. Oyun bir title_screen ile başlar ve kullanıcı oynat düğmesini tıkladığında in_game ekranına gider.
title_screen temsil eden parça aşağıdaki gibi 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); }); } }
Kullanıcı Oynat'ı tıkladığında uygulamanın kullanıcıyı in_game ekranına doğru şekilde yönlendirip yönlendirmediğini test etmek amacıyla testinizin, bu parçanın NavController
öğesini R.id.in_game
ekranına doğru şekilde taşıdığını doğrulaması gerekir.
FragmentScenario
, Espresso ve TestNavHostController
kombinasyonunu kullanarak bu senaryoyu test etmek için gerekli koşulları aşağıdaki örnekte gösterildiği gibi yeniden oluşturabilirsiniz:
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, TestNavHostController
öğesinin bir örneğini oluşturur ve bunu parçaya atar. Ardından, kullanıcı arayüzünü yönlendirmek için Espresso'yu kullanır ve uygun gezinme işleminin yapıldığını doğrular.
Gerçek bir NavController
işleminde olduğu gibi, TestNavHostController
öğesini başlatmak için setGraph
yöntemini çağırmanız gerekir. Bu örnekte test edilen parça, grafiğimizin başlangıç
hedefiydi. TestNavHostController
, testiniz başlamadan önce NavController
öğesinin doğru durumda olması için geçerli hedefi (ve isteğe bağlı olarak söz konusu hedefe ilişkin bağımsız değişkenleri) ayarlamanıza olanak tanıyan bir setCurrentDestination
yöntemi sunar.
NavHostFragment
tarafından kullanılacak NavHostController
örneğinden farklı olarak TestNavHostController
, navigate()
çağırdığınızda temel navigate()
davranışını (FragmentNavigator
'ın yaptığı FragmentTransaction
gibi) tetiklemez ve yalnızca TestNavHostController
durumunu günceller.
NavigationUI'yi Fragment senaryosuyla test etme
Önceki örnekte, titleScenario.onFragment()
için sağlanan geri çağırma, parça yaşam döngüsü boyunca RESUMED
durumuna taşındıktan sonra çağrılır. Bu zamana kadar parçanın görünümü önceden oluşturulmuş ve eklenmiştir. Dolayısıyla, yaşam döngüsünde düzgün bir şekilde test edilemeyecek kadar geç olabilir. Örneğin, parçanızdaki görünümlerle NavigationUI
kullanırken (örneğin, parçanız tarafından kontrol edilen bir Toolbar
ile), parça RESUMED
durumuna ulaşmadan önce NavController
ile kurulum yöntemlerini çağırabilirsiniz. Bu nedenle, TestNavHostController
cihazınızı yaşam döngüsünün erken aşamalarında ayarlamak için bir yönteme ihtiyacınız vardır.
Kendi Toolbar
değerine sahip 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
değerine ihtiyacımız vardır.
Önceki onFragment()
yaklaşımını kullanmak, TestNavHostController
çözümümüzün yaşam döngüsünde çok geç olmasına neden olarak findNavController()
çağrısının başarısız olmasına neden olur.
FragmentScenario
, yaşam döngüsü olayları için geri çağırmaları kaydetmek üzere kullanılabilecek bir FragmentFactory
arayüzü sunar. Bu, aşağıdaki örnekte gösterildiği gibi onCreateView()
ile hemen sonra gelen bir geri çağırma almak için Fragment.getViewLifecycleOwnerLiveData()
ile birleştirilebilir:
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ılmadan önce kullanılabilir. Bu da parçanın kilitlenmeden NavigationUI
yöntemlerini kullanmasına olanak tanır.
Geri yığın girişleriyle etkileşimleri test etme
TestNavHostController
, arka yığın girişleriyle etkileşim kurarken denetleyiciyi NavHostController
'dan devraldığı API'leri kullanarak kendi test LifecycleOwner
, ViewModelStore
ve OnBackPressedDispatcher
testinize bağlamanıza olanak tanır.
Örneğin, gezinme kapsamlı ViewModel kullanan bir parçayı test ederken TestNavHostController
üzerinde setViewModelStore
çağrısı yapmanız gerekir:
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())
İlgili konular
- Araçlı birim testleri oluşturma - Araçlı test paketinizi nasıl kuracağınızı ve testleri bir Android cihazda nasıl çalıştıracağınızı öğrenin.
- Espresso - Uygulamanızın kullanıcı arayüzünü Espresso ile test edin.
- AndroidX Testi ile JUnit4 kuralları - Daha fazla esneklik sağlamak ve testlerde gereken standart kodu azaltmak için AndroidX Test kitaplıklarıyla JUnit 4 kurallarını kullanın.
- Uygulamanızın parçalarını test edin -
FragmentScenario
ile uygulamanızın parçalarını ayrı olarak nasıl test edeceğinizi öğrenin. - AndroidX Test için proje oluşturma - AndroidX Test'i kullanmak için uygulamanızın proje dosyalarında gereken kitaplıkların nasıl tanımlanacağını öğrenin.