किसी ऐप्लिकेशन को टारगेट करने का उदाहरण

इंस्ट्रूमेंटेशन टेस्ट की यह कैटगरी, सामान्य Android ऐप्लिकेशन को टारगेट करने वाले टेस्ट से ज़्यादा अलग नहीं है. यह ध्यान रखना ज़रूरी है कि इंस्ट्रूमेंटेशन वाले टेस्ट ऐप्लिकेशन पर, उसी सर्टिफ़िकेट से साइन किया जाना चाहिए जिससे टारगेट किए जा रहे ऐप्लिकेशन पर साइन किया गया है.

ध्यान दें कि इस गाइड में यह माना गया है कि आपको प्लैटफ़ॉर्म सोर्स ट्री के वर्कफ़्लो के बारे में पहले से कुछ जानकारी है. अगर ऐसा नहीं है, तो कृपया ज़रूरी शर्तें देखें. यहां दिए गए उदाहरण में, टारगेट पैकेज को अपने टेस्ट ऐप्लिकेशन पैकेज पर सेट करके, नया इंस्ट्रूमेंटेशन टेस्ट लिखा गया है. अगर आपको इस कॉन्सेप्ट के बारे में जानकारी नहीं है, तो कृपया प्लैटफ़ॉर्म टेस्टिंग का परिचय पढ़ें.

इस गाइड में, सैंपल के तौर पर यह टेस्ट इस्तेमाल किया गया है:

  • frameworks/base/packages/Shell/tests

आगे बढ़ने से पहले, हमारा सुझाव है कि आप कोड को ब्राउज़ करें, ताकि आपको इसकी सामान्य जानकारी मिल सके.

सोर्स की जगह तय करना

इंस्ट्रूमेंटेशन टेस्ट, किसी ऐप्लिकेशन को टारगेट करेगा. इसलिए, टेस्ट के सोर्स कोड को प्लैटफ़ॉर्म सोर्स ट्री में, कॉम्पोनेंट सोर्स डायरेक्ट्री के रूट में मौजूद tests डायरेक्ट्री में रखने का तरीका अपनाया जाता है.

सेल्फ़-इंस्ट्रूमेंटिंग टेस्ट के लिए, एंड-टू-एंड उदाहरण में सोर्स की जगह के बारे में ज़्यादा चर्चाएं देखें.

मेनिफ़ेस्ट फ़ाइल

सामान्य ऐप्लिकेशन की तरह, हर इंस्ट्रूमेंटेशन टेस्ट मॉड्यूल के लिए एक मेनिफ़ेस्ट फ़ाइल की ज़रूरत होती है. अगर फ़ाइल का नाम AndroidManifest.xml रखा जाता है और इसे अपने टेस्ट मॉड्यूल के लिए Android.mk के बगल में रखा जाता है, तो यह BUILD_PACKAGE कोर मेकफ़ाइल में अपने-आप शामिल हो जाएगी.

आगे बढ़ने से पहले, हमारा सुझाव है कि आप सबसे पहले ऐप्लिकेशन मेनिफ़ेस्ट की खास जानकारी देखें.

इससे, मेनिफ़ेस्ट फ़ाइल के बुनियादी कॉम्पोनेंट और उनकी सुविधाओं की खास जानकारी मिलती है.

सैंपल Gerrit में किए गए बदलाव के लिए, मेनिफ़ेस्ट फ़ाइल का सबसे नया वर्शन यहां ऐक्सेस किया जा सकता है: https://android.googlesource.com/platform/frameworks/base/+/android17-release/packages/Shell/tests/AndroidManifest.xml

यहां एक स्नैपशॉट शामिल किया गया है:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.android.shell.tests">

    <application>
        <uses-library android:name="android.test.runner" />

        <activity
            android:name="com.android.shell.ActionSendMultipleConsumerActivity"
            android:label="ActionSendMultipleConsumer"
            android:theme="@android:style/Theme.NoDisplay"
            android:noHistory="true"
            android:excludeFromRecents="true">
            <intent-filter>
                <action android:name="android.intent.action.SEND_MULTIPLE" />
                <category android:name="android.intent.category.DEFAULT" />
                <data android:mimeType="*/*" />
            </intent-filter>
        </activity>
    </application>

    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
        android:targetPackage="com.android.shell"
        android:label="Tests for Shell" />

