이 단계에서는 액션(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개의 댓글