Вы можете тестировать приложения, созданные с помощью Compose, используя проверенные подходы и шаблоны.
Тестирование в изолированной среде
ComposeTestRule позволяет запустить объект activity, отображающий любой компонуемый элемент: все приложение, отдельный экран или небольшой элемент. Также рекомендуется убедиться, что ваши функции, созданные с помощью Compose, правильно инкапсулированы и работают независимо друг от друга. Это упростит тестирование интерфейса.
Это не означает, что вам следует создавать только модульные тесты интерфейса. Также очень важно проводить тестирование больших частей интерфейса.
Получите доступ к заданию и ресурсам после того, как добавите собственный контент.
Часто вам нужно задать тестируемый контент с помощью composeTestRule.setContent, а также получить доступ к ресурсам действий, например чтобы убедиться, что отображаемый текст соответствует строковому ресурсу. Однако вы не можете вызвать setContent для правила, созданного с помощью createAndroidComposeRule(), если оно уже вызывается в действии.
Для этого часто создается AndroidComposeTestRule с помощью пустого действия, например ComponentActivity.
class MyComposeTest {
@get:Rule
val composeTestRule = createAndroidComposeRule<ComponentActivity>()
@Test
fun myTest() {
// Start the app
composeTestRule.setContent {
MyAppTheme {
MainScreen(uiState = exampleUiState, /*...*/)
}
}
val continueLabel = composeTestRule.activity.getString(R.string.next)
composeTestRule.onNodeWithText(continueLabel).performClick()
}
}
Обратите внимание, что ComponentActivity необходимо добавить в файл AndroidManifest.xml вашего приложения. Чтобы включить эту функцию, добавьте в модуль следующую зависимость:
debugImplementation("androidx.compose.ui:ui-test-manifest:$compose_version")
Свойства настраиваемой семантики
Вы можете создать специальные свойства semantics, чтобы передавать информацию в тесты.
Для этого определите новый SemanticsPropertyKey и сделайте его доступным с помощью SemanticsPropertyReceiver.
// Creates a semantics property of type Long.
val PickedDateKey = SemanticsPropertyKey<Long>("PickedDate")
var SemanticsPropertyReceiver.pickedDate by PickedDateKey
Теперь используйте это свойство в модификаторе semantics:
val datePickerValue by remember { mutableStateOf(0L) }
MyCustomDatePicker(
modifier = Modifier.semantics { pickedDate = datePickerValue }
)
В тестах используйте SemanticsMatcher.expectValue, чтобы подтвердить значение свойства:
composeTestRule
.onNode(SemanticsMatcher.expectValue(PickedDateKey, 1445378400)) // 2015-10-21
.assertExists()
Как проверить восстановление состояния
Убедитесь, что состояние элементов Compose правильно восстанавливается при воссоздании процесса или действия. Выполняйте такие проверки, не полагаясь на воссоздание активности с помощью класса StateRestorationTester.
Этот класс позволяет имитировать воссоздание компонента. Особенно полезно проверять с его помощью реализацию rememberSaveable.
class MyStateRestorationTests {
@get:Rule
val composeTestRule = createComposeRule()
@Test
fun onRecreation_stateIsRestored() {
val restorationTester = StateRestorationTester(composeTestRule)
restorationTester.setContent { MainScreen() }
// TODO: Run actions that modify the state
// Trigger a recreation
restorationTester.emulateSavedInstanceStateRestore()
// TODO: Verify that state has been correctly restored.
}
}
Тестирование различных конфигураций устройств
Приложения для Android должны адаптироваться к различным условиям: размеру окна, региональным настройкам, размеру шрифта, светлой и темной темам и т. д. Большинство этих условий основаны на значениях на уровне устройства, которые контролируются пользователем и доступны в текущем экземпляре Configuration. Тестировать разные конфигурации непосредственно в тесте сложно, поскольку тест должен настраивать свойства на уровне устройства.
DeviceConfigurationOverride – это API, предназначенный только для тестирования. Он позволяет имитировать различные конфигурации устройств с учетом региональных особенностей для тестируемого контента @Composable.
У сопутствующего объекта DeviceConfigurationOverride есть следующие функции расширения, которые переопределяют свойства конфигурации на уровне устройства:
DeviceConfigurationOverride.DarkMode()– переключает тему на тёмную или светлую, игнорируя системные настройки.DeviceConfigurationOverride.FontScale()– переопределяет системный масштаб шрифта.DeviceConfigurationOverride.FontWeightAdjustment()– переопределяет настройку насыщенности системного шрифта.DeviceConfigurationOverride.ForcedSize()– принудительное указание определенного размера независимо от размера устройства.DeviceConfigurationOverride.LayoutDirection(): переопределяет направление макета (слева направо или справа налево).DeviceConfigurationOverride.Locales()– переопределяет значение параметра locale.DeviceConfigurationOverride.RoundScreen(): переопределяет, является ли экран круглым.
Чтобы применить определенное переопределение, оберните тестируемый контент в вызов функции верхнего уровня DeviceConfigurationOverride(), передав переопределение в качестве параметра.
Например, следующий код применяет переопределение DeviceConfigurationOverride.ForcedSize(), чтобы изменить плотность локально, заставляя компонуемый элемент MyScreen отрисовываться в большом горизонтальном окне, даже если устройство, на котором выполняется тест, напрямую не поддерживает такой размер окна:
composeTestRule.setContent { DeviceConfigurationOverride( DeviceConfigurationOverride.ForcedSize(DpSize(1280.dp, 800.dp)) ) { MyScreen() // Will be rendered in the space for 1280dp by 800dp without clipping. } }
Чтобы применить несколько переопределений одновременно, используйте DeviceConfigurationOverride.then():
composeTestRule.setContent { DeviceConfigurationOverride( DeviceConfigurationOverride.FontScale(1.5f) then DeviceConfigurationOverride.FontWeightAdjustment(200) ) { Text(text = "text with increased scale and weight") } }
Как настроить обработку ошибок в тестах
При отладке тестов интерфейса часто требуется проверить состояние экрана и иерархию композиции, когда утверждение не выполняется.
Compose предоставляет встроенный конвейер обработки ошибок, который упрощает диагностику. Вы можете автоматически создавать скриншоты и иерархии интерфейса при сбоях или регистрировать собственные обработчики для пересылки данных о сбоях во внешние инструменты.
Как настроить обработку ошибок
Чтобы настроить обработку ошибок, передайте объект TestFailurePolicy в ComposeUiTestConfig.
Как собирать встроенные артефакты сбоев
Чтобы при сбое теста сохранялись скриншоты и иерархии интерфейса, задайте свойства screenshotCaptureMode и uiHierarchyCaptureMode в файле TestFailurePolicy.
class MyComposeTest { private val customConfig = ComposeUiTestConfig( failurePolicy = TestFailurePolicy( screenshotCaptureMode = TestFailurePolicy.CaptureMode.Enabled, uiHierarchyCaptureMode = TestFailurePolicy.CaptureMode.Enabled ) ) @Test fun myFirstTest() = runComposeUiTest(config = customConfig) { // ... } }
Как использовать собственный обработчик ошибок
Чтобы задать собственное поведение при сбое теста, добавьте TestFailureHandler в список failureHandlers. Каждый обработчик получает объект FailureContext, который содержит исходную ошибку и список артефактов, созданных предыдущими обработчиками конвейера.
class MyComposeTestWithHandler { private val customConfig = ComposeUiTestConfig( failurePolicy = TestFailurePolicy( screenshotCaptureMode = TestFailurePolicy.CaptureMode.Enabled, failureHandlers = listOf( TestFailureHandler { context -> val screenshot = context.artifacts.firstOrNull { it.type == FailureArtifact.Type.Screenshot } // ... } ) ) ) @get:Rule val rule = createComposeRule(config = customConfig) // ... }
Как настроить захват для всего набора тестов
Вы можете включить создание артефактов сбоя для всего набора тестов, передав аргументы средства запуска инструментов в файл build.gradle модуля. Вам не придется обновлять отдельные тестовые файлы.
// build.gradle.kts
android {
defaultConfig {
// ...
testInstrumentationRunnerArguments["androidx.compose.ui.test.failure.isUiHierarchyCaptureEnabled"] = "true"
testInstrumentationRunnerArguments["androidx.compose.ui.test.failure.isScreenshotCaptureEnabled"] = "true"
}
}
Приоритет конфигурации
Фреймворк оценивает как локальные аргументы TestFailurePolicy, так и аргументы на уровне набора:
- Если вы явно зададите режим
CaptureMode.Enabled/Disabledв тестовом файлеfailurePolicy, это переопределит аргумент исполнителя. - Если для режима задано значение
CaptureMode.Unspecified(или дляfailurePolicyоставлено значениеnull), фреймворк возвращается к аргументу исполнителя.
Дополнительные ресурсы
- Тестирование приложений для Android. На этой целевой странице собрана основная информация о тестировании, а также описаны различные методы.
- Основы тестирования. Узнайте больше об основных принципах тестирования приложений для Android.
- Локальные тесты. Некоторые тесты можно запустить локально на своей рабочей станции.
- Инструментальные тесты. Рекомендуем также проводить инструментальные тесты. То есть тесты, которые выполняются непосредственно на устройстве.
- Непрерывная интеграция: Непрерывная интеграция позволяет встроить тесты в конвейер развертывания.
- Проверьте, как сайт выглядит на экранах разных размеров. Пользователи могут просматривать ваш сайт на самых разных устройствах, поэтому важно убедиться, что он корректно отображается на экранах разных размеров.
- Espresso. Хотя Espresso предназначен для тестирования интерфейсов на основе представлений, знания об этом фреймворке могут быть полезны при тестировании некоторых аспектов Compose.