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

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

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

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

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 أو Compose أو UI Automator. قد تشمل هذه الإجراءات ضبط حجم الشاشة أو تبديل حالات الشاشة القابلة للطي. أو الوضعيات. على سبيل المثال، يمكنك التحكم في المحاكي القابل للطي وضبطه على وضع "الشاشة المسطحة". يحتوي جهاز Espresso أيضًا على قواعد 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 لا يزال في المرحلة الأولية ويتميّز بما يلي: المتطلبات:

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

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

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

التعليق التوضيحي مطلوب لوضع الجهاز

يمكن استخدام التعليق التوضيحي 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.
    }
}

مختبِر StateRestoration

تُستخدَم الفئة 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.

على سبيل المثال، يمكنك استخدام الدالة 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.

مصادر إضافية

المستندات

نماذج

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