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

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

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

Now in Android के इस उदाहरण में, Robolectric को 480 dpi रिज़ॉल्यूशन के साथ, 1000x1000 dp स्क्रीन साइज़ को एम्युलेट करने के लिए कॉन्फ़िगर किया गया है:

@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-managed devices

Gradle-managed devices (जीएमडी) 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 टेस्ट लैब (एफ़टीएल) या डिवाइस फ़ार्म की इसी तरह की किसी सेवा का इस्तेमाल करके, असली डिवाइसों पर टेस्ट किए जा सकते हैं. हो सकता है कि आपके पास इन डिवाइसों का ऐक्सेस न हो. जैसे, अलग-अलग साइज़ के फ़ोल्ड किए जा सकने वाले डिवाइस या टैबलेट. Firebase टेस्ट लैब, पैसे चुकाकर ली जाने वाली सेवा है . हालांकि, इसमें एक फ़्री टियर भी शामिल है. एफ़टीएल में, एम्युलेटर पर भी टेस्ट किए जा सकते हैं. इन सेवाओं से, इंस्ट्रुमेंटेड टेस्टिंग की विश्वसनीयता और स्पीड बेहतर होती है, क्योंकि ये डिवाइसों और एम्युलेटर को पहले से प्रोविज़निंग कर सकती हैं.

जीएमडी के साथ एफ़टीएल का इस्तेमाल करने के बारे में जानने के लिए, Gradle-managed devices की मदद से टेस्ट को स्केल करना लेख पढ़ें.

टेस्ट रनर की मदद से टेस्ट फ़िल्टर करना

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

कुछ टेस्ट को सिर्फ़ कुछ डिवाइसों पर करने के लिए, उन पर एनोटेशन जोड़ा जा सकता है. इसके बाद, टेस्ट करने वाले कमांड का इस्तेमाल करके, AndroidJUnitRunner को कोई आर्ग्युमेंट पास किया जा सकता है.

उदाहरण के लिए, अलग-अलग एनोटेशन बनाए जा सकते हैं:

annotation class TestExpandedWidth
annotation class TestCompactWidth

और इनका इस्तेमाल, अलग-अलग टेस्ट पर किया जा सकता है:

class MyTestClass {

    @Test
    @TestExpandedWidth
    fun myExample_worksOnTablet() {
        ...
    }

    @Test
    @TestCompactWidth
    fun myExample_worksOnPortraitPhone() {
        ...
    }

}

इसके बाद, टेस्ट करते समय, android.testInstrumentationRunnerArguments.annotation प्रॉपर्टी का इस्तेमाल करके, खास टेस्ट को फ़िल्टर किया जा सकता है. उदाहरण के लिए, अगर Gradle-managed devices का इस्तेमाल किया जा रहा है, तो:

$ ./gradlew pixelTabletApi30DebugAndroidTest -Pandroid.testInstrumentationRunnerArguments.annotation='com.sample.TestExpandedWidth'

अगर जीएमडी का इस्तेमाल नहीं किया जा रहा है और सीआई पर एम्युलेटर मैनेज किए जा रहे हैं, तो सबसे पहले पक्का करें कि सही एम्युलेटर या डिवाइस तैयार हो और कनेक्ट हो. इसके बाद, इंस्ट्रुमेंटेड टेस्ट करने के लिए, Gradle के किसी एक कमांड को पैरामीटर पास करें:

$ ./gradlew connectedAndroidTest -Pandroid.testInstrumentationRunnerArguments.annotation='com.sample.TestExpandedWidth'

ध्यान दें कि Espresso Device (अगला सेक्शन देखें) भी डिवाइस की प्रॉपर्टी का इस्तेमाल करके, टेस्ट को फ़िल्टर कर सकता है.

Espresso Device

Espresso Device का इस्तेमाल करके, टेस्ट में एम्युलेटर पर कार्रवाइयां की जा सकती हैं. इसके लिए, Espresso, Compose या यूज़र इंटरफ़ेस (यूआई) 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 या इसके बाद वाला वर्शन
  • 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 एनोटेशन

The 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

The 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 library में, ऐसी यूटिलिटी शामिल होती हैं जिनकी मदद से, ऐसे टेस्ट लिखे जा सकते हैं जो विंडो मैनेजमेंट से जुड़ी सुविधाओं पर निर्भर होते हैं या उनकी पुष्टि करते हैं. जैसे, गतिविधि एम्बेड करना या फ़ोल्ड किए जा सकने वाले डिवाइस की सुविधाएं. यह आर्टफ़ैक्ट, Google's मेवन रिपॉज़िटरीपर उपलब्ध है.

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

Compose

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

अन्य संसाधन

दस्तावेज़

सैंपल

कोडलैब