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, и пользовательский интерфейс находится в известном, стабильном состоянии для текущего кадра. - Выполнение в потоке пользовательского интерфейса : Для обеспечения стабильности дерева пользовательского интерфейса вызовите
runWithoutImplicitWaitв потоке пользовательского интерфейса, например, с помощью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 отключен.
- Тестирование макета композиции
- Другие соображения
- Настройка анимации