مكتبات وأدوات لاختبار أحجام الشاشات المختلفة

يوفّر Android مجموعة متنوّعة من الأدوات وواجهات برمجة التطبيقات التي يمكن أن تساعدك في إنشاء اختبارات لأحجام الشاشات والنوافذ المختلفة.

‫DeviceConfigurationOverride

تتيح لك الدالة البرمجية المركّبة DeviceConfigurationOverride تجاوز سمات الإعدادات لاختبار أحجام الشاشات والنوافذ المتعدّدة في تنسيقات Compose. يُناسب الإلغاء 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 in Android*.

بالإضافة إلى ذلك، يمكنك استخدام هذه الدالة البرمجية المركّبة لضبط مقياس الخط والسمات والخصائص الأخرى التي قد تريد اختبارها على أحجام النوافذ المختلفة.

Robolectric

استخدِم Robolectric لإجراء اختبارات واجهة المستخدم المستندة إلى Compose أو طرق العرض على جهاز JVM محليًا، بدون الحاجة إلى أجهزة أو محاكيات. يمكنك ضبط Robolectric لاستخدام أحجام شاشة معيّنة، من بين خصائص مفيدة أخرى.

في المثال التالي من Now in Android، تم ضبط Robolectric لمحاكاة حجم شاشة يبلغ 1000×1000 وحدة بكسل مستقلة الكثافة بدقة 480 نقطة في البوصة:

@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

يتيح لك المكوّن الإضافي لنظام Gradle المتوافق مع Android للأجهزة التي يديرها Gradle التي يديرها Gradle (GMD) تحديد مواصفات المحاكيات والأجهزة الحقيقية التي يتم عليها إجراء اختبارات قياس حالة التطبيق. يمكنك إنشاء مواصفات للأجهزة بأحجام شاشة مختلفة لتنفيذ استراتيجية اختبار يجب فيها إجراء اختبارات معيّنة على أحجام شاشة معيّنة. باستخدام الأجهزة التي يديرها Gradle مع التكامل المستمر (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"
                }
            }
        }
    }
}

يمكنك الاطّلاع على أمثلة متعدّدة للأجهزة التي يديرها Gradle في مشروع testing-samples.

مختبر Firebase الافتراضي

استخدِم مختبر Firebase الافتراضي (FTL)، أو خدمة مشابهة لمجموعة الأجهزة لإجراء اختباراتك على أجهزة حقيقية معيّنة قد لا يكون بإمكانك الوصول إليها، مثل الأجهزة القابلة للطي أو الأجهزة اللوحية بأحجام مختلفة. مختبر Firebase الافتراضي هو خدمة مدفوعة تتضمّن طبقة مجانية. يتيح مركز الاختبار الافتراضي لمنصة Firebase أيضًا إجراء الاختبارات على المحاكيات. تُحسِّن هذه الخدمات موثوقية اختبارات قياس حالة التطبيق وسرعتها لأنّه يمكنها توفير الأجهزة والمحاكيات مسبقًا.

للحصول على معلومات حول استخدام مركز الاختبار الافتراضي لمنصة Firebase مع الأجهزة التي يديرها Gradle، يُرجى الاطّلاع على مقالة توسيع نطاق اختباراتك باستخدام الأجهزة التي يديرها 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'

إذا كنت لا تستخدم الأجهزة التي يديرها Gradle وتُدير المحاكيات على التكامل المستمر، تأكَّد أولاً من أنّ المحاكي أو الجهاز الصحيح جاهز ومتصل، ثم مرِّر المَعلمة إلى أحد أوامر Gradle لإجراء اختبارات قياس حالة التطبيق:

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

يُرجى العِلم أنّ Espresso Device (راجِع القسم التالي) يمكنه أيضًا فلترة الاختبارات باستخدام خصائص الجهاز.

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 لا يزال في مرحلة الإصدار الأولي ويستوفي المتطلبات التالية:

  • الإصدار 8.3 من المكوّن الإضافي لنظام Gradle المتوافق مع Android أو إصدار أحدث
  • الإصدار 33.1.10 من محاكي Android أو إصدار أحدث
  • جهاز Android افتراضي يعمل بمستوى واجهة برمجة التطبيقات 24 أو إصدار أحدث

فلترة الاختبارات

يمكن لـ Espresso Device قراءة خصائص الأجهزة المتصلة ليتيح لك فلترة الاختبارات باستخدام التعليقات التوضيحية. إذا لم يتم استيفاء المتطلبات التي تم وضع تعليقات توضيحية لها، يتم تخطّي الاختبارات.

التعليق التوضيحي 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()
}

مكتبة اختبار النوافذ

تحتوي مكتبة اختبار النوافذ على أدوات مساعدة لمساعدتك في كتابة الاختبارات التي تعتمد على الميزات المتعلّقة بإدارة النوافذ أو تتحقّق منها، مثل تضمين الأنشطة أو الميزات القابلة للطي. يتوفّر العنصر من خلال مستودع 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.

مراجع إضافية

الوثائق

نماذج

اختبارات الرموز