अलग-अलग स्क्रीन साइज़ की जांच करने के लिए लाइब्रेरी और टूल

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 का इस्तेमाल करके, टैबलेट के लेआउट को छोटे साइज़, डाइमेंशन या कॉन्फ़िगरेशन वाले डिवाइस में फ़िट करना, जैसा कि \*Now in Android* में किया गया है.

इसके अलावा, इस कंपोज़ेबल का इस्तेमाल करके, फ़ॉन्ट स्केल, थीम, और अन्य प्रॉपर्टी सेट की जा सकती हैं. इन्हें अलग-अलग विंडो साइज़ पर टेस्ट किया जा सकता है.

Robolectric

जेवीएम पर Compose या व्यू-आधारित यूज़र इंटरफ़ेस (यूआई) टेस्ट को लोकल तौर पर चलाने के लिए, Robolectric का इस्तेमाल करें. इसके लिए, किसी डिवाइस या एम्युलेटर की ज़रूरत नहीं होती. Robolectric को कॉन्फ़िगर किया जा सकता है, ताकि वह अन्य काम की प्रॉपर्टी के साथ-साथ, स्क्रीन के खास साइज़ का इस्तेमाल कर सके.

Now in Android के इस उदाहरण में, Robolectric को 1000x1000 डीपी के स्क्रीन साइज़ और 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 प्लगिन की मदद से, एम्युलेटर और असली डिवाइसों की खास जानकारी तय की जा सकती है. इन डिवाइसों पर, आपके इंस्ट्रुमेंटेड टेस्ट चलते हैं. अलग-अलग स्क्रीन साइज़ वाले डिवाइसों के लिए स्पेसिफ़िकेशन बनाएं, ताकि टेस्टिंग की ऐसी रणनीति लागू की जा सके जिसमें कुछ टेस्ट, कुछ खास स्क्रीन साइज़ पर ही चलाए जाएं. लगातार इंटिग्रेशन (सीआई) के साथ जीएमडी का इस्तेमाल करके, यह पक्का किया जा सकता है कि ज़रूरत पड़ने पर सही टेस्ट चलाए जाएं. साथ ही, एम्युलेटर को उपलब्ध कराया जा सके और लॉन्च किया जा सके. इसके अलावा, सीआई सेटअप को आसान बनाया जा सके.

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"
                }
            }
        }
    }
}

testing-samples प्रोजेक्ट में, जीएमडी के कई उदाहरण देखे जा सकते हैं.

Firebase टेस्ट लैब

Firebase Test Lab (FTL) या डिवाइस फ़ार्म की ऐसी ही किसी सेवा का इस्तेमाल करके, अपने टेस्ट को उन असली डिवाइसों पर चलाएं जिनका ऐक्सेस आपके पास नहीं है. जैसे, फ़ोल्ड किए जा सकने वाले डिवाइस या अलग-अलग साइज़ के टैबलेट. Firebase टेस्ट लैब, पैसे चुकाकर ली जाने वाली सेवा है. हालांकि, इसका एक फ़्री टियर भी है. FTL, एमुलेटर पर टेस्ट चलाने की सुविधा भी देता है. ये सेवाएं, इंस्ट्रुमेंटेड टेस्टिंग की विश्वसनीयता और स्पीड को बेहतर बनाती हैं. ऐसा इसलिए, क्योंकि ये डिवाइसों और एम्युलेटर को पहले से ही उपलब्ध करा सकती हैं.

जीएमडी के साथ एफ़टीएल का इस्तेमाल करने के बारे में जानकारी पाने के लिए, ऐसे डिवाइसों की मदद से टेस्ट को बड़े पैमाने पर करें जिन्हें 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 के किसी एक कमांड को पैरामीटर पास करें:

$ ./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 प्लगिन 8.3 या इसके बाद का वर्शन
  • Android Emulator 33.1.10 या इसके बाद का वर्शन
  • एपीआई लेवल 24 या इसके बाद के वर्शन पर काम करने वाला 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 एनोटेशन

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 library

Window Testing लाइब्रेरी में ऐसी सुविधाएं होती हैं जिनकी मदद से, विंडो मैनेजमेंट से जुड़ी सुविधाओं पर निर्भर या उनकी पुष्टि करने वाले टेस्ट लिखे जा सकते हैं. जैसे, गतिविधि एम्बेड करना या फ़ोल्ड किए जा सकने वाले डिवाइसों से जुड़ी सुविधाएं. आर्टफ़ैक्ट, Google की Maven रिपॉज़िटरी पर उपलब्ध है.

उदाहरण के लिए, 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() फ़ंक्शन का इस्तेमाल करके, यूज़र इंटरफ़ेस (यूआई) टेस्ट में डिसप्ले की सुविधाओं को एम्युलेट किया जा सकता है. नीचे दिए गए उदाहरण में, स्क्रीन के बीच में HALF_OPENED वर्टिकल हिंज वाले FoldingFeature को सिम्युलेट किया गया है. इसके बाद, यह जांच की गई है कि लेआउट उम्मीद के मुताबिक है या नहीं:

लिखें

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 प्रोजेक्ट में आपको ज़्यादा सैंपल मिल सकते हैं.

अन्य संसाधन

दस्तावेज़

सैंपल

कोडलैब