सेल्फ़-इंस्ट्रूमेंटिंग टेस्ट का उदाहरण

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

इसका मतलब है कि इंस्ट्रुमेंटेशन टेस्ट, Android फ़्रेमवर्क यानी सिस्टम सर्वर में खुद को इंजेक्ट नहीं कर सकता, ताकि उसे एक्ज़ीक्यूट किया जा सके. Android फ़्रेमवर्क की जांच करने के लिए, टेस्ट कोड सिर्फ़ सार्वजनिक एपीआई सर्फ़ेस या Android इंटरफ़ेस डेफ़िनिशन लैंग्वेज AIDL का इस्तेमाल करके दिखाए गए सर्फ़ेस को लागू कर सकता है. ये सर्फ़ेस, प्लैटफ़ॉर्म सोर्स ट्री में उपलब्ध होते हैं. इस कैटगरी के टेस्ट के लिए, किसी खास पैकेज को टारगेट करना सही नहीं है. इसलिए, इस तरह के इंस्ट्रुमेंटेशन के लिए, अपने टेस्ट ऐप्लिकेशन पैकेज को टारगेट करने का एलान करना ज़रूरी है. ऐसा AndroidManifest.xml के <manifest> टैग में तय किए गए तरीके से किया जाता है.

ज़रूरी शर्तों के आधार पर, इस कैटगरी में टेस्ट ऐप्लिकेशन पैकेज भी शामिल हो सकते हैं:

  • जांच के लिए बंडल गतिविधियों की ज़रूरत होती है.
  • सिस्टम के साथ User-ID शेयर करें.
  • प्लैटफ़ॉर्म की कुंजी से साइन किया गया हो.
  • उसे सार्वजनिक SDK टूल के बजाय, फ़्रेमवर्क सोर्स के हिसाब से कंपाइल किया गया हो.

इंस्ट्रुमेंटेशन टेस्ट की इस कैटगरी को कभी-कभी सेल्फ-इंस्ट्रुमेंटेशन कहा जाता है. यहां प्लैटफ़ॉर्म सोर्स में, खुद से इंस्ट्रुमेंटेशन की गई जांचों के कुछ उदाहरण दिए गए हैं:

यहां दिए गए उदाहरण में, टारगेट पैकेज के साथ एक नया इंस्ट्रुमेंटेशन टेस्ट लिखा गया है. टारगेट पैकेज को उसके अपने टेस्ट ऐप्लिकेशन पैकेज पर सेट किया गया है. इस गाइड में, उदाहरण के तौर पर इस टेस्ट का इस्तेमाल किया गया है:

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

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

आम तौर पर, आपकी टीम के पास कोड में चेक इन करने की जगहों और टेस्ट जोड़ने की जगहों का पहले से तय पैटर्न होता है. ज़्यादातर टीमों के पास एक ही Git रिपॉज़िटरी होती है. इसके अलावा, वे इसे अन्य टीमों के साथ शेयर करती हैं. हालांकि, उनके पास एक अलग सब-डायरेक्ट्री होती है, जिसमें कॉम्पोनेंट का सोर्स कोड होता है.

मान लें कि आपके कॉम्पोनेंट सोर्स की रूट लोकेशन <component source root> पर है. ज़्यादातर कॉम्पोनेंट में इसके नीचे src और tests फ़ोल्डर होते हैं. साथ ही, कुछ अतिरिक्त फ़ाइलें होती हैं, जैसे कि Android.mk (या अतिरिक्त .mk फ़ाइलों में बांटा गया), मेनिफ़ेस्ट फ़ाइल AndroidManifest.xml, और टेस्ट कॉन्फ़िगरेशन फ़ाइल 'AndroidTest.xml'.

नया टेस्ट जोड़ने के लिए, आपको अपने कॉम्पोनेंट src के बगल में tests डायरेक्ट्री बनानी होगी और उसमें कॉन्टेंट जोड़ना होगा.

