Библиотеки и инструменты для тестирования экранов разных размеров

Android предоставляет множество инструментов и API, которые помогут вам создавать тесты для разных размеров экранов и окон.

Переопределение конфигурации устройства

Компонуемый объект DeviceConfigurationOverride позволяет переопределять атрибуты конфигурации для тестирования различных размеров экранов и окон в макетах Compose. Переопределение ForcedSize позволяет разместить любой макет в доступном пространстве, что позволяет запускать любые тесты пользовательского интерфейса на экранах любого размера. Например, вы можете использовать небольшой форм-фактор телефона для всех своих тестов пользовательского интерфейса, включая тесты для больших телефонов, складных устройств и планшетов.

   DeviceConfigurationOverride(
        DeviceConfigurationOverride.ForcedSize(DpSize(1280.dp, 800.dp))
    ) {
        MyScreen() // Will be rendered in the space for 1280dp by 800dp without clipping.
    }
Рисунок 1. Использование DeviceConfigurationOverride для размещения макета планшета на устройстве меньшего форм-фактора, как в \*Теперь в Android*.

Кроме того, вы можете использовать этот компонуемый элемент для установки масштаба шрифта, тем и других свойств, которые вы, возможно, захотите протестировать на окнах разных размеров.

Робоэлектрик

Используйте Robolectric для локального запуска Compose или UI-тестов на основе представлений на JVM — устройства или эмуляторы не требуются. Robolectric можно настроить на использование определённых размеров экрана, а также на другие полезные функции.

В следующем примере из Now in Android Robolectric настроен на эмуляцию экрана размером 1000x1000 точек на дюйм с разрешением 480 точек на дюйм:

@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 Device .

Устройства, управляемые Gradle

Плагин Gradle для Android Gradle «Устройства, управляемые 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-samples .

Тестовая лаборатория 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 (см. следующий раздел) также может фильтровать тесты, используя свойства устройства.

Устройство для приготовления эспрессо

Используйте 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 8.3 или выше
  • Эмулятор Android 33.1.10 или выше
  • Виртуальное устройство Android, работающее под управлением API уровня 24 или выше

Тесты фильтров

Устройство Espresso может считывать свойства подключенных устройств, что позволяет фильтровать тесты по аннотациям . Если аннотированные требования не выполняются, тесты пропускаются.

Требуется аннотация 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 .

Дополнительные ресурсы

Документация

Образцы

Codelabs

,

Android предоставляет множество инструментов и API, которые помогут вам создавать тесты для разных размеров экранов и окон.

Переопределение конфигурации устройства

Компонуемый объект DeviceConfigurationOverride позволяет переопределять атрибуты конфигурации для тестирования различных размеров экранов и окон в макетах Compose. Переопределение ForcedSize позволяет разместить любой макет в доступном пространстве, что позволяет запускать любые тесты пользовательского интерфейса на экранах любого размера. Например, вы можете использовать небольшой форм-фактор телефона для всех своих тестов пользовательского интерфейса, включая тесты для больших телефонов, складных устройств и планшетов.

   DeviceConfigurationOverride(
        DeviceConfigurationOverride.ForcedSize(DpSize(1280.dp, 800.dp))
    ) {
        MyScreen() // Will be rendered in the space for 1280dp by 800dp without clipping.
    }
Рисунок 1. Использование DeviceConfigurationOverride для размещения макета планшета на устройстве меньшего форм-фактора, как в \*Теперь в Android*.

Кроме того, вы можете использовать этот компонуемый элемент для установки масштаба шрифта, тем и других свойств, которые вы, возможно, захотите протестировать на окнах разных размеров.

Робоэлектрик

Используйте Robolectric для локального запуска Compose или UI-тестов на основе представлений на JVM — устройства или эмуляторы не требуются. Robolectric можно настроить на использование определённых размеров экрана, а также на другие полезные функции.

В следующем примере из Now in Android Robolectric настроен на эмуляцию экрана размером 1000x1000 точек на дюйм с разрешением 480 точек на дюйм:

@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 Device .

Устройства, управляемые Gradle

Плагин Gradle для Android Gradle «Устройства, управляемые 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-samples .

Тестовая лаборатория 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 (см. следующий раздел) также может фильтровать тесты, используя свойства устройства.

Устройство для приготовления эспрессо

Используйте 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 8.3 или выше
  • Эмулятор Android 33.1.10 или выше
  • Виртуальное устройство Android, работающее под управлением API уровня 24 или выше

Тесты фильтров

Устройство Espresso может считывать свойства подключенных устройств, что позволяет фильтровать тесты по аннотациям . Если аннотированные требования не выполняются, тесты пропускаются.

Требуется аннотация 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 .

Дополнительные ресурсы

Документация

Образцы

Codelabs