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.
Navigation bileşeni, hedefler arasında gezinmeyi yönetme, bağımsız değişkenleri iletme ve FragmentManager ile çalışma gibi tüm işlemleri gerçekleştirir. Bu özellikler zaten titizlikle test edildiğinden uygulamanızda tekrar test etmenize gerek yoktur. Ancak parçalarınızdaki uygulamaya özel kod ile NavController arasındaki etkileşimleri test etmeniz önemlidir.
Yalıtılmış ortamda test etme
Parça etkileşimlerini NavController ile izole bir şekilde test etmek için,
Navigation 2.3 ve sonraki sürümlerde, geçerli hedefi ayarlamak ve NavController.navigate() işlemlerinden sonra geri yığını doğrulamak için API'ler sağlayan bir TestNavHostController bulunur.
Aşağıdaki bağımlılığı uygulama modülünüzün build.gradle dosyasına ekleyerek Navigation Testing yapısını projenize ekleyebilirsiniz:
Groovy
dependencies { def nav_version = "2.9.8" androidTestImplementation "androidx.navigation:navigation-testing:$nav_version" }
Kotlin
dependencies { val nav_version = "2.9.8" androidTestImplementation("androidx.navigation:navigation-testing:$nav_version") }
Bilgi yarışması oyunu oynatın. Oyun, title_screen ile başlar ve kullanıcı oynat'ı tıkladığında in_game ekranına gider.
title_screen öğesini temsil eden parça şöyle 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ı Oyna'yı tıkladığında uygulamanın kullanıcıyı in_game ekranına doğru şekilde yönlendirdiğini test etmek için testinizin bu parçanın NavController öğesini R.id.in_game ekranına doğru şekilde taşıdığını doğrulaması gerekir.
Aşağıdaki örnekte gösterildiği gibi FragmentScenario, Espresso ve TestNavHostController kombinasyonunu kullanarak bu senaryoyu test etmek için gereken koşulları 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 using 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
fun 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 using 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 örnekte TestNavHostController öğesinin bir örneği oluşturulur ve bu örnek, parçaya atanır. 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 gibi, TestNavHostController öğesini başlatmak için setGraph öğesini çağırmanız gerekir. Bu örnekte, test edilen parça grafiğimizin başlangıç hedefiydi. TestNavHostController, mevcut hedefi (ve isteğe bağlı olarak bu hedefe yönelik bağımsız değişkenleri) ayarlamanıza olanak tanıyan bir setCurrentDestination yöntemi sunar. Böylece, testiniz başlamadan önce NavController doğru durumda olur.
NavHostFragment tarafından kullanılan bir NavHostController örneğinin aksine, TestNavHostController, navigate()'ı çağırdığınızda temel navigate() davranışını (ör. FragmentNavigator'ün yaptığı gibi FragmentTransaction) tetiklemez. Yalnızca TestNavHostController'ın durumunu günceller.
NavigationUI'yı FragmentScenario ile test etme
Önceki örnekte, titleScenario.onFragment() öğesine sağlanan geri çağırma, parça yaşam döngüsünde RESUMED durumuna geçtikten sonra çağrılır. Bu noktada, parçanın görünümü zaten oluşturulmuş ve eklenmiş olduğundan, yaşam döngüsünde düzgün bir şekilde test etmek için çok geç olabilir. Örneğin, parçanızdaki görünümlerle NavigationUI kullanırken (ör. 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, yaşam döngüsünde TestNavHostController değerini daha erken ayarlamanız gerekir.
Kendi Toolbar öğesine 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 NavController oluşturulmuş olmalıdır. Önceki onFragment() yaklaşımını kullanmak, TestNavHostController öğemizi yaşam döngüsünde çok geç ayarlayarak findNavController() çağrısının başarısız olmasına neden olur.
FragmentScenario, yaşam döngüsü etkinlikleri için geri çağırmaları kaydetmek üzere kullanılabilecek bir FragmentFactory
arayüzü sunar. Bu, aşağıdaki örnekte gösterildiği gibi Fragment.getViewLifecycleOwnerLiveData() ile birleştirilerek onCreateView()'den hemen sonra geri arama alınabilir:
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. Böylece, parça NavigationUI yöntemlerini kilitlenmeden kullanabilir.
Geri yığın girişleriyle etkileşimleri test etme
eski yığın girişleriyle etkileşimde bulunurken TestNavHostController, NavHostController'dan devraldığı API'leri kullanarak denetleyiciyi kendi test LifecycleOwner, ViewModelStore ve OnBackPressedDispatcher cihazlarınıza bağlamanıza olanak tanır.
Örneğin, navigation scoped ViewModel kullanan bir parçayı test ederken TestNavHostController üzerinde setViewModelStore işlevini çağırmanı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çlarla birim testleri oluşturma: Araçlı test paketinizi nasıl ayarlayacağınızı ve Android destekli bir cihazda testleri nasıl çalıştıracağınızı öğrenin.
- Espresso: Uygulamanızın kullanıcı arayüzünü Espresso ile test edin.
- AndroidX Test ile JUnit4 kuralları: Daha fazla esneklik sağlamak ve testlerde gereken ortak metin kodunu azaltmak için AndroidX Test kitaplıklarıyla JUnit 4 kurallarını kullanın.
- Uygulamanızın parçalarını test etme:
FragmentScenarioile uygulamanızın parçalarını izole bir şekilde nasıl test edeceğinizi öğrenin. - AndroidX Test için proje oluşturma: AndroidX Test'i kullanmak üzere uygulamanızın proje dosyalarında gerekli kitaplıkları nasıl bildireceğinizi öğrenin.