</manifest>

मेनिफ़ेस्ट फ़ाइल पर कुछ चुनिंदा टिप्पणियां:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.android.shell.tests">

package एट्रिब्यूट, ऐप्लिकेशन पैकेज का नाम होता है. यह यूनीक आइडेंटिफ़ायर है, जिसका इस्तेमाल Android ऐप्लिकेशन फ़्रेमवर्क, किसी ऐप्लिकेशन की पहचान करने के लिए करता है. इस संदर्भ में, यह आपके टेस्ट ऐप्लिकेशन की पहचान करने के लिए इस्तेमाल किया जाता है. सिस्टम में मौजूद हर उपयोगकर्ता, उस पैकेज के नाम वाला सिर्फ़ एक ऐप्लिकेशन इंस्टॉल कर सकता है.

यह टेस्ट ऐप्लिकेशन पैकेज है. यह टेस्ट किए जा रहे ऐप्लिकेशन पैकेज से अलग है. इसलिए, पैकेज का कोई दूसरा नाम इस्तेमाल करना ज़रूरी है. आम तौर पर, .test सफ़िक्स जोड़ा जाता है.

इसके अलावा, यह package एट्रिब्यूट, ComponentName#getPackageName() से मिलने वाली वैल्यू के बराबर होता है. साथ ही, यह उस वैल्यू के बराबर भी होता है जिसका इस्तेमाल adb shell के ज़रिए, pm की अलग-अलग सब कमांड के साथ इंटरैक्ट करने के लिए किया जाता है.

कृपया यह भी ध्यान रखें कि आम तौर पर, पैकेज का नाम Java पैकेज के नाम के जैसा होता है. हालांकि, इसका Java पैकेज के नाम से बहुत कम लेना-देना होता है. दूसरे शब्दों में, आपके ऐप्लिकेशन (या टेस्ट) पैकेज में, किसी भी पैकेज के नाम वाली क्लास शामिल हो सकती हैं. हालांकि, दूसरी ओर, आसानी के लिए, आपके ऐप्लिकेशन या टेस्ट में, टॉप लेवल के Java पैकेज का नाम, ऐप्लिकेशन पैकेज के नाम जैसा हो सकता है.

<uses-library android:name="android.test.runner" />

यह सभी इंस्ट्रूमेंटेशन टेस्ट के लिए ज़रूरी है, क्योंकि इससे जुड़ी क्लास को अलग फ़्रेमवर्क जार लाइब्रेरी फ़ाइल में पैकेज किया जाता है. इसलिए, जब टेस्ट पैकेज को ऐप्लिकेशन फ़्रेमवर्क से लागू किया जाता है, तो इसके लिए अतिरिक्त क्लासपाथ एंट्री की ज़रूरत होती है.

android:targetPackage="com.android.shell"

इससे, इंस्ट्रूमेंटेशन का टारगेट पैकेज com.android.shell पर सेट हो जाता है. जब am instrument कमांड के ज़रिए इंस्ट्रूमेंटेशन को लागू किया जाता है, तो फ़्रेमवर्क com.android.shell प्रोसेस को रीस्टार्ट करता है. साथ ही, टेस्ट को लागू करने के लिए, प्रोसेस में इंस्ट्रूमेंटेशन कोड डालता है. इसका मतलब यह भी है कि टेस्ट कोड के पास, टेस्ट किए जा रहे ऐप्लिकेशन में चल रहे सभी क्लास इंस्टेंस का ऐक्सेस होगा. साथ ही, यह टेस्ट हुक के आधार पर, स्टेट में बदलाव कर सकता है.

आसान कॉन्फ़िगरेशन फ़ाइल

हर नए टेस्ट मॉड्यूल में एक कॉन्फ़िगरेशन फ़ाइल होनी चाहिए. इससे, बिल्ड सिस्टम को मॉड्यूल के मेटाडेटा, कंपाइल-टाइम डिपेंडेंसी, और पैकेजिंग के निर्देशों के बारे में जानकारी मिलती है. ज़्यादातर मामलों में, Soong पर आधारित, ब्लूप्रिंट फ़ाइल का विकल्प काफ़ी होता है. ज़्यादा जानकारी के लिए, टेस्ट का आसान कॉन्फ़िगरेशन देखें.

