Android предоставляет множество инструментов и API, которые помогут вам создавать тесты для различных размеров экрана и окон.
DeviceConfigurationOverride
Компонент DeviceConfigurationOverride позволяет переопределять атрибуты конфигурации для тестирования различных размеров экрана и окна в макетах Compose. Переопределение ForcedSize позволяет адаптировать любой макет к доступному пространству, что дает возможность запускать любые тесты пользовательского интерфейса на экранах любого размера. Например, вы можете использовать небольшой форм-фактор телефона для запуска всех ваших тестов пользовательского интерфейса, включая тесты для больших телефонов, складных устройств и планшетов.
DeviceConfigurationOverride(
DeviceConfigurationOverride.ForcedSize(DpSize(1280.dp, 800.dp))
) {
MyScreen() // Will be rendered in the space for 1280dp by 800dp without clipping.
}

Кроме того, с помощью этого составного элемента можно задать масштаб шрифта, темы оформления и другие свойства, которые вы, возможно, захотите протестировать на окнах разных размеров.
Робоэлектрик
Используйте Robolectric для запуска UI-тестов Compose или на основе представлений локально на JVM — без необходимости использования устройств или эмуляторов. Вы можете настроить Robolectric для использования определенных размеров экрана, а также других полезных свойств.
В следующем примере из Now in Android Robolectric настроен на эмуляцию экрана размером 1000x1000 пикселей с разрешением 480 dpi:
@RunWith(RobolectricTestRunner::class)
// Configure Robolectric to use a very large screen size that can fit all of the test sizes.
// This allows enough room to render the content under test without clipping or scaling.
@Config(qualifiers = "w1000dp-h1000dp-480dpi")
class NiaAppScreenSizesScreenshotTests { ... }
Вы также можете задать квалификаторы из тела теста, как это сделано в этом фрагменте из примера Now in Android :
val (width, height, dpi) = ...
// Set qualifiers from specs.
RuntimeEnvironment.setQualifiers("w${width}dp-h${height}dp-${dpi}dpi")
Обратите внимание, что RuntimeEnvironment.setQualifiers() обновляет системные и прикладные ресурсы новой конфигурацией, но не запускает никаких действий в активных процессах или других компонентах.
Более подробную информацию можно найти в документации по настройке устройств Robolectric.
Устройства, управляемые Gradle
Плагин Gradle для Android , управляемый устройствами Gradle (GMD), позволяет определять спецификации эмуляторов и реальных устройств, на которых выполняются инструментальные тесты. Создавайте спецификации для устройств с разными размерами экрана, чтобы реализовать стратегию тестирования, при которой определенные тесты должны выполняться на определенных размерах экрана. Используя GMD с непрерывной интеграцией (CI), вы можете гарантировать, что соответствующие тесты будут запускаться при необходимости, обеспечивая инициализацию и запуск эмуляторов, а также упрощая настройку CI.
android {
testOptions {
managedDevices {
devices {
// Run with ./gradlew nexusOneApi30DebugAndroidTest.
nexusOneApi30(com.android.build.api.dsl.ManagedVirtualDevice) {
device = "Nexus One"
apiLevel = 30
// Use the AOSP ATD image for better emulator performance
systemImageSource = "aosp-atd"
}
// Run with ./gradlew foldApi34DebugAndroidTest.
foldApi34(com.android.build.api.dsl.ManagedVirtualDevice) {
device = "Pixel Fold"
apiLevel = 34
systemImageSource = "aosp-atd"
}
}
}
}
}
Многочисленные примеры использования GMD можно найти в проекте testing-amples .
Тестовая лаборатория Firebase
Используйте Firebase Test Lab (FTL) или аналогичный сервис для запуска тестов на реальных устройствах, к которым у вас может не быть доступа, например, на складных устройствах или планшетах разных размеров. Firebase Test Lab — это платный сервис с бесплатным уровнем . FTL также поддерживает запуск тестов на эмуляторах. Эти сервисы повышают надежность и скорость инструментального тестирования, поскольку позволяют заранее подготовить устройства и эмуляторы.
Для получения информации об использовании FTL с GMD см. раздел «Масштабирование тестов с помощью устройств, управляемых Gradle» .
Фильтрация тестов с помощью средства запуска тестов
Оптимальная стратегия тестирования не должна проверять одно и то же дважды, поэтому большинству ваших UI-тестов не нужно запускать их на нескольких устройствах. Как правило, вы фильтруете свои UI-тесты, запуская все или большинство из них на устройствах определенного форм-фактора телефона и только часть — на устройствах с разными размерами экрана.
Вы можете указать, что определенные тесты должны запускаться только на определенных устройствах, а затем передать аргумент в AndroidJUnitRunner, используя команду, запускающую тесты.
Например, вы можете создавать различные аннотации:
annotation class TestExpandedWidth
annotation class TestCompactWidth
И используйте их в различных тестах:
class MyTestClass {
@Test
@TestExpandedWidth
fun myExample_worksOnTablet() {
...
}
@Test
@TestCompactWidth
fun myExample_worksOnPortraitPhone() {
...
}
}
Затем вы можете использовать свойство android.testInstrumentationRunnerArguments.annotation при запуске тестов, чтобы отфильтровать определенные тесты. Например, если вы используете устройства, управляемые Gradle:
$ ./gradlew pixelTabletApi30DebugAndroidTest -Pandroid.testInstrumentationRunnerArguments.annotation='com.sample.TestExpandedWidth'
Если вы не используете GMD и управляете эмуляторами в CI, сначала убедитесь, что нужный эмулятор или устройство готовы и подключены, а затем передайте параметр одной из команд Gradle для запуска инструментальных тестов:
$ ./gradlew connectedAndroidTest -Pandroid.testInstrumentationRunnerArguments.annotation='com.sample.TestExpandedWidth'
Обратите внимание, что Espresso Device (см. следующий раздел) также может фильтровать тесты, используя свойства устройства.
Кофемашина для эспрессо
Используйте Espresso Device для выполнения действий над эмуляторами в тестах с использованием любых типов инструментированных тестов, включая тесты Espresso, Compose или UI Automator. Эти действия могут включать в себя установку размера экрана или переключение состояний или положений складывающихся элементов. Например, вы можете управлять складывающимся эмулятором и установить его в настольный режим. Espresso Device также содержит правила и аннотации JUnit для требования определенных функций:
@RunWith(AndroidJUnit4::class)
class OnDeviceTest {
@get:Rule(order=1) val activityScenarioRule = activityScenarioRule<MainActivity>()
@get:Rule(order=2) val screenOrientationRule: ScreenOrientationRule =
ScreenOrientationRule(ScreenOrientation.PORTRAIT)
@Test
fun tabletopMode_playerIsDisplayed() {
// Set the device to tabletop mode.
onDevice().setTabletopMode()
onView(withId(R.id.player)).check(matches(isDisplayed()))
}
}
Обратите внимание, что Espresso Device всё ещё находится на стадии альфа-тестирования и имеет следующие требования:
- Android Gradle Plugin 8.3 или выше
- Эмулятор Android версии 33.1.10 или выше.
- Виртуальное устройство Android, работающее под управлением API уровня 24 или выше.
Фильтры тестов
Espresso Device может считывать свойства подключенных устройств, что позволяет фильтровать тесты с помощью аннотаций . Если аннотированные требования не выполняются, тесты пропускаются.
Требуется аннотация DeviceMode
Аннотация RequiresDeviceMode может использоваться несколько раз для указания теста, который будет выполняться только в том случае, если все значения DeviceMode поддерживаются на устройстве.
class OnDeviceTest {
...
@Test
@RequiresDeviceMode(TABLETOP)
@RequiresDeviceMode(BOOK)
fun tabletopMode_playerIdDisplayed() {
// Set the device to tabletop mode.
onDevice().setTabletopMode()
onView(withId(R.id.player)).check(matches(isDisplayed()))
}
}
Требуется аннотация отображения.
Аннотация RequiresDisplay позволяет указать ширину и высоту экрана устройства с помощью классов размеров , которые определяют размерные интервалы в соответствии с официальными классами размеров окна .
class OnDeviceTest {
...
@Test
@RequiresDisplay(EXPANDED, COMPACT)
fun myScreen_expandedWidthCompactHeight() {
...
}
}
Изменение размера дисплеев
Используйте метод setDisplaySize() для изменения размеров экрана во время выполнения. Используйте этот метод совместно с классом DisplaySizeRule , который гарантирует, что любые изменения, внесенные во время тестов, будут отменены перед следующим тестом.
@RunWith(AndroidJUnit4::class)
class ResizeDisplayTest {
@get:Rule(order = 1) val activityScenarioRule = activityScenarioRule<MainActivity>()
// Test rule for restoring device to its starting display size when a test case finishes.
@get:Rule(order = 2) val displaySizeRule: DisplaySizeRule = DisplaySizeRule()
@Test
fun resizeWindow_compact() {
onDevice().setDisplaySize(
widthSizeClass = WidthSizeClass.COMPACT,
heightSizeClass = HeightSizeClass.COMPACT
)
// Verify visual attributes or state restoration.
}
}
При изменении размера дисплея с помощью setDisplaySize() плотность пикселей устройства не изменяется, поэтому, если размер не помещается на целевом устройстве, тест завершается с ошибкой UnsupportedDeviceOperationException . Чтобы предотвратить запуск тестов в этом случае, используйте аннотацию RequiresDisplay для их фильтрации:
@RunWith(AndroidJUnit4::class)
class ResizeDisplayTest {
@get:Rule(order = 1) var activityScenarioRule = activityScenarioRule<MainActivity>()
// Test rule for restoring device to its starting display size when a test case finishes.
@get:Rule(order = 2) var displaySizeRule: DisplaySizeRule = DisplaySizeRule()
/**
* Setting the display size to EXPANDED would fail in small devices, so the [RequiresDisplay]
* annotation prevents this test from being run on devices outside the EXPANDED buckets.
*/
@RequiresDisplay(
widthSizeClass = WidthSizeClassEnum.EXPANDED,
heightSizeClass = HeightSizeClassEnum.EXPANDED
)
@Test
fun resizeWindow_expanded() {
onDevice().setDisplaySize(
widthSizeClass = WidthSizeClass.EXPANDED,
heightSizeClass = HeightSizeClass.EXPANDED
)
// Verify visual attributes or state restoration.
}
}
StateRestorationTester
Класс StateRestorationTester используется для проверки восстановления состояния составных компонентов без повторного создания активностей. Это делает тесты быстрее и надежнее, поскольку повторное создание активностей — сложный процесс с множеством механизмов синхронизации.
@Test
fun compactDevice_selectedEmailEmailRetained_afterConfigChange() {
val stateRestorationTester = StateRestorationTester(composeTestRule)
// Set content through the StateRestorationTester object.
stateRestorationTester.setContent {
MyApp()
}
// Simulate a config change.
stateRestorationTester.emulateSavedInstanceStateRestore()
}
Библиотека для тестирования окон
Библиотека Window Testing содержит утилиты, которые помогут вам писать тесты, основанные на функциях управления окнами или проверяющие их, например, встраивание активности или функции сворачивания. Этот артефакт доступен через репозиторий Maven от Google .
Например, вы можете использовать функцию FoldingFeature() для создания пользовательского FoldingFeature , который можно использовать в предварительном просмотре Compose. В Java используйте функцию createFoldingFeature() .
В режиме предварительного просмотра Compose вы можете реализовать FoldingFeature следующим образом:
@Preview(showBackground = true, widthDp = 480, heightDp = 480)
@Composable private fun FoldablePreview() =
MyApplicationTheme {
ExampleScreen(
displayFeatures = listOf(FoldingFeature(Rect(0, 240, 480, 240)))
)
}
Кроме того, вы можете эмулировать функции отображения в тестах пользовательского интерфейса, используя функцию TestWindowLayoutInfo() . В следующем примере имитируется функция FoldingFeature с вертикальным шарниром HALF_OPENED в центре экрана, а затем проверяется, соответствует ли макет ожидаемому:
Сочинить
import androidx.window.layout.FoldingFeature.Orientation.Companion.VERTICAL
import androidx.window.layout.FoldingFeature.State.Companion.HALF_OPENED
import androidx.window.testing.layout.FoldingFeature
import androidx.window.testing.layout.TestWindowLayoutInfo
import androidx.window.testing.layout.WindowLayoutInfoPublisherRule
@RunWith(AndroidJUnit4::class)
class MediaControlsFoldingFeatureTest {
@get:Rule(order=1)
val composeTestRule = createAndroidComposeRule<ComponentActivity>()
@get:Rule(order=2)
val windowLayoutInfoPublisherRule = WindowLayoutInfoPublisherRule()
@Test
fun foldedWithHinge_foldableUiDisplayed() {
composeTestRule.setContent {
MediaPlayerScreen()
}
val hinge = FoldingFeature(
activity = composeTestRule.activity,
state = HALF_OPENED,
orientation = VERTICAL,
size = 2
)
val expected = TestWindowLayoutInfo(listOf(hinge))
windowLayoutInfoPublisherRule.overrideWindowLayoutInfo(expected)
composeTestRule.waitForIdle()
// Verify that the folding feature is detected and media controls shown.
composeTestRule.onNodeWithTag("MEDIA_CONTROLS").assertExists()
}
}
Мнения
import androidx.window.layout.FoldingFeature.Orientation
import androidx.window.layout.FoldingFeature.State
import androidx.window.testing.layout.FoldingFeature
import androidx.window.testing.layout.TestWindowLayoutInfo
import androidx.window.testing.layout.WindowLayoutInfoPublisherRule
@RunWith(AndroidJUnit4::class)
class MediaControlsFoldingFeatureTest {
@get:Rule(order=1)
val activityRule = ActivityScenarioRule(MediaPlayerActivity::class.java)
@get:Rule(order=2)
val windowLayoutInfoPublisherRule = WindowLayoutInfoPublisherRule()
@Test
fun foldedWithHinge_foldableUiDisplayed() {
activityRule.scenario.onActivity { activity ->
val feature = FoldingFeature(
activity = activity,
state = State.HALF_OPENED,
orientation = Orientation.VERTICAL)
val expected = TestWindowLayoutInfo(listOf(feature))
windowLayoutInfoPublisherRule.overrideWindowLayoutInfo(expected)
}
// Verify that the folding feature is detected and media controls shown.
onView(withId(R.id.media_controls)).check(matches(isDisplayed()))
}
}
Больше примеров вы найдете в проекте WindowManager .
Дополнительные ресурсы
Документация
- Рекомендации по качеству приложений для больших экранов
- Тестирование приложений на Android
- Тестирование макета композиции
Образцы
- Пример WindowManager
- Образцы кофемашин Espresso
- Теперь в Android
- Использует тестирование скриншотов для проверки различных размеров экрана.
Кодлабс