작성하는 모든 테스트는 테스트 대상과 적절하게 동기화(synchronization)되어야 한다. 예를 들어 onNodeWithText와 같은 finder를 사용할 때, 테스트는 시맨틱 트리를 쿼리하기 전에 앱이 유휴 상태가 될 때까지 기다린다. 동기화가 없으면 테스트에서 표시되기 전에 요소를 찾거나 불필요하게 기다릴 수 있다.
이 단계에서는 앱을 실행할 때 다음과 같은 Overview(개요) 화면을 사용한다.
‘Alerts’ 카드의 반복적인 깜박임 애니메이션에 주목하고, 이 요소에 주의를 기울이자.
OverviewScreenTest라는 다른 테스트 클래스를 만들고, 다음 내용을 추가하자.
class OverviewScreenTest {
@get:Rule
val composeTestRule = createComposeRule()
@Test
fun overviewScreen_alertsDisplayed() {
composeTestRule.setContent {
OverviewBody()
}
composeTestRule
.onNodeWithText("Alerts")
.assertIsDisplayed()
}
}
만약 테스트를 실행하면, 테스트가 끝나지 않는 것을 확인할 수 있다. (30초 후에 타임아웃 된다)
에러 내용은 다음과 같다.
androidx.compose.ui.test.junit4.android.ComposeNotIdleException: Idling resource timed out: possibly due to compose being busy.
IdlingResourceRegistry has the following idling resources registered:
- [busy] androidx.compose.ui.test.junit4.android.ComposeIdlingResource@d075f91
이것은 기본적으로 컴포즈가 지속적으로 무언가 하기 때문에(busy), 앱을 테스트와 동기화]할 방법이 없다는 것을 알려준다.
이미 짐작했을 것이다. 문제는 무한으로 깜박이는 애니메이션이다. 앱이 유휴(idle) 상태가 아니므로 테스트를 계속할 수 없다.
무한 애니메이션의 구현을 살펴보자.
app/src/main/java/com/example/compose/rally/ui/overview/OverviewScreen.kt
var currentTargetElevation by remember { mutableStateOf(1.dp) }
LaunchedEffect(Unit) {
// Start the animation
currentTargetElevation = 8.dp
}
val animatedElevation = animateDpAsState(
targetValue = currentTargetElevation,
animationSpec = tween(durationMillis = 500),
finishedListener = {
currentTargetElevation = if (currentTargetElevation > 4.dp) {
1.dp
} else {
8.dp
}
}
)
Card(elevation = animatedElevation.value) { ... }
이 코드는 기본적으로 애니메이션이 완료되기를 기다리고(finishedListener) 다시 실행한다.
이 테스트를 수정하는 한 가지 방법은 개발자 옵션에서 애니메이션을 비활성화하는 것이다. 이는 View 시스템에서 이를 처리하는 널리 허용되는 방법 중 하나다.
컴포즈에서 애니메이션 API는 테스트 가능성을 염두에 두고 설계되었으므로, 올바른 API를 사용하여 문제를 해결할 수 있다. animateDpAsState 애니메이션을 다시 시작하는 대신, 무한 애니메이션을 사용할 수 있다.
무한 애니메이션은 컴포즈가테스트가 이해하는 특별한 경우이므로, 테스트를 바쁘게(busy) 유지하지 않는다.
OverviewScreen.kt에서 AlertCard 컴포저블 함수의 코드를 적절한 API로 바꿔보자.
val infiniteElevationAnimation = rememberInfiniteTransition()
val animatedElevation: Dp by infiniteElevationAnimation.animateValue(
initialValue = 1.dp,
targetValue = 8.dp,
typeConverter = Dp.VectorConverter,
animationSpec = infiniteRepeatable(
animation = tween(500),
repeatMode = RepeatMode.Reverse
)
)
Card(elevation = animatedElevation) {
테스트를 실행하면 이제 통과하는 것을 확인할 수 있다.
축하! 이 단계에서는 동기화와 애니메이션이 테스트에 미치는 영향에 대해 배웠다.
0개의 댓글