Compose предлагает 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. Если запустить его вне потока UI, то в тесте могут возникнуть состояния гонки и чтение устаревших состояний. - Утверждения только для чтения. Блок должен содержать только утверждения только для чтения. Любые действия, изменяющие состояние, должны выполняться за пределами этого блока.
Пример
@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 отключен.
- Тестирование макета Compose
- Другие причины
- Как настроить анимацию