कुछ मामलों में, आपकी टीम को tests में और डायरेक्ट्री स्ट्रक्चर बनाने पड़ सकते हैं. ऐसा इसलिए, क्योंकि अलग-अलग टेस्ट के सुइट को अलग-अलग APK में पैकेज करने की ज़रूरत होती है. इस मामले में, आपको tests में एक नई सब-डायरेक्ट्री बनानी होगी.

स्ट्रक्चर कैसा भी हो, आपको tests डायरेक्ट्री या नई बनाई गई सब डायरेक्ट्री में, सैंपल gerrit बदलाव में मौजूद tests डायरेक्ट्री में मौजूद फ़ाइलों जैसी फ़ाइलें भरनी होंगी.instrumentation इस दस्तावेज़ में, हर फ़ाइल के बारे में बाद में बताया गया है.

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

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

अगर आपको AndroidManifest.xml फ़ाइल के बारे में जानकारी नहीं है, तो ऐप्लिकेशन मेनिफ़ेस्ट की खास जानकारी पढ़ें

यहां AndroidManifest.xml फ़ाइल का सैंपल दिया गया है:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
  android:sharedUserId="android.uid.system"
  package="android.test.example.helloworld" >

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

    <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
                     android:targetPackage="android.test.example.helloworld"
                     android:label="Hello World Test"/>

</manifest>

मेनिफ़ेस्ट फ़ाइल के बारे में कुछ ज़रूरी बातें:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="android.test.example.helloworld" >

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

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

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

android:sharedUserId="android.uid.system"

इससे यह पता चलता है कि इंस्टॉलेशन के समय, इस APK फ़ाइल को वही यूज़र आईडी यानी रनटाइम आइडेंटिटी दी जानी चाहिए जो कोर प्लैटफ़ॉर्म को दी गई है. ध्यान दें कि यह इस बात पर निर्भर करता है कि APK पर उसी सर्टिफ़िकेट से हस्ताक्षर किया गया हो जिससे कोर प्लैटफ़ॉर्म पर हस्ताक्षर किया गया है (पिछले सेक्शन में LOCAL_CERTIFICATE देखें). हालांकि, ये अलग-अलग कॉन्सेप्ट हैं:

  • कुछ अनुमतियां या एपीआई, हस्ताक्षर से सुरक्षित होते हैं. इसके लिए, एक ही हस्ताक्षर वाले सर्टिफ़िकेट की ज़रूरत होती है
  • कुछ अनुमतियों या एपीआई के लिए, कॉल करने वाले व्यक्ति की system उपयोगकर्ता पहचान की ज़रूरत होती है. इसके लिए, कॉल करने वाले पैकेज को system के साथ उपयोगकर्ता आईडी शेयर करना होता है. ऐसा तब करना होता है, जब यह कोर प्लैटफ़ॉर्म से अलग पैकेज हो
<uses-library android:name="android.test.runner" />

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

android:targetPackage="android.test.example.helloworld"

आपने शायद ध्यान दिया होगा कि यहां targetPackage को उसी तरह से तय किया गया है जिस तरह से इस फ़ाइल के manifest टैग में package एट्रिब्यूट को तय किया गया है. टेस्टिंग की बुनियादी बातें में बताया गया है कि इस कैटगरी के इंस्ट्रुमेंटेशन टेस्ट आम तौर पर टेस्टिंग फ़्रेमवर्क एपीआई के लिए होते हैं. इसलिए, इनके लिए खुद के अलावा किसी अन्य ऐप्लिकेशन पैकेज को टारगेट करना ज़्यादा मायने नहीं रखता.

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

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

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

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

टेस्ट कॉन्फ़िगरेशन में, डिवाइस सेटअप के खास विकल्प और टेस्ट क्लास को उपलब्ध कराने के लिए डिफ़ॉल्ट आर्ग्युमेंट तय किए जा सकते हैं. उदाहरण के लिए, /platform_testing/tests/example/instrumentation/AndroidTest.xml देखें.

आपकी सुविधा के लिए, यहां एक स्नैपशॉट शामिल किया गया है:

<configuration description="Runs sample instrumentation test.">
  <target_preparer class="com.android.tradefed.targetprep.TestFilePushSetup"/>
  <target_preparer class="com.android.tradefed.targetprep.TestAppInstallSetup">
    <option name="test-file-name" value="HelloWorldTests.apk"/>
  </target_preparer>
  <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer"/>
  <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer"/>
  <option name="test-suite-tag" value="apct"/>
  <option name="test-tag" value="SampleInstrumentationTest"/>

  <test class="com.android.tradefed.testtype.AndroidJUnitTest">
    <option name="package" value="android.test.example.helloworld"/>
    <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="HelloWorldTests.apk"/>
</target_preparer>

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

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

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

ज़्यादा जानकारी के लिए, मॉड्यूल कॉन्फ़िगरेशन की जांच करना लेख पढ़ें.

JUnit4 की सुविधाएं

android-support-test लाइब्रेरी को टेस्ट रनर के तौर पर इस्तेमाल करने से, JUnit4 स्टाइल की नई टेस्ट क्लास को अपनाया जा सकता है. साथ ही, सैंपल Gerrit में किए गए बदलाव में, इसकी सुविधाओं का बहुत बुनियादी इस्तेमाल शामिल है. /platform_testing/tests/example/instrumentation/src/android/test/example/helloworld/HelloWorldTest.java पर जाकर उदाहरण देखें.

टेस्टिंग के पैटर्न आम तौर पर कॉम्पोनेंट टीमों के हिसाब से तय किए जाते हैं. हालांकि, इस्तेमाल के कुछ पैटर्न ऐसे होते हैं जो आम तौर पर मददगार होते हैं.

@RunWith(JUnit4.class)
public class HelloWorldTest {

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

    @BeforeClass
    public static void beforeClass() {
    ...
    @AfterClass
    public static void afterClass() {
    ...
    @Before
    public void before() {
    ...
    @After
    public void after() {
    ...
    @Test
    @SmallTest
    public void testHelloWorld() {
    ...

JUnit4, @Before और @After एनोटेशन का इस्तेमाल, टेस्ट से पहले सेटअप करने और टेस्ट के बाद बंद करने के लिए करता है. इसी तरह, JUnit4 में @BeforeClass और @AfterClass एनोटेशन का इस्तेमाल, टेस्ट क्लास में सभी टेस्ट को लागू करने से पहले सेटअप करने और बाद में सेटअप हटाने के लिए किया जाता है. ध्यान दें कि क्लास-स्कोप सेटअप और टियरडाउन के तरीके स्टैटिक होने चाहिए. टेस्ट के तरीकों के बारे में बात करें, तो JUnit के पिछले वर्शन के उलट, अब उन्हें test से शुरू करने की ज़रूरत नहीं है. इसके बजाय, हर तरीके को @Test से एनोटेट किया जाना चाहिए. हमेशा की तरह, टेस्ट के तरीके सार्वजनिक होने चाहिए. साथ ही, उनमें कोई रिटर्न वैल्यू नहीं होनी चाहिए, कोई पैरामीटर नहीं होना चाहिए, और वे अपवादों को थ्रो कर सकते हैं.

इंस्ट्रूमेंटेशन क्लास का ऐक्सेस

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

JUnit4 टेस्ट के लिए अब किसी सामान्य बेस क्लास की ज़रूरत नहीं होती. इसलिए, Instrumentation इंस्टेंस को InstrumentationTestCase#getInstrumentation() के ज़रिए पाना अब ज़रूरी नहीं है. इसके बजाय, नया टेस्ट रनर इसे InstrumentationRegistry के ज़रिए मैनेज करता है. इसमें इंस्ट्रुमेंटेशन फ़्रेमवर्क की ओर से बनाया गया कॉन्टेक्स्ट और एनवायरमेंटल सेटअप सेव होता है.

Instrumentation क्लास के इंस्टेंस को ऐक्सेस करने के लिए, InstrumentationRegistry क्लास पर स्टैटिक मेथड getInstrumentation() को कॉल करें:

Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation()

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

सबसे ज़्यादा इस्तेमाल होने वाले उदाहरणों के लिए, Atest का इस्तेमाल करें.

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