ספריות וכלים לבדיקת מסכים בגדלים שונים

ב-Android יש מגוון כלים וממשקי API שיכולים לעזור לכם ליצור בדיקות במסכים ובחלונות בגדלים שונים.

DeviceConfigurationOverride

התוכן הקומפוזבילי DeviceConfigurationOverride מאפשר לשנות מברירת המחדל תצורה כדי לבדוק גדלים מרובים של מסכים וחלונות במצב 'כתיבה' שונות. השינוי ForcedSize מתאים לכל פריסה במרחב הזמין, שמאפשר להריץ בדיקה של ממשק המשתמש על כל גודל מסך. למשל, אפשר להשתמש בגורם צורה קטן לטלפון כדי להריץ את כל בדיקות ממשק המשתמש, כולל בדיקות ממשק משתמש לטלפונים גדולים, מתקפלים בטאבלטים.

   DeviceConfigurationOverride(
        DeviceConfigurationOverride.ForcedSize(DpSize(1280.dp, 800.dp))
    ) {
        MyScreen() // Will be rendered in the space for 1280dp by 800dp without clipping.
    }
איור 1. שימוש ב-DeviceConfigurationOverride כדי להתאים פריסת טאבלט למכשיר קטן יותר של גורם צורה, כמו \*Now ב-Android*.

בנוסף, ניתן להשתמש בתוכן הקומפוזבילי הזה כדי להגדיר קנה מידה של גופנים, עיצובים ועוד למאפיינים שכדאי לבדוק בגדלים שונים של חלונות.

רובולקטרי

להשתמש ב-Robolectric כדי להריץ בדיקות ממשק משתמש על כתיבה או על צפייה ב-JVM מקומית – אין צורך במכשירים או באמולטורים. אפשר להגדיר Robolectric לשימוש בגדלי מסך ספציפיים, כמו גם מאפיינים שימושיים אחרים.

בדוגמה הבאה מ-Now ב-Android, Robolectric מוגדר כדי לבצע אמולציה של גודל מסך של 1000x1000 dp ברזולוציה של 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 { ... }

ניתן גם להגדיר את ערכי האישור מגוף הבדיקה, כפי שנעשה בקטע הקוד הזה מתוך הדוגמה עכשיו ב-Android:

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

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

חשוב לשים לב שמערכת RuntimeEnvironment.setQualifiers() מעדכנת את המערכת. משאבים באפליקציה עם התצורה החדשה, אבל לא מפעילה שום פעולה בפעילויות פעילות או ברכיבים אחרים.

מידע נוסף זמין במסמכים בנושא הגדרת המכשיר של Robolectric.

מכשירים בניהול Gradle

הפלאגין Gradle-managed devices (GMD) של Android Gradle מאפשרת להגדיר את המפרט של אמולטורים ומכשירים אמיתיים שהבדיקות האינסטרומנטליות שלך פועלות. יצירת מפרטים למכשירים עם מסכים בגדלים שונים כדי ליישם אסטרטגיית בדיקה שבה בדיקות מסוימות תוצג במסכים בגדלים מסוימים. באמצעות GMD עם אינטגרציה רציפה (CI) (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.

Firebase Test Lab

משתמשים ב-Firebase Test Lab (FTL), או בשירות דומה של חוות מכשירים, כדי להפעיל את בדיקות במכשירים אמיתיים ספציפיים שייתכן שאין לך גישה אליהם, כמו מכשירים מתקפלים או טאבלטים בגדלים שונים. Firebase Test Lab הוא פלטפורמה בתשלום באמצעות תוכנית ללא תשלום. FTL תומך גם בהרצת בדיקות של אמולטורים. השירותים האלה משפרים את האמינות והמהירות של הבדיקות האינסטרומנטליות כי הם יכולים להקצות מכשירים ואמולטורים מראש.

מידע נוסף על השימוש ב-FTL עם GMD זמין במאמר התאמת הבדיקות באמצעות מכשירים שמנוהלים על ידי Gradle.

בדיקת הסינון באמצעות הרצת הבדיקה

כשמשתמשים באסטרטגיית בדיקה אופטימלית, לא כדאי לאמת את אותו הדבר פעמיים, לכן רוב בדיקות ממשק משתמש לא צריכות לפעול בכמה מכשירים. בדרך כלל, מסננים את ממשק המשתמש על ידי הרצה של כולם או של רובם על גורם צורה טלפוני, ורק קבוצת משנה שלהם במכשירים עם מסכים בגדלים שונים.

אפשר להוסיף הערות לבדיקות מסוימות כדי שירוצו רק במכשירים מסוימים, ואז לעבור ארגומנט ל-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'

לתשומת ליבך, מכשיר אספרסו (מידע נוסף מפורט בקטע הבא) יכול גם לסנן בדיקות באמצעות מאפייני המכשיר.

מכשיר אספרסו

משתמשים במכשיר אספרסו כדי לבצע פעולות על אמולטורים בבדיקות באמצעות כל בדיקות כלים אינסטרומנטליים, כולל בדיקות אספרסו, נייטיב או אוטומציה של ממשק המשתמש. הפעולות האלה יכולות לכלול הגדרה של גודל המסך או החלפת המצבים של 'מכשיר מתקפל' או תנוחות. לדוגמה, ניתן לשלוט באמולטור מתקפל ולהגדיר אותו לערך למצב 'על משטח, מסך למעלה'. מכשיר אספרסו מכיל גם כללים והערות של 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 ומעלה

סינון הבדיקות

מכשיר האספרסו יכול לקרוא את המאפיינים של מכשירים מחוברים כדי לאפשר לך בדיקות סינון באמצעות הערות. אם הדרישות להוספת הערות לא מתמלאות, המערכת תדלג על הבדיקות.

הערת 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 משמשת לבדיקת שחזור המדינה לרכיבים קומפוזביליים בלי ליצור פעילויות מחדש. כך הבדיקות מהירות יותר ואמין יותר, כיוון שבילוי פעילות הוא תהליך מורכב שכולל מנגנוני סנכרון:

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

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

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

הספרייה לבדיקת חלונות

הספרייה 'בדיקת חלונות' מכילה כלי עזר שיעזרו לך לכתוב בדיקות המסתמכות להפעיל או לאמת תכונות שקשורות לניהול החלונות, כמו פעילות הטמעה או תכונות מתקפלות. פריט המידע שנוצר בתהליך הפיתוח (Artifact) זמין דרך Google Maven Repository.

לדוגמה, אפשר להשתמש בפונקציה FoldingFeature() כדי ליצור FoldingFeature בהתאמה אישית, ואפשר להשתמש בו בתצוגות המקדימות של הכתיבה. ב-Java, להשתמש בפונקציה createFoldingFeature().

בתצוגה מקדימה של פיתוח נייטיב, אפשר להטמיע את 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.

מקורות מידע נוספים

מסמכים

דוגמיות

שיעורי Lab