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

اندروید ابزارها و API های مختلفی را ارائه می دهد که می توانند به شما در ایجاد آزمایش برای اندازه های مختلف صفحه و پنجره کمک کنند.

DeviceConfiguration Override

DeviceConfigurationOverride composable به شما امکان می‌دهد تا ویژگی‌های پیکربندی را لغو کنید تا چندین اندازه صفحه و پنجره را در طرح‌بندی‌های Compose آزمایش کنید. نادیده گرفتن ForcedSize با هر طرح بندی در فضای موجود مطابقت دارد، که به شما امکان می دهد هر آزمایش UI را در هر اندازه صفحه نمایش اجرا کنید. برای مثال، می‌توانید از یک ضریب فرم تلفن کوچک برای اجرای تمام آزمایش‌های رابط کاربری خود، از جمله تست‌های رابط کاربری برای تلفن‌های بزرگ، تاشوها و تبلت‌ها استفاده کنید.

   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 برای اجرای تست های UI Compose یا view-based در JVM به صورت محلی استفاده کنید — بدون نیاز به دستگاه یا شبیه ساز. می‌توانید Robolectric را برای استفاده از اندازه‌های خاص صفحه نمایش، در میان سایر ویژگی‌های مفید، پیکربندی کنید .

در مثال زیر از Now in Android ، Robolectric به گونه ای پیکربندی شده است که اندازه صفحه نمایش 1000x1000 dp با وضوح 480 dpi را تقلید کند:

@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() سیستم و منابع برنامه را با پیکربندی جدید به روز می کند اما هیچ اقدامی را روی فعالیت های فعال یا سایر مؤلفه ها انجام نمی دهد.

می توانید در مستندات پیکربندی دستگاه روبولکتریک بیشتر بخوانید.

دستگاه های مدیریت شده توسط 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 را در پروژه نمونه های آزمایشی بیابید.

آزمایشگاه تست 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، Compose یا UI Automator استفاده کنید. این اقدامات ممکن است شامل تنظیم اندازه صفحه یا تغییر حالت‌ها یا وضعیت‌های تاشو باشد. به عنوان مثال، می توانید یک شبیه ساز تاشو را کنترل کنید و آن را روی حالت میز قرار دهید. دستگاه اسپرسو همچنین حاوی قوانین و حاشیه نویسی 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()))
    }
}

توجه داشته باشید که دستگاه اسپرسو هنوز در مرحله آلفا است و دارای شرایط زیر است:

  • پلاگین اندروید Gradle 8.3 یا بالاتر
  • شبیه ساز اندروید 33.1.10 یا بالاتر
  • دستگاه مجازی Android که API سطح 24 یا بالاتر را اجرا می کند

تست های فیلتر

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

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 شامل ابزارهایی است که به شما کمک می‌کند آزمایش‌هایی بنویسید که بر روی ویژگی‌های مرتبط با مدیریت پنجره‌ها تکیه یا تأیید می‌کنند، مانند تعبیه فعالیت یا ویژگی‌های تاشو. این مصنوع از طریق مخزن Maven Google در دسترس است.

برای مثال، می‌توانید از تابع FoldingFeature() برای ایجاد یک FoldingFeature سفارشی استفاده کنید، که می‌توانید از آن در پیش‌نمایش‌های Compose استفاده کنید. در جاوا از تابع createFoldingFeature() استفاده کنید.

در پیش نمایش Compose، می توانید FoldingFeature به روش زیر پیاده سازی کنید:

@Preview(showBackground = true, widthDp = 480, heightDp = 480)
@Composable private fun FoldablePreview() =
    MyApplicationTheme {
        ExampleScreen(
            displayFeatures = listOf(FoldingFeature(Rect(0, 240, 480, 240)))
        )
 }

همچنین، می‌توانید ویژگی‌های نمایش را در تست‌های UI با استفاده از تابع 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