Android

안드로이드 - Jetpack Compose Navigation Test Code

팡세영 2023. 12. 30. 10:25

오늘은 Jetpack Navigation의 Test Code 작성하는 법에 대해 알아 보겠습니다!

우선 테스트 코드 작성을 위해 아래 의존성을 추가해 줍니다.

dependencies {
    implementation("androidx.navigation:navigation-compose:$nav_version")
    androidTestImplementation("androidx.compose.ui:ui-test-junit4:$junit_version")
}

테스트 코드 작성

시작하기 앞서 해당 테스트는 안드로이드 기기에서 실행되어야 하므로 /app/src/androidTest 폴더 안에 위치해 있어야 합니다.

우선 이해하기 쉽게 아래 두 화면과 NavHost가 정의되어 있다고 가정하겠습니다.

sealed class Screen(val route: String) {
    object ScreenA : Screen("ScreenA")
    object ScreenB : Screen("ScreenB")
}

@Composable
fun TestNavHost(navController: NavHostController) {
    NavHost(navController, startDestination = Screen.ScreenA.route) {
        composable(Screen.ScreenA.route) { /* ... */ }
        composable(Screen.ScreenB.route) { /* ... */ }
    }
}

간단하게 A 화면에서 B 화면으로 이동하는 테스트 코드는 아래와 같이 작성하실 수 있습니다.

class NavigationTest {

    @get:Rule
    val composeTestRule = createComposeRule()

    @Test
    fun screenA_navigateButtonClicked_navigateToscreenB() {
        composeTestRule.setContent {
            val navController = rememberNavController()
            TestNavHost(navController = navController)
        }

        // ScreenA에서 ScreenB로 네비게이션하는 버튼을 찾아 클릭
        composeTestRule.onNodeWithContentDescription("Navigate to ScreenB").performClick()

        // ScreenB에 도달했는지 확인
        composeTestRule.onNodeWithContentDescription("ScreenB Content").assertIsDisplayed()
    }
}

createComposeRule()을 사용하여 테스트 환경을 초기화 하고,

onNodeWithContentDescription()을 통해 UI 요소를 찾아 조작할 수 있습니다.

위에서 사용된 메소드의 역할은 아래와 같습니다.

createComposeRule()

  • 해당 메소드는 Compose UI 테스트를 위한 테스트 환경을 설정하고
    Compose의 컴포넌트를 테스트하기 전에 필요한 초기화와 정리 작업을 제공합니다.
  • @get:Rule 어노테이션과 함께 사용되며 setContent 메소드를 사용하여 Compose 컴포넌트를 로드 합니다.

performClick()

  • 해당 메소드는 Compose에서 UI 요소에 클릭 이벤트를 시뮬레이션하는 데 사용됩니다.
  • 테스트하고자 하는 Compose 요소를 onNodeWithContentDescription, onNodeWithText 등의 함수로 찾은 후,
    performClick 함수를 적용해 해당 요소에 가상의 클릭 이벤트를 발생시킵니다.

assertIsDisplayed()

  • 해당 메소드는 특정 UI 요소가 화면에 표시되는지 여부를 검증하는 데 사용됩니다.
  • 테스트하고자 하는 Compose 요소를 onNodeWithContentDescription, onNodeWithText 등의 함수로 찾은 후
    assertIsDisplayed를 호출하여 해당 요소가 사용자에게 보이는지 확인합니다.
    이 함수는 요소가 화면에 표시되지 않으면 테스트 실패를 반환합니다.

다른 방법으로 navigation 테스트

위의 방법 말고도 navController.currentBackStackEntry?.destination?.route 방식으로 테스트 하는 방법 예시입니다.
currentBackStackEntry를 사용하여 현재 문자열 경로를 예상 경로와 비교함으로써 검증하실 수 있습니다.

class NavigationTest {

    @get:Rule
    val composeTestRule = createComposeRule()

    @Test
    fun aScreen_navigateButtonClicked_navigateToBScreen() {
        composeTestRule.setContent {
            val navController = rememberNavController()
            TestNavHost(navController = navController)
        }

        composeTestRule.onNodeWithContentDescription("Navigate to ScreenB")
        .performClick()

    val route = navController.currentBackStackEntry?.destination?.route
    assertEquals(route, "ScreenB")

    }
}

navController.navigate 직접 호출하는 방법

해당 방법은 UI 스레드에서 navController.navigate()를 호출해야 하므로,
메인 스레드 디스패처와 함께 코루틴을 사용하여 이를 수행할 수 있습니다.

class NavigationTest {

    @get:Rule
    val composeTestRule = createComposeRule()

    @Test
    fun aScreen_navigateButtonClicked_navigateToBScreen() {
         runBlocking {
           withContext(Dispatchers.Main) {
               navController.navigate(Screen.ScreenB.route)
           }
       }

    composeTestRule
        .onNodeWithContentDescription("ScreenB Content")
        .assertIsDisplayed()

    }
}

새로운 상태에 대한 Assertion을 만들기 전에 호출이 발생해야 하므로, runBlocking 호출로 래핑 해주시면 됩니다.