जटिल कॉन्फ़िगरेशन फ़ाइल

ज़्यादा जटिल टेस्ट के लिए, आपको Android के टेस्ट हार्नेस, Trade Federation के लिए टेस्ट कॉन्फ़िगरेशन फ़ाइल भी लिखनी होगी.

टेस्ट कॉन्फ़िगरेशन में, डिवाइस के खास सेटअप के विकल्प और टेस्ट क्लास को सप्लाई करने के लिए डिफ़ॉल्ट आर्ग्युमेंट तय किए जा सकते हैं.

सैंपल Gerrit में किए गए बदलाव के लिए, कॉन्फ़िगरेशन फ़ाइल का सबसे नया वर्शन यहां ऐक्सेस किया जा सकता है: यहां: frameworks/base/packages/Shell/tests/AndroidTest.xml

यहां एक स्नैपशॉट शामिल किया गया है:

<configuration description="Runs Tests for Shell.">
    <target_preparer class="com.android.tradefed.targetprep.TestAppInstallSetup">
        <option name="test-file-name" value="ShellTests.apk" />
    </target_preparer>

    <option name="test-suite-tag" value="apct" />
    <option name="test-tag" value="ShellTests" />
    <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
        <option name="package" value="com.android.shell.tests" />
        <option name="runner" value="android.support.test.runner.AndroidJUnitRunner" />
    </test>
</configuration>

टेस्ट कॉन्फ़िगरेशन फ़ाइल पर कुछ चुनिंदा टिप्पणियां:

<target_preparer class="com.android.tradefed.targetprep.TestAppInstallSetup">
  <option name="test-file-name" value="ShellTests.apk"/>
</target_preparer>

इससे, Trade Federation को यह निर्देश मिलता है कि वह तय किए गए target_preparer का इस्तेमाल करके, टारगेट डिवाइस पर ShellTests.apk इंस्टॉल करे. Trade Federation में, डेवलपर के लिए कई टारगेट प्रिपेरर उपलब्ध हैं. इनका इस्तेमाल, टेस्ट को लागू करने से पहले यह पक्का करने के लिए किया जा सकता है कि डिवाइस को सही तरीके से सेटअप किया गया है.

<test class="com.android.tradefed.testtype.AndroidJUnitTest">
  <option name="package" value="com.android.shell.tests"/>
  <option name="runner" value="android.support.test.runner.AndroidJUnitRunner"/>
</test>

इससे, टेस्ट को लागू करने के लिए, Trade Federation की टेस्ट क्लास तय की जाती है. साथ ही, डिवाइस पर लागू किए जाने वाले पैकेज और टेस्ट रनर फ़्रेमवर्क को पास किया जाता है. इस मामले में, यह JUnit है.

टेस्ट मॉड्यूल के कॉन्फ़िगरेशन के बारे में ज़्यादा जानकारी के लिए यहां देखें

JUnit4 की सुविधाएं

टेस्ट रनर के तौर पर android-support-test लाइब्रेरी का इस्तेमाल करने से, JUnit4 स्टाइल की नई टेस्ट क्लास को अपनाया जा सकता है. साथ ही, सैंपल Gerrit में किए गए बदलाव में, इसकी सुविधाओं का बहुत बुनियादी इस्तेमाल शामिल है.

सैंपल Gerrit में किए गए बदलाव के लिए, सोर्स कोड का सबसे नया वर्शन यहां ऐक्सेस किया जा सकता है: frameworks/base/packages/Shell/tests/src/com/android/shell/BugreportReceiverTest.java

टेस्टिंग के पैटर्न आम तौर पर कॉम्पोनेंट टीमों के हिसाब से अलग-अलग होते हैं. हालांकि, कुछ पैटर्न आम तौर पर काम के होते हैं.

@SmallTest
@RunWith(AndroidJUnit4.class)
public final class FeatureFactoryImplTest {

JUnit4 में एक अहम अंतर यह है कि टेस्ट के लिए, अब किसी सामान्य बेस टेस्ट क्लास से इनहेरिट करने की ज़रूरत नहीं होती. इसके बजाय, टेस्ट को सामान्य Java क्लास में लिखा जाता है. साथ ही, टेस्ट के सेटअप और कंस्ट्रेंट के बारे में बताने के लिए, एनोटेशन का इस्तेमाल किया जाता है. इस उदाहरण में, हम यह निर्देश दे रहे हैं कि इस क्लास को Android JUnit4 टेस्ट के तौर पर चलाया जाना चाहिए.

@SmallTest एनोटेशन में, पूरी टेस्ट क्लास के लिए टेस्ट का साइज़ तय किया गया है. इस टेस्ट क्लास में जोड़े गए सभी टेस्ट के तरीकों में, टेस्ट के साइज़ का यह एनोटेशन इनहेरिट होता है. टेस्ट क्लास के सेटअप से पहले, टेस्ट के बाद, और टेस्ट क्लास के बाद, सेटअप को हटाना: यह JUnit4 में setUp और tearDown तरीकों के जैसा है. Test एनोटेशन का इस्तेमाल, असल टेस्ट को एनोटेट करने के लिए किया जाता है.

    @Before
    public void setup() {
    ...
    @Test
    public void testGetProvider_shouldCacheProvider() {
    ...

JUnit4 में, @Before एनोटेशन का इस्तेमाल, टेस्ट के सेटअप से पहले के लिए किया जाता है. इस उदाहरण में इसका इस्तेमाल नहीं किया गया है. हालांकि, टेस्ट के बाद सेटअप को हटाने के लिए, @After का भी इस्तेमाल किया जा सकता है. इसी तरह, JUnit4 में, @BeforeClass और @AfterClass एनोटेशन का इस्तेमाल, टेस्ट क्लास में सभी टेस्ट को लागू करने से पहले सेटअप करने और बाद में सेटअप को हटाने के लिए किया जा सकता है. ध्यान दें कि क्लास-स्कोप के सेटअप और सेटअप को हटाने के तरीकों को स्टैटिक होना चाहिए.

टेस्ट के तरीकों के लिए, JUnit के पुराने वर्शन के उलट, अब तरीके के नाम की शुरुआत test से करने की ज़रूरत नहीं है. इसके बजाय, इनमें से हर तरीके को @Test से एनोटेट करना ज़रूरी है. आम तौर पर, टेस्ट के तरीके सार्वजनिक होने चाहिए. इनमें कोई रिटर्न वैल्यू नहीं होनी चाहिए, कोई पैरामीटर नहीं होना चाहिए, और इनमें अपवाद हो सकते हैं.

        Context context = InstrumentationRegistry.getTargetContext();

JUnit4 टेस्ट के लिए, अब किसी सामान्य बेस क्लास की ज़रूरत नहीं होती. इसलिए, बेस क्लास के तरीकों के ज़रिए, Context या getTargetContext() से इंस्टेंस पाने की ज़रूरत नहीं होती. इसके बजाय, नया टेस्ट रनर InstrumentationRegistry के ज़रिए इन्हें मैनेज करता है. इसमें, इंस्ट्रूमेंटेशन फ़्रेमवर्क से बनाया गया कॉन्टेक्चुअल और एनवायरमेंटल सेटअप सेव होता है.getContext() इस क्लास के ज़रिए, ये कॉल भी किए जा सकते हैं:

  • getInstrumentation(): Instrumentation क्लास का इंस्टेंस
  • getArguments(): -e <key> <value> के ज़रिए, am instrument को पास किए गए कमांड लाइन आर्ग्युमेंट

स्थानीय तौर पर बनाना और टेस्ट करना

आम तौर पर इस्तेमाल किए जाने वाले मामलों के लिए, Atestका इस्तेमाल करें.

ज़्यादा जटिल मामलों के लिए, जिनमें ज़्यादा बदलाव करने की ज़रूरत होती है, इंस्ट्रूमेंटेशन के निर्देशों का पालन करें.