이 단계에서는 액션(Action)을 사용하여 RallyTopAppBar의 다른 탭을 클릭하면 선택 항목이 변경되는지 확인한다. Action에 대한 부분은 Testing Cheat Sheet를 참조하자.
힌트:
- 테스트 범위에는 RallyApp이 소유한 상태(State)가 포함되어야 한다.
- 행동(behavior)이 아니라 상태(state)를 확인(verify)하자. 호출된 객체와 방법에 의존하는 대신 UI 상태에 대한 주장(assertion)을 사용한다.
이 연습문제에는 제공된 해답이 없다.
정답이 없지만 열심히 연습문제를 풀어보겠습니다. 태클 환영합니다.
주어진 힌트가 사실은 제약사항인 것 같다.
RallyApp이 가지고 있는 상태를 반드시 포함해서 테스트 범위를 잡아야 하고, 상단 바의 탭을 클릭해서 해당 상태가 변경되었는지도 확인해야 하니 우선 RallyApp코드를 살펴본다.
@Composable fun RallyApp() { RallyTheme { val allScreens = RallyScreen.values().toList() var currentScreen by rememberSaveable { mutableStateOf(RallyScreen.Overview) } Scaffold( topBar = { RallyTopAppBar( allScreens = allScreens, onTabSelected = { screen -> currentScreen = screen }, currentScreen = currentScreen ) } ) { innerPadding -> Box(Modifier.padding(innerPadding)) { currentScreen.content(onScreenChange = { screen -> currentScreen = screen }) } } } }
코드를 살펴보면 첫번째 힌트에 나온 상태가 currentScreen임을 알 수 있다. currentScreen에 따라 RallyTopAppBar가 영향을 받고 있고, 또 RallyTopAppBar의 탭을 클릭함에 따라 상태가 변경되는 것을 알 수 있다.
RallyApp은 상태를 갖고 있기 때문에 stateful하다. stateful한 컴포저블 함수는 테스트 하기 어렵다. 테스트에서 해당 상태에 액세스 하기 어렵기 때문이다. 상태를 끌어올려(state hoisting) 테스트 가능하도록 변경해보자.
@Composable fun RallyApp(currentScreen:RallyScreen, onTabSelected: (RallyScreen) -> Unit) { RallyTheme { val allScreens = RallyScreen.values().toList() Scaffold( topBar = { RallyTopAppBar( allScreens = allScreens, onTabSelected = onTabSelected, currentScreen = currentScreen ) } ) { innerPadding -> Box(Modifier.padding(innerPadding)) { currentScreen.content(onScreenChange = onTabSelected) } } } }
Note: state hoisting은 상태를 끌어올리기 위한 일반적 패턴은 컴포저블 함수 내에 선언된 상태 변수를 해당 컴포저블 함수의 두 개의 매개변수로 바꾸는 것이다.
1. value: T : 표시할 현재 값. 위의 코드에서 currentScreen에 해당
2. onValuChange: (T) -> Unit : T가 제안된 새값인 경우 값을 변경하도록 요청하는 이벤트. 위의 코드에서 onTabSelected에 해당
RallyApp을 변경하였으므로 RallyActivity의 코드도 다음과 같이 변경하자.
class RallyActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContent { var currentScreen:RallyScreen by rememberSaveable { mutableStateOf(RallyScreen.Overview) } RallyApp(currentScreen){ screen-> currentScreen = screen } } } }
이제 테스트 코드를 작성하자.
... @get:Rule val composeTestRule = createComposeRule() @Test fun rallyTopAppBarTest_clickTabs(){ var currentScreen:RallyScreen = RallyScreen.Overview // 현재 상태 composeTestRule.setContent { 컴포즈 테스트 룰에 RallyApp 설정하기 RallyApp(currentScreen){ screen-> currentScreen = screen } } // 모든 탭을 순회하면서 클릭 하고 현재 상태를 확인한다. RallyScreen.values().forEach { screen-> composeTestRule .onNodeWithContentDescription(screen.name) .performClick() assert(currentScreen == screen) } }
onNoWithContentDescription 파인더(finder)를 통해 특정 탭을 찾고, performClick()을 호출하여 해당 탭을 클릭한다. 클릭한 후 currentScreen이 올바르게 변경되었는지 확인한다.
0개의 댓글