Android предоставляет множество инструментов и API, которые помогут вам создавать тесты для различных размеров экрана и окон.
DeviceConfigurationOverride
The DeviceConfigurationOverride composable lets you override configuration attributes to test multiple screen and window sizes in Compose layouts. The ForcedSize override fits any layout in the available space, which lets you to run any UI test on any screen size. For example, you can use a small phone form factor to run all your UI tests, including UI tests for big phones, foldables, and tablets.
DeviceConfigurationOverride(
DeviceConfigurationOverride.ForcedSize(DpSize(1280.dp, 800.dp))
) {
MyScreen() // Will be rendered in the space for 1280dp by 800dp without clipping.
}

Кроме того, с помощью этого составного элемента можно задать масштаб шрифта, темы оформления и другие свойства, которые вы, возможно, захотите протестировать на окнах разных размеров.
Робоэлектрик
Используйте Robolectric для запуска тестов пользовательского интерфейса 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
The Gradle-managed devices (GMD) Android Gradle plugin lets you define the specifications of the emulators and real devices where your instrumented tests run. Create specifications for devices with different screen sizes to implement a testing strategy where certain tests must be run on certain screen sizes. By using GMD with Continuous Integration (CI), you can make sure that the appropriate tests run when required, provisioning and launching emulators and simplifying your CI setup.
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
Use Firebase Test Lab (FTL), or a similar device farm service, to run your tests on specific real devices that you might not have access to, such as foldables or tablets of varying sizes. Firebase Test Lab is a paid service with a free tier . FTL also supports running tests on emulators. These services improve the reliability and speed of instrumented testing because they can provision devices and emulators ahead of time.
Для получения информации об использовании 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 (см. следующий раздел) также может фильтровать тесты, используя свойства устройства.
Кофемашина для эспрессо
Use Espresso Device to perform actions on emulators in tests using any type of instrumented tests, including Espresso, Compose, or UI Automator tests. These actions might include setting the screen size or toggling foldable states or postures. For example, you can control a foldable emulator and set it to tabletop mode. Espresso Device also contains JUnit rules and annotations to require certain features:
@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
- Использует тестирование скриншотов для проверки различных размеров экрана.
Кодлабс