Bibliothèques et outils pour tester différentes tailles d'écran

Android fournit divers outils et API qui peuvent vous aider à créer des tests pour différentes tailles d'écran et de fenêtre.

Remplacement de la configuration de l'appareil

Le composable DeviceConfigurationOverride vous permet de remplacer attributs de configuration pour tester plusieurs tailles d'écran et de fenêtre dans Compose mises en page. Le forçage ForcedSize s'adapte à n'importe quelle mise en page dans l'espace disponible. qui vous permet d'exécuter Test de l'interface utilisateur sur n'importe quelle taille d'écran Par exemple, vous pouvez utiliser un petit facteur de forme pour exécuter tous vos tests d'interface utilisateur, y compris pour les grands téléphones, les appareils pliables et tablettes.

   DeviceConfigurationOverride(
        DeviceConfigurationOverride.ForcedSize(DpSize(1280.dp, 800.dp))
    ) {
        MyScreen() // Will be rendered in the space for 1280dp by 800dp without clipping.
    }
<ph type="x-smartling-placeholder">
</ph>
Figure 1. Utilisation de DeviceConfigurationOverride pour adapter la mise en page d'une tablette à un appareil plus petit, comme dans \*Now in Android*.

Vous pouvez également utiliser ce composable pour définir l'échelle de police, les thèmes et d'autres à tester sur différentes tailles de fenêtre.

Robolectric

Utiliser Robolectric pour exécuter des tests Compose ou des tests d'interface utilisateur basés sur les vues sur la JVM en local : aucun appareil ni émulateur n'est requis. Vous pouvez configurer Robolectric pour utiliser des tailles d'écran spécifiques, entre autres propriétés utiles.

Dans l'exemple suivant tiré de Now in Android, Robolectric est configuré pour émuler une taille d'écran de 1 000 x 1 000 dp avec une résolution de 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 { ... }

Vous pouvez également définir les qualificatifs à partir du corps de test, comme indiqué dans cet extrait à partir de l'exemple Now in Android:

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

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

Notez que RuntimeEnvironment.setQualifiers() met à jour le système et ressources d'application avec la nouvelle configuration, mais ne déclenche aucune action sur les activités actives ou d'autres composants.

Pour en savoir plus, consultez la documentation de Robolectric sur la configuration de l'appareil.

Appareils gérés par Gradle

Plug-in Android Gradle appareils gérés par Gradle (GMD) vous permet de définir les spécifications des émulateurs et des appareils réels l'exécution de vos tests instrumentés. Créer des spécifications pour les appareils avec différentes tailles d'écran pour mettre en œuvre une stratégie de test dans laquelle certains tests doivent être exécutée sur certaines tailles d'écran. En utilisant GMD avec l'intégration continue (CI), vous pouvez vous assurer que les tests appropriés sont exécutés lorsque cela est nécessaire, de provisionner et de lancer des émulateurs, et de simplifier la configuration 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"
                }
            }
        }
    }
}

Vous trouverez plusieurs exemples de GMD dans le projet testing-samples.

Firebase Test Lab

Utilisez Firebase Test Lab (FTL) ou un service de batterie d'appareils similaire pour exécuter votre des tests sur des appareils réels spécifiques auxquels vous n'avez peut-être pas accès, tels que des pliables ou des tablettes de tailles différentes. Firebase Test Lab est un service payant disponible avec une version sans frais. FTL permet également d'exécuter des tests sur des émulateurs. Ces services améliorent la fiabilité et la vitesse des tests d'instrumentation, car ils peuvent provisionner des appareils et des émulateurs à l'avance.

Pour en savoir plus sur l'utilisation de FTL avec GMD, consultez Effectuer le scaling de vos tests avec Appareils gérés par Gradle

Filtrer les tests avec le lanceur de test

Une stratégie de test optimale ne devrait pas vérifier la même chose deux fois. La plupart de vos Les tests de l'interface utilisateur n'ont pas besoin d'être exécutés sur plusieurs appareils. Généralement, vous filtrez votre UI tests en les exécutant tous ou la plupart sur un facteur de forme de téléphone et seulement un sous-ensemble appareils avec différentes tailles d’écran.

Vous pouvez annoter certains tests pour qu'ils ne soient exécutés qu'avec certains appareils, puis réussir un argument à AndroidJUnitRunner à l'aide de la commande qui exécute le tests.

Par exemple, vous pouvez créer différentes annotations:

