ב-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 כך שישתמש בגדלי מסך ספציפיים, בין מאפיינים שימושיים אחרים.
בדוגמה הבאה מתוך Now in Android, Robolectric מוגדר להדמיה של גודל מסך של 1,000x1,000dp ברזולוציה של 480dpi:
@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 מגרסה 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 מאפשרת לציין את הרוחב והגובה של מסך המכשיר באמצעות מחלקות גודל, שמגדירות קבוצות של מידות בהתאם למחלקות הגודל הרשמיות של חלונות.
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