کتابخانه‌ها و ابزارها برای آزمایش اندازه‌های مختلف صفحه‌نمایش

‫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.
    }
شکل ۱. استفاده از DeviceConfigurationOverride برای جا دادن چیدمان رایانه لوحی در دستگاهی با ضریب شکل کوچک‌تر، مانند \*اکنون در Android*.

علاوه‌براین، می‌توانید از این ترکیب‌پذیر برای تنظیم مقیاس قلم، زمینه‌ها، و سایر ویژگی‌هایی که ممکن است بخواهید در اندازه‌های مختلف پنجره آزمایش کنید استفاده کنید.

Robolectric

از Robolectric برای اجرای Compose یا آزمایش‌های واسط کاربر مبتنی بر نما در JVM به‌صورت محلی استفاده کنید—نیازی به دستگاه یا شبیه‌ساز نیست. می‌توانید Robolectric را پیکربندی کنید تا از اندازه‌های خاص صفحه‌نمایش، در میان سایر ویژگی‌های مفید، استفاده کند.

در نمونه زیر از اکنون در Android، 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 { ... }

همچنین می‌توانید واجدشرایط‌ها را از بدنه آزمایش تنظیم کنید، همان‌طور که در این قطعه کد از مثال اکنون در 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 (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

از 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

از 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 نسخه ۸.۳ یا بالاتر
  • «شبیه‌ساز Android» نسخه ۳۳.۱.۱۰ یا بالاتر
  • دستگاه مجازی Android که سطح میانای برنامه‌سازی کاربردی ۲۴ یا بالاتر را اجرا می‌کند

فیلتر کردن آزمایش‌ها

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 annotation

حاشیه‌نویسی 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 پیدا کنید.

منابع بیشتر

اسناد

نمونه‌ها

Codelabs