מערכת Android מספקת מגוון כלים וממשקי API שיכולים לעזור לכם ליצור בדיקות לגדלים שונים של מסכים וחלונות.
DeviceConfigurationOverride
רכיב ה-Composable DeviceConfigurationOverride מאפשר לכם לשנות מאפייני הגדרה כדי לבדוק כמה גדלים של מסכים וחלונות בפריסות של Compose. ההחלפה של ForcedSize מתאימה לכל פריסה במקום הזמין, ומאפשרת להריץ כל בדיקת ממשק משתמש בכל גודל מסך. לדוגמה, אפשר להשתמש בטלפון קטן כדי להריץ את כל בדיקות ממשק המשתמש, כולל בדיקות ממשק משתמש לטלפונים גדולים, למכשירים מתקפלים ולטאבלטים.
DeviceConfigurationOverride(
DeviceConfigurationOverride.ForcedSize(DpSize(1280.dp, 800.dp))
) {
MyScreen() // Will be rendered in the space for 1280dp by 800dp without clipping.
}
בנוסף, אפשר להשתמש ב-composable הזה כדי להגדיר את קנה המידה של הגופן, את העיצובים ואת המאפיינים האחרים שרוצים לבדוק בגדלים שונים של חלונות.
Robolectric
אפשר להשתמש ב-Robolectric כדי להריץ בדיקות של ממשק משתמש מבוסס-תצוגה או Compose ב-JVM באופן מקומי – לא נדרשים מכשירים או אמולטורים. אתם יכולים להגדיר את Robolectric כך שישתמש בגדלי מסך ספציפיים, בין מאפיינים שימושיים אחרים.
@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
תוסף Android Gradle Gradle-managed devices (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 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'
שימו לב שאפשר גם לסנן בדיקות באמצעות מאפייני המכשיר (ראו את הקטע הבא) ב-Espresso Device.
מכשיר אספרסו
אפשר להשתמש ב-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 Plugin 8.3 ואילך
- Android Emulator מגרסה 33.1.10 ואילך
- מכשיר וירטואלי של Android עם API ברמה 24 ומעלה
סינון בדיקות
מכשיר Espresso יכול לקרוא את המאפיינים של מכשירים מחוברים כדי לאפשר לכם לסנן בדיקות באמצעות הערות. אם הדרישות עם ההערות לא מתקיימות, המערכת מדלגת על הבדיקות.
נדרשת ההערה RequiresDeviceMode
אפשר להשתמש בהערה 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
ההערה 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
ספריית 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.
מקורות מידע נוספים
מסמכים
דוגמיות
- דוגמה ל-WindowManager
- דוגמאות למכשירי אספרסו
- Now In Android
- משתמש בבדיקות צילומי מסך כדי לאמת גדלים שונים של מסכים
Codelabs