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

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

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

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

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

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

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

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

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

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

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

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

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

भले ही, स्ट्रक्चर कुछ भी हो, आपको tests डायरेक्ट्री या बनाई गई नई सब-डायरेक्ट्री में, उन फ़ाइलों को भरना होगा जो सैंपल gerrit बदलाव में 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() दिखाता है. साथ ही, इसका इस्तेमाल adb shell का इस्तेमाल करके, अलग-अलग pm सब-कमांड के साथ इंटरैक्ट करने के लिए किया जाता है.

ध्यान दें कि आम तौर पर पैकेज का नाम उसी स्टाइल में होता है जैसा 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 को, किसी तय target_preparer का इस्तेमाल करके, टारगेट डिवाइस पर HelloWorldTests.apk इंस्टॉल करने के लिए कहा जाता है. 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 में एक अहम अंतर यह है कि अब टेस्ट को किसी सामान्य बेस टेस्ट क्लास से इनहेरिट करने की ज़रूरत नहीं है. इसके बजाय, टेस्ट को साधारण 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() {
    ...

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

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

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

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

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

Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation()

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

आम तौर पर, इस्तेमाल के उदाहरणों के लिए, Atest इस्तेमाल करें.

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