annotation class TestExpandedWidth
annotation class TestCompactWidth

Utilisez-les dans différents tests:

class MyTestClass {

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

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

}

Vous pouvez ensuite utiliser android.testInstrumentationRunnerArguments.annotation lors de l'exécution des tests pour filtrer des tests spécifiques. Par exemple, si vous à l'aide d'appareils gérés par Gradle:

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

Si vous n'utilisez pas GMD et que vous gérez les émulateurs sur CI, assurez-vous d'abord que le l'émulateur ou l'appareil approprié est prêt et connecté, puis transmettez le paramètre à l'une des commandes Gradle pour exécuter des tests d'instrumentation:

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

Notez qu'Espresso Device (voir la section suivante) peut également filtrer les tests en utilisant Propriétés de l'appareil.

Appareil Espresso

Utilisez Espresso Device pour effectuer des actions sur les émulateurs lors des tests à l'aide de n'importe quelle Type de tests d'instrumentation, y compris les tests Espresso, Compose ou UI Automator. Par exemple, vous pouvez définir la taille de l'écran ou activer/désactiver les états des appareils pliables. ou des stratégies. Par exemple, vous pouvez contrôler un émulateur d'appareil pliable et le définir sur mode Écran à plat. Espresso Device contient également des règles et des annotations JUnit nécessitent certaines fonctionnalités:

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

Notez que Espresso Device est encore en phase alpha et présente les fonctionnalités suivantes : configuration requise:

  • Plug-in Android Gradle 8.3 ou version ultérieure
  • Android Emulator 33.1.10 ou version ultérieure
  • Appareil virtuel Android exécutant le niveau d'API 24 ou supérieur

Filtrer les tests

Espresso Device peut lire les propriétés des appareils connectés pour vous permettre : filtrer les tests à l'aide d'annotations. Si les exigences de l'annotation ne sont pas remplies, les tests sont ignorés.

Annotation DeviceMode requise

L'annotation RequiresDeviceMode peut être utilisée plusieurs fois pour indiquer Un test qui ne s'exécutera que si toutes les valeurs DeviceMode sont acceptées sur l'appareil.

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

Nécessite une annotation Display

L'annotation RequiresDisplay vous permet de spécifier la largeur et la hauteur l'écran de l'appareil à l'aide de classes de taille, qui définissent des buckets de dimensions. en suivant les classes de taille de fenêtre officielles.

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

Redimensionner les écrans

Utilisez la méthode setDisplaySize() pour redimensionner les dimensions de l'écran. lors de l'exécution. Utilisez la méthode conjointement avec DisplaySizeRule. , qui garantit que toutes les modifications apportées lors des tests sont annulées test suivant.

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

Lorsque vous redimensionnez un écran avec setDisplaySize(), la densité n'est pas modifiée de l'appareil. Ainsi, si une dimension n'est pas adaptée à l'appareil cible, le test échoue avec une erreur UnsupportedDeviceOperationException. Pour éviter que les tests dans ce cas, utilisez l'annotation RequiresDisplay pour les filtrer:

@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

La classe StateRestorationTester permet de tester la restauration de l'état. pour les composants composables sans recréer d'activités. Cela permet d'accélérer les tests et plus fiable, car la recréation d'activité est un processus complexe impliquant plusieurs de synchronisation des données:

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

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

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

Bibliothèque de tests de fenêtres

La bibliothèque de test de fenêtre contient des utilitaires qui vous aideront à écrire des tests qui reposent sur ou vérifier les fonctionnalités liées à la gestion des fenêtres, telles que l'activité d'intégration ou d'appareils pliables. L'artefact est disponible via l'API Dépôt Maven.

Par exemple, vous pouvez utiliser la fonction FoldingFeature() pour générer une un FoldingFeature personnalisé, que vous pouvez utiliser dans les aperçus Compose. En Java, utilisez la fonction createFoldingFeature().

Dans un aperçu de Compose, vous pouvez implémenter FoldingFeature comme suit:

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

Vous pouvez également émuler les fonctionnalités d'affichage dans les tests de l'interface utilisateur à l'aide de la méthode fonction TestWindowLayoutInfo(). L'exemple suivant simule une FoldingFeature avec une HALF_OPENED charnière verticale au centre de l'écran, puis vérifie si est celle attendue:

Compose

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

Vues

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

Vous trouverez d'autres exemples dans le projet WindowManager.

Ressources supplémentaires

Documentation

Exemples

Ateliers de programmation