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

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

تجاوز إعداد الجهاز

تتيح لك العناصر القابلة للتجميع 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 in Android*

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

Robolectric

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

في المثال التالي من الميزات الجديدة في Android، تم ضبط Robolectric لمحاكاة حجم شاشة يبلغ 1000x1000 dp بدرجة دقة 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 { ... }

يمكنك أيضًا ضبط المؤهّلات من نص الاختبار كما هو موضّح في المقتطف التالي من مثال الميزات الم��و��ّرة ��ل��ن على Android:

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

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

يُرجى العِلم أنّ RuntimeEnvironment.setQualifiers() يعدّل موارد النظام والتطبيق باستخدام الإعدادات الجديدة، ولكنّه لا يُشغّل أي إجراء في الأنشطة النشطة أو المكوّنات الأخرى.

يمكنك الاطّلاع على مزيد من المعلومات في مستندات إعداد الجهاز في Robolectric.

الأجهزة المُدارة من Gradle

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

آلة إسبريسو

استخدِم جهاز Espresso لتنفيذ إجراءا�� على المحاكيات في الاختبارات باستخدام أي نوع من الاختبارات المزوّدة بأدوات قياس الأداء، بما في ذلك اختبارات 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 لا يزال في المرحلة التجريبية ويتضمّن ال requirements التالية:

  • الإصدار 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 تحديد عرض شاشة الجهاز وارتفاعها باستخدام فئات المقاس التي تحدّد مجموعات السمات وفقًا لفئات حجم النوافذ الرسمية.

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 Repository في Google.

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

مصادر إضافية

المستندات

نماذج

الدروس التطبيقية حول الترميز