Compose offers ComposeTestRule позволяет писать тесты для анимации детерминированным образом с полным контролем над тестовыми часами. Это позволяет проверять промежуточные значения анимации. Кроме того, тест может выполняться быстрее, чем длится анимация.
ComposeTestRule предоставляет тестовые часы как mainClock. Чтобы управлять часами в тестовом коде, задайте для свойства autoAdvance значение false. После запуска анимации, которую вы хотите протестировать, время можно перевести вперед с помощью кнопки advanceTimeBy.
Обратите внимание, что advanceTimeBy не переводит часы ровно на указанное время. а округляет его до ближайшего значения, которое кратно длительности кадра.
@get:Rule val rule = createComposeRule() @Test fun testAnimationWithClock() { // Pause animations rule.mainClock.autoAdvance = false var enabled by mutableStateOf(false) rule.setContent { val color by animateColorAsState( targetValue = if (enabled) Color.Red else Color.Green, animationSpec = tween(durationMillis = 250) ) Box(Modifier.size(64.dp).background(color)) } // Initiate the animation. enabled = true // Let the animation proceed. rule.mainClock.advanceTimeBy(50L) // Compare the result with the image showing the expected result. // `assertAgainGolden` needs to be implemented in your code. rule.onRoot().captureToImage().assertAgainstGolden() }
Как оптимизировать тестирование анимации
При тестировании высококачественных анимаций часто требуется отключить автопереход и вручную переходить между кадрами, чтобы проверить промежуточные состояния интерфейса. Для таких циклов с покадровой анимацией используйте метод runWithoutImplicitWait, чтобы выполнить утверждения. Стандартные запросы узлов (например, onNodeWithTag или fetchSemanticsNode) запускают неявные синхронизации, которые избыточны, когда вы вручную управляете часами, поэтому их обход значительно ускоряет выполнение тестов.
Рекомендации по использованию
- Управление часами вручную. Используйте этот API, когда для параметра
mainClock.autoAdvanceзадано значениеfalseи интерфейс находится в известном стабильном состоянии для текущего кадра. - Выполнение потока UI. Чтобы обеспечить стабильность дерева UI, вызовите
runWithoutImplicitWaitв потоке UI, например с помощьюrunOnUiThread. Если вы запустите его не в основном потоке, то в тесте могут возникнуть состояния гонки и устаревшие данные. - Утверждения, доступные только для чтения. Блок должен содержать только утверждения, доступные только для чтения. Любые действия, изменяющие состояние, должны выполняться за пределами этого блока.
Пример
@Test fun runWithoutImplicitWaitSample() = runComposeUiTest { setContent { MainScreen() } mainClock.autoAdvance = false // Trigger an animation onNodeWithText("Start Animation").performClick() // Step through the animation frame-by-frame while (hasPendingWork()) { mainClock.advanceTimeByFrame() waitForIdle() runOnUiThread { // Suppress implicit synchronization inside this block to avoid redundant // waits on each node query, making the frame assertions execute much faster. runWithoutImplicitWait { val box1 = onNodeWithTag("Box1").fetchSemanticsNode() val box2 = onNodeWithTag("Box2").fetchSemanticsNode() val box3 = onNodeWithTag("Box3").fetchSemanticsNode() // Assert the exact intermediate state of all three properties for this frame assert(box1.boundsInRoot.right <= box2.boundsInRoot.left) assert(box2.boundsInRoot.right <= box3.boundsInRoot.left) } } } }
Рекомендуем
- Примечание. Текст ссылки показывается, когда JavaScript отключен.
- Как протестировать макет письма
- Другие причины
- Как настроить анимацию