Bibliotheken und Tools zum Testen verschiedener Bildschirmgrößen

Android bietet eine Vielzahl von Tools und APIs, mit denen Sie Tests für verschiedene Bildschirm- und Fenstergrößen erstellen können.

DeviceConfigurationOverride

Mit der zusammensetzbaren Funktion DeviceConfigurationOverride können Sie Konfigurationsattribute überschreiben, um mehrere Bildschirm- und Fenstergrößen in Layouts zu testen. Die Überschreibung ForcedSize passt zu jedem Layout im verfügbaren Platz, sodass Sie einen beliebigen UI-Test für jede Bildschirmgröße ausführen können. Sie können beispielsweise einen kleinen Smartphone-Formfaktor verwenden, um alle UI-Tests auszuführen, einschließlich UI-Tests für große Smartphones, faltbare Smartphones und Tablets.

   DeviceConfigurationOverride(
        DeviceConfigurationOverride.ForcedSize(DpSize(1280.dp, 800.dp))
    ) {
        MyScreen() // Will be rendered in the space for 1280dp by 800dp without clipping.
    }
Abbildung 1. DeviceConfigurationOverride, um ein Tablet-Layout in ein Gerät mit einem kleineren Formfaktor zu integrieren, wie in \*Now in Android*.

Außerdem können Sie diese zusammensetzbare Funktion verwenden, um Schriftskala, Designs und andere Eigenschaften festzulegen, die Sie an verschiedenen Fenstergrößen testen möchten.

Robolektisch

Mit Robolectric können Sie Compose- oder ansichtsbasierte UI-Tests für die JVM lokal ausführen – ohne Geräte oder Emulatoren. Sie können Robolectric so konfigurieren, dass neben anderen nützlichen Eigenschaften bestimmte Bildschirmgrößen verwendet werden.

Im folgenden Beispiel aus Now in Android ist Robolectric so konfiguriert, dass eine Bildschirmgröße von 1.000 x 1.000 dp mit einer Auflösung von 480 dpi emuliert wird:

@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 { ... }

Sie können die Qualifier aus dem Testtext auch wie in diesem Snippet aus dem Beispiel Now in Android festlegen:

val (width, height, dpi) = ...

// Set qualifiers from specs.
RuntimeEnvironment.setQualifiers("w${width}dp-h${height}dp-${dpi}dpi")

Beachten Sie, dass RuntimeEnvironment.setQualifiers() die System- und Anwendungsressourcen mit der neuen Konfiguration aktualisiert, aber keine Aktion für aktive Aktivitäten oder andere Komponenten auslöst.

Weitere Informationen finden Sie in der Gerätekonfiguration von Robolectric.

Von Gradle verwaltete Geräte

Mit dem Android-Gradle-Plug-in Gradle-managed devices (GMD) können Sie die Spezifikationen der Emulatoren und tatsächlichen Geräte definieren, auf denen Ihre instrumentierten Tests ausgeführt werden. Erstelle Spezifikationen für Geräte mit unterschiedlichen Bildschirmgrößen, um eine Teststrategie zu implementieren, bei der bestimmte Tests auf bestimmten Bildschirmgrößen ausgeführt werden müssen. Wenn Sie GMD mit Continuous Integration (CI) verwenden, können Sie dafür sorgen, dass die entsprechenden Tests bei Bedarf ausgeführt werden, Emulatoren bereitstellen und starten sowie Ihre CI-Einrichtung vereinfachen.

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"
                }
            }
        }
    }
}

Im Projekt testing-sample sind mehrere Beispiele für GMD zu finden.

Firebase Test Lab

Verwenden Sie Firebase Test Lab (FTL) oder einen ähnlichen Gerätefarmdienst, um Ihre Tests auf bestimmten echten Geräten auszuführen, auf die Sie möglicherweise keinen Zugriff haben, z. B. auf faltbaren Geräten oder Tablets verschiedener Größen. Firebase Test Lab ist ein kostenpflichtiger Dienst mit einer kostenlosen Stufe. FTL unterstützt auch das Ausführen von Tests auf Emulatoren. Diese Dienste verbessern die Zuverlässigkeit und Geschwindigkeit von instrumentierten Tests, da sie Geräte und Emulatoren im Voraus bereitstellen können.

Informationen zur Verwendung von FTL mit GMD finden Sie unter Tests mit Gradle-verwalteten Geräten skalieren.

Filterung mit dem Test-Runner testen

Eine optimale Teststrategie sollte nicht zweimal überprüft werden, sodass die meisten Ihrer UI-Tests nicht auf mehreren Geräten ausgeführt werden müssen. Normalerweise filtern Sie Ihre UI-Tests, indem Sie alle oder die meisten davon auf einem Smartphone-Formfaktor und nur einen Teil davon auf Geräten mit unterschiedlichen Bildschirmgrößen ausführen.

Sie können bestimmte Tests annotieren, die nur mit bestimmten Geräten ausgeführt werden sollen, und dann mit dem Befehl, der die Tests ausführt, ein Argument an den AndroidJUnitRunner übergeben.

Sie können beispielsweise verschiedene Annotationen erstellen:

annotation class TestExpandedWidth
annotation class TestCompactWidth

Und verwende sie für verschiedene Tests:

class MyTestClass {

    @Test
    @TestExpandedWidth
    fun myExample_worksOnTablet() {
        ...
    }

    @Test
    @TestCompactWidth
    fun myExample_worksOnPortraitPhone() {
        ...
    }

}

Beim Ausführen der Tests können Sie dann das Attribut android.testInstrumentationRunnerArguments.annotation verwenden, um bestimmte Tests zu filtern. Wenn Sie beispielsweise von Gradle verwaltete Geräte verwenden:

$ ./gradlew pixelTabletApi30DebugAndroidTest -Pandroid.testInstrumentationRunnerArguments.annotation='com.sample.TestExpandedWidth'

Wenn Sie GMD nicht verwenden und Emulatoren für CI verwalten, sorgen Sie zuerst dafür, dass der richtige Emulator oder das richtige Gerät bereit und verbunden ist. Übergeben Sie dann den Parameter an einen der Gradle-Befehle, um instrumentierte Tests auszuführen:

$ ./gradlew connectedAndroidTest -Pandroid.testInstrumentationRunnerArguments.annotation='com.sample.TestExpandedWidth'

Beachten Sie, dass Espresso Device (siehe nächster Abschnitt) Tests auch mithilfe von Geräteeigenschaften filtern kann.

Espressogerät

Verwenden Sie Espresso Device, um Aktionen an Emulatoren in Tests mit beliebigen instrumentierten Tests durchzuführen, einschließlich Espresso-, Compose- oder UI Automator-Tests. Diese Aktionen können das Festlegen der Bildschirmgröße oder das Wechseln zwischen faltbaren Geräten und Einstellungen umfassen. Sie können beispielsweise einen faltbaren Emulator steuern und auf den Modus „Auf dem Tisch“ setzen. Das Espresso Device enthält auch JUnit-Regeln und -Annotationen, um bestimmte Funktionen zu erfordern:

@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()))
    }
}

Beachten Sie, dass sich das Espresso-Gerät noch in der Alphaphase befindet und folgende Anforderungen erfüllt:

  • Android Gradle-Plug-in 8.3 oder höher
  • Android Emulator 33.1.10 oder höher
  • Virtuelles Android-Gerät mit API-Level 24 oder höher

Tests filtern

Espresso Device kann die Eigenschaften verbundener Geräte lesen, damit Sie Tests mit Annotationen filtern können. Wenn die annotierten Anforderungen nicht erfüllt sind, werden die Tests übersprungen.

Annotation „DeviceMode“ erforderlich

Die Annotation RequiresDeviceMode kann mehrmals verwendet werden, um einen Test anzugeben, der nur ausgeführt wird, wenn alle DeviceMode-Werte auf dem Gerät unterstützt werden.

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()))
    }
}

Display-Annotation erforderlich

Mit der Annotation RequiresDisplay können Sie die Breite und Höhe des Gerätebildschirms mithilfe von Größenklassen angeben. Diese definieren Dimensions-Buckets gemäß den offiziellen Fenstergrößenklassen.

class OnDeviceTest {
    ...
    @Test
    @RequiresDisplay(EXPANDED, COMPACT)
    fun myScreen_expandedWidthCompactHeight() {
        ...
    }
}

Größe des Bildschirms anpassen

Verwenden Sie die Methode setDisplaySize(), um die Größe des Bildschirms zur Laufzeit anzupassen. Verwenden Sie die Methode in Verbindung mit der Klasse DisplaySizeRule, damit alle während Tests vorgenommenen Änderungen vor dem nächsten Test rückgängig gemacht werden.

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

Wenn Sie die Größe einer Anzeige mit setDisplaySize() ändern, wirkt sich das nicht auf die Dichte des Geräts aus. Wenn eine Dimension nicht in das Zielgerät passt, schlägt der Test mit einem UnsupportedDeviceOperationException fehl. Um zu verhindern, dass in diesem Fall Tests ausgeführt werden, filtern Sie sie mit der Annotation RequiresDisplay heraus:

@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

Mit der Klasse StateRestorationTester wird die Zustandswiederherstellung für zusammensetzbare Komponenten getestet, ohne Aktivitäten neu zu erstellen. Dies macht Tests schneller und zuverlässiger, da die Wiederherstellung von Aktivitäten ein komplexer Prozess mit mehreren Synchronisierungsmechanismen ist:

@Test
fun compactDevice_selectedEmailEmailRetained_afterConfigChange() {
    val stateRestorationTester = StateRestorationTester(composeTestRule)

    // Set content through the StateRestorationTester object.
    stateRestorationTester.setContent {
        MyApp()
    }

    // Simulate a config change.
    stateRestorationTester.emulateSavedInstanceStateRestore()
}

Fenstertestbibliothek

Die Fenstertestbibliothek enthält Dienstprogramme, mit denen Sie Tests schreiben können, die auf Funktionen der Fensterverwaltung basieren oder diese prüfen, z. B. Aktivitätseinbettung oder faltbare Funktionen. Das Artefakt ist über das Maven-Repository von Google verfügbar.

Sie können beispielsweise mit der Funktion FoldingFeature() eine benutzerdefinierte FoldingFeature generieren, die Sie in der Funktion „Compose“ verwenden können. Verwenden Sie in Java die Funktion createFoldingFeature().

In der Vorschau des Schreibens können Sie FoldingFeature so implementieren:

@Preview(showBackground = true, widthDp = 480, heightDp = 480)
@Composable private fun FoldablePreview() =
    MyApplicationTheme {
        ExampleScreen(
            displayFeatures = listOf(FoldingFeature(Rect(0, 240, 480, 240)))
        )
 }

Außerdem können Sie mit der Funktion TestWindowLayoutInfo() Displayfunktionen in UI-Tests emulieren. Im folgenden Beispiel wird ein FoldingFeature mit einem senkrechten Scharnier HALF_OPENED in der Bildschirmmitte simuliert. Dann wird geprüft, ob das erwartete Layout dem entspricht:

Schreiben

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()
    }
}

Aufrufe

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()))
    }
}

Weitere Beispiele finden Sie im WindowManager-Projekt.

Weitere Informationen

Dokumentation

Produktproben

Codelabs