टेस्टिंग के बारे में बुनियादी जानकारी

यह कोडलैब, Kotlin में ऐडवांस Android कोर्स का हिस्सा है. अगर इस कोर्स के कोडलैब को क्रम से पूरा किया जाता है, तो आपको सबसे ज़्यादा फ़ायदा मिलेगा. हालांकि, ऐसा करना ज़रूरी नहीं है. कोर्स के सभी कोडलब, Advanced Android in Kotlin कोडलब के लैंडिंग पेज पर दिए गए हैं.

परिचय

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

कंप्यूटर, स्केल करने और ऑटोमेशन के लिए बेहतरीन होते हैं! इसलिए, छोटी और बड़ी कंपनियों के डेवलपर ऑटोमेटेड टेस्ट लिखते हैं. ये ऐसे टेस्ट होते हैं जिन्हें सॉफ़्टवेयर चलाता है. इनमें कोड के काम करने की पुष्टि करने के लिए, आपको ऐप्लिकेशन को मैन्युअल तरीके से चलाने की ज़रूरत नहीं होती.

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

इस पहले कोडलैब में, Android पर टेस्टिंग की बुनियादी बातों के बारे में बताया गया है. इसमें, आपको अपने पहले टेस्ट लिखने और LiveData और ViewModel की टेस्टिंग करने का तरीका जानने को मिलेगा.

आपको पहले से क्या पता होना चाहिए

आपको इनके बारे में जानकारी होनी चाहिए:

आपको क्या सीखने को मिलेगा

आपको इन विषयों के बारे में जानकारी मिलेगी:

  • Android पर यूनिट टेस्ट कैसे लिखें और चलाएं
  • टेस्ट ड्रिवन डेवलपमेंट का इस्तेमाल कैसे करें
  • इंस्ट्रुमेंट किए गए टेस्ट और लोकल टेस्ट चुनने का तरीका

आपको यहां दी गई लाइब्रेरी और कोड के कॉन्सेप्ट के बारे में जानकारी मिलेगी:

आपको क्या करना होगा

  • Android में लोकल और इंस्ट्रूमेंटेड, दोनों तरह के टेस्ट सेट अप करें, उन्हें चलाएं, और उनके नतीजों का विश्लेषण करें.
  • JUnit4 और Hamcrest का इस्तेमाल करके, Android में यूनिट टेस्ट लिखें.
  • आसान LiveData और ViewModel टेस्ट लिखें.

इस कोडलैब सीरीज़ में, आपको TO-DO Notes ऐप्लिकेशन के साथ काम करना होगा. इस ऐप्लिकेशन की मदद से, पूरे किए जाने वाले टास्क लिखे जा सकते हैं. साथ ही, उन्हें सूची में दिखाया जा सकता है. इसके बाद, उन्हें 'पूरा हो गया' या 'पूरा नहीं हुआ' के तौर पर मार्क किया जा सकता है, फ़िल्टर किया जा सकता है या मिटाया जा सकता है.

यह ऐप्लिकेशन Kotlin में लिखा गया है. इसमें कई स्क्रीन हैं. यह Jetpack कॉम्पोनेंट का इस्तेमाल करता है और ऐप्लिकेशन के आर्किटेक्चर के लिए गाइड में दिए गए आर्किटेक्चर का पालन करता है. इस ऐप्लिकेशन की जांच करने का तरीका जानने के बाद, उन ऐप्लिकेशन की जांच की जा सकती है जो एक ही लाइब्रेरी और आर्किटेक्चर का इस्तेमाल करते हैं.

शुरू करने के लिए, कोड डाउनलोड करें:

ज़िप फ़ाइल डाउनलोड करें

इसके अलावा, कोड के लिए Github रिपॉज़िटरी को क्लोन किया जा सकता है:

$ git clone https://github.com/googlecodelabs/android-testing.git
$ cd android-testing
$ git checkout starter_code

इस टास्क में, आपको ऐप्लिकेशन चलाना होगा और कोड बेस को एक्सप्लोर करना होगा.

पहला चरण: सैंपल ऐप्लिकेशन चलाना

TO-DO ऐप्लिकेशन डाउनलोड करने के बाद, इसे Android Studio में खोलें और चलाएं. इसे कंपाइल किया जाना चाहिए. ऐप्लिकेशन को एक्सप्लोर करने के लिए, यह तरीका अपनाएं:

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

दूसरा चरण: सैंपल ऐप्लिकेशन का कोड एक्सप्लोर करना

TO-DO ऐप्लिकेशन, लोकप्रिय आर्किटेक्चर ब्लूप्रिंट टेस्टिंग और आर्किटेक्चर सैंपल पर आधारित है. इसमें सैंपल के रिएक्टिव आर्किटेक्चर वर्शन का इस्तेमाल किया गया है. ऐप्लिकेशन, ऐप्लिकेशन के आर्किटेक्चर की गाइड में दिए गए आर्किटेक्चर का पालन करता है. यह फ़्रैगमेंट, रिपॉज़िटरी, और Room के साथ ViewModels का इस्तेमाल करता है. अगर आपने नीचे दिए गए किसी उदाहरण का इस्तेमाल किया है, तो इस ऐप्लिकेशन का आर्किटेक्चर भी ऐसा ही है:

किसी एक लेयर के लॉजिक को गहराई से समझने के बजाय, ऐप्लिकेशन के सामान्य आर्किटेक्चर को समझना ज़्यादा ज़रूरी है.

यहां आपको मिलने वाले पैकेज की खास जानकारी दी गई है:

पैकेज: com.example.android.architecture.blueprints.todoapp

.addedittask

टास्क जोड़ने या उसमें बदलाव करने वाली स्क्रीन: टास्क जोड़ने या उसमें बदलाव करने के लिए यूज़र इंटरफ़ेस (यूआई) लेयर का कोड.

.data

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

.statistics

आंकड़ों की स्क्रीन: आंकड़ों की स्क्रीन के लिए यूज़र इंटरफ़ेस लेयर का कोड.

.taskdetail

टास्क की जानकारी वाली स्क्रीन: किसी एक टास्क के लिए यूज़र इंटरफ़ेस (यूआई) लेयर का कोड.

.tasks

टास्क स्क्रीन: सभी टास्क की सूची के लिए यूज़र इंटरफ़ेस (यूआई) लेयर कोड.

.util

यूटिलिटी क्लास: ये ऐसी क्लास होती हैं जिनका इस्तेमाल ऐप्लिकेशन के अलग-अलग हिस्सों में किया जाता है. उदाहरण के लिए, कई स्क्रीन पर इस्तेमाल किए जाने वाले स्वाइप रिफ़्रेश लेआउट के लिए.

डेटा लेयर (.data)

इस ऐप्लिकेशन में, remote पैकेज में एक सिम्युलेटेड नेटवर्किंग लेयर और local पैकेज में एक डेटाबेस लेयर शामिल है. आसानी के लिए, इस प्रोजेक्ट में नेटवर्किंग लेयर को सिर्फ़ HashMap के साथ सिम्युलेट किया गया है. इसमें असली नेटवर्क अनुरोधों के बजाय, कुछ समय के लिए देरी की जाती है.

DefaultTasksRepository नेटवर्किंग लेयर और डेटाबेस लेयर के बीच तालमेल बिठाता है. साथ ही, यह यूज़र इंटरफ़ेस (यूआई) लेयर को डेटा दिखाता है.

यूज़र इंटरफ़ेस (यूआई) लेयर ( .addedittask, .statistics, .taskdetail, .tasks)

यूज़र इंटरफ़ेस (यूआई) लेयर के हर पैकेज में एक फ़्रैगमेंट और एक व्यू मॉडल होता है. साथ ही, यूज़र इंटरफ़ेस (यूआई) के लिए ज़रूरी अन्य क्लास भी होती हैं. जैसे, टास्क लिस्ट के लिए अडैप्टर. TaskActivity एक ऐसी गतिविधि है जिसमें सभी फ़्रैगमेंट शामिल होते हैं.

नेविगेशन

ऐप्लिकेशन के नेविगेशन को नेविगेशन कॉम्पोनेंट से कंट्रोल किया जाता है. इसे nav_graph.xml फ़ाइल में तय किया जाता है. Event क्लास का इस्तेमाल करके, व्यू मॉडल में नेविगेशन ट्रिगर किया जाता है. व्यू मॉडल यह भी तय करते हैं कि कौनसे आर्ग्युमेंट पास करने हैं. फ़्रैगमेंट, Event को देखते हैं और स्क्रीन के बीच नेविगेशन की सुविधा देते हैं.

इस टास्क में, आपको पहली बार टेस्ट चलाने होंगे.

  1. Android Studio में, Project पैनल खोलें और इन तीन फ़ोल्डर को ढूंढें:
  • com.example.android.architecture.blueprints.todoapp
  • com.example.android.architecture.blueprints.todoapp (androidTest)
  • com.example.android.architecture.blueprints.todoapp (test)

इन फ़ोल्डर को सोर्स सेट कहा जाता है. सोर्स सेट ऐसे फ़ोल्डर होते हैं जिनमें आपके ऐप्लिकेशन का सोर्स कोड होता है. हरे रंग (androidTest और test) वाले सोर्स सेट में, आपके टेस्ट शामिल होते हैं. कोई नया Android प्रोजेक्ट बनाने पर, आपको डिफ़ॉल्ट रूप से ये तीन सोर्स सेट मिलते हैं. कैंपेन के तीनों सब-टाइप के नाम ये रहे:

  • main: इसमें आपके ऐप्लिकेशन का कोड होता है. यह कोड, ऐप्लिकेशन के उन सभी वर्शन के साथ शेयर किया जाता है जिन्हें बनाया जा सकता है. इन्हें बिल्ड वैरिएंट कहा जाता है
  • androidTest: इसमें इंस्ट्रुमेंटेड टेस्ट शामिल होते हैं.
  • test: इसमें लोकल टेस्ट शामिल होते हैं.

लोकल टेस्ट और इंस्ट्रुमेंटेड टेस्ट के बीच का अंतर, उन्हें चलाने के तरीके में होता है.

स्थानीय टेस्ट (test सोर्स सेट)

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

Android Studio में, स्थानीय जांचों को हरे और लाल रंग के ट्राइएंगल आइकॉन से दिखाया जाता है.

इंस्ट्रुमेंट किए गए टेस्ट (androidTest सोर्स सेट)

ये टेस्ट, असली या नकली Android डिवाइसों पर चलते हैं. इसलिए, इनसे पता चलता है कि असल ज़िंदगी में क्या होगा. हालांकि, ये टेस्ट बहुत धीरे होते हैं.

Android Studio में, इंस्ट्रुमेंटेड टेस्ट को हरे और लाल रंग के ट्राइएंगल आइकॉन वाले Android से दिखाया जाता है.

पहला चरण: स्थानीय जांच करना

  1. test फ़ोल्डर तब तक खोलें, जब तक आपको ExampleUnitTest.kt फ़ाइल न मिल जाए.
  2. इस पर राइट क्लिक करें और Run ExampleUnitTest को चुनें.

आपको स्क्रीन पर सबसे नीचे मौजूद Run विंडो में यह आउटपुट दिखेगा:

  1. हरे रंग के सही के निशान देखें और टेस्ट के नतीजों को बड़ा करके देखें. इससे पुष्टि करें कि addition_isCorrect नाम का एक टेस्ट पास हो गया है. हमें यह जानकर खुशी हुई कि जोड़ें सुविधा उम्मीद के मुताबिक काम कर रही है!

दूसरा चरण: टेस्ट को फ़ेल करना

नीचे, अभी-अभी किया गया टेस्ट दिया गया है.

ExampleUnitTest.kt

// A test class is just a normal class
class ExampleUnitTest {

   // Each test is annotated with @Test (this is a Junit annotation)
   @Test
   fun addition_isCorrect() {
       // Here you are checking that 4 is the same as 2+2
       assertEquals(4, 2 + 2)
   }
}

ध्यान दें कि टेस्ट

  • टेस्ट सोर्स सेट में से किसी एक क्लास में हों.
  • इनमें ऐसे फ़ंक्शन शामिल होते हैं जो @Test एनोटेशन से शुरू होते हैं. हर फ़ंक्शन एक टेस्ट होता है.
  • इनमें आम तौर पर, पुष्टि करने वाले स्टेटमेंट शामिल होते हैं.

Android, टेस्टिंग के लिए JUnit टेस्टिंग लाइब्रेरी का इस्तेमाल करता है. इस कोडलैब में JUnit4 का इस्तेमाल किया गया है. Assertion और @Test एनोटेशन, दोनों JUnit से मिलते हैं.

Assertion, आपके टेस्ट का मुख्य हिस्सा होता है. यह एक कोड स्टेटमेंट है. इससे यह पता चलता है कि आपका कोड या ऐप्लिकेशन, उम्मीद के मुताबिक काम कर रहा है या नहीं. इस मामले में, assertEquals(4, 2 + 2) एक दावा है. यह जांच करता है कि 4, 2 + 2 के बराबर है या नहीं.

यह देखने के लिए कि टेस्ट फ़ेल होने पर कैसा दिखता है, एक ऐसा दावा जोड़ें जो आसानी से फ़ेल हो जाए. इससे यह पता चलेगा कि 3, 1+1 के बराबर है या नहीं.

  1. addition_isCorrect टेस्ट में assertEquals(3, 1 + 1) जोड़ें.

ExampleUnitTest.kt

class ExampleUnitTest {

   // Each test is annotated with @Test (this is a Junit annotation)
   @Test
   fun addition_isCorrect() {
       assertEquals(4, 2 + 2)
       assertEquals(3, 1 + 1) // This should fail
   }
}
  1. जांच करें.
  1. जांच के नतीजों में, जांच के बगल में मौजूद X पर ध्यान दें.

  1. यह भी ध्यान दें:
  • एक भी पुष्टि पूरी न होने पर, पूरा टेस्ट फ़ेल हो जाता है.
  • आपको अनुमानित वैल्यू (3) और असल में कैलकुलेट की गई वैल्यू (2) के बारे में बताया जाता है.
  • आपको उस लाइन पर ले जाया जाता है जहां (ExampleUnitTest.kt:16) की पुष्टि नहीं हो पाई.

तीसरा चरण: इंस्ट्रुमेंट किया गया टेस्ट चलाना

इंस्ट्रुमेंट किए गए टेस्ट, androidTest सोर्स सेट में हैं.

  1. androidTest सोर्स सेट खोलें.
  2. ExampleInstrumentedTest नाम की जांच करें.

ExampleInstrumentedTest

@RunWith(AndroidJUnit4::class)
class ExampleInstrumentedTest {
    @Test
    fun useAppContext() {
        // Context of the app under test.
        val appContext = InstrumentationRegistry.getInstrumentation().targetContext
        assertEquals("com.example.android.architecture.blueprints.reactive",
            appContext.packageName)
    }
}

लोकल टेस्ट के उलट, यह टेस्ट किसी डिवाइस पर चलता है. यहां दिए गए उदाहरण में, यह Pixel 2 फ़ोन पर चल रहा है:

अगर आपने कोई डिवाइस अटैच किया है या एम्युलेटर चालू है, तो आपको एम्युलेटर पर टेस्ट रन दिखेगा.

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

पहला चरण: टेस्ट क्लास बनाना

  1. main सोर्स सेट में, todoapp.statistics में जाकर StatisticsUtils.kt खोलें.
  2. getActiveAndCompletedStats फ़ंक्शन ढूंढें.

StatisticsUtils.kt

internal fun getActiveAndCompletedStats(tasks: List<Task>?): StatsResult {

   val totalTasks = tasks!!.size
   val numberOfActiveTasks = tasks.count { it.isActive }
   val activePercent = 100 * numberOfActiveTasks / totalTasks
   val completePercent = 100 * (totalTasks - numberOfActiveTasks) / totalTasks

   return StatsResult(
       activeTasksPercent = activePercent.toFloat(),
       completedTasksPercent = completePercent.toFloat()
   )
  
}

data class StatsResult(val activeTasksPercent: Float, val completedTasksPercent: Float)

getActiveAndCompletedStats फ़ंक्शन, टास्क की सूची को स्वीकार करता है और StatsResult दिखाता है. StatsResult एक डेटा क्लास है. इसमें दो नंबर होते हैं. पहला नंबर, पूरे किए गए टास्क का प्रतिशत होता है. वहीं, दूसरा नंबर, चालू टास्क का प्रतिशत होता है.

Android Studio में ऐसे टूल उपलब्ध हैं जिनकी मदद से टेस्ट स्टब जनरेट किए जा सकते हैं. इससे आपको इस फ़ंक्शन के लिए टेस्ट लागू करने में मदद मिलती है.

  1. getActiveAndCompletedStats पर राइट क्लिक करें. इसके बाद, जनरेट करें > टेस्ट करें को चुनें.

जांच बनाएं डायलॉग बॉक्स खुलता है:

  1. क्लास का नाम: को StatisticsUtilsTest में बदलें (StatisticsUtilsKtTest के बजाय; टेस्ट क्लास के नाम में KT न होना थोड़ा बेहतर है).
  2. बाकी डिफ़ॉल्ट सेटिंग को बनाए रखें. JUnit 4, टेस्टिंग के लिए सही लाइब्रेरी है. डेस्टिनेशन पैकेज सही है (यह StatisticsUtils क्लास की जगह के हिसाब से है). साथ ही, आपको किसी भी चेक बॉक्स को चुनने की ज़रूरत नहीं है. इससे सिर्फ़ अतिरिक्त कोड जनरेट होता है. हालांकि, आपको अपना टेस्ट शुरू से लिखना होगा.
  3. ठीक है दबाएं

इसके बाद, डेस्टिनेशन डायरेक्ट्री चुनें डायलॉग बॉक्स खुलता है:

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

  1. test डायरेक्ट्री (androidTest नहीं) चुनें, क्योंकि आपको स्थानीय टेस्ट लिखने होंगे.
  2. ठीक है पर क्लिक करें.
  3. test/statistics/ में जनरेट की गई StatisticsUtilsTest क्लास पर ध्यान दें.

दूसरा चरण: अपना पहला टेस्ट फ़ंक्शन लिखना

आपको एक ऐसा टेस्ट लिखना है जो इन बातों की जांच करे:

  • अगर कोई भी टास्क पूरा नहीं हुआ है और एक टास्क चालू है, तो
  • कि चालू टेस्ट का प्रतिशत 100% हो,
  • और पूरे किए गए टास्क का प्रतिशत 0% है.
  1. StatisticsUtilsTest खोलें.
  2. getActiveAndCompletedStats_noCompleted_returnsHundredZero नाम का फ़ंक्शन बनाया गया.

StatisticsUtilsTest.kt

class StatisticsUtilsTest {

    fun getActiveAndCompletedStats_noCompleted_returnsHundredZero() {
        // Create an active task

        // Call your function

        // Check the result
    }
}
  1. फ़ंक्शन के नाम के ऊपर @Test एनोटेशन जोड़ें, ताकि यह पता चल सके कि यह एक टेस्ट है.
  2. टास्क की सूची बनाएं.
// Create an active task 
val tasks = listOf<Task>(
            Task("title", "desc", isCompleted = false)
        )
  1. इन टास्क के लिए, getActiveAndCompletedStats को कॉल करें.
// Call your function
val result = getActiveAndCompletedStats(tasks)
  1. पुष्टि करें कि result एट्रिब्यूट की वैल्यू वही है जो आपने तय की थी. इसके लिए, असर्शन का इस्तेमाल करें.
// Check the result
assertEquals(result.completedTasksPercent, 0f)
assertEquals(result.activeTasksPercent, 100f)

यहां पूरा कोड दिया गया है.

StatisticsUtilsTest.kt

class StatisticsUtilsTest {

    @Test
    fun getActiveAndCompletedStats_noCompleted_returnsHundredZero() {

        // Create an active task (the false makes this active)
        val tasks = listOf<Task>(
            Task("title", "desc", isCompleted = false)
        )
        // Call your function
        val result = getActiveAndCompletedStats(tasks)

        // Check the result
        assertEquals(result.completedTasksPercent, 0f)
        assertEquals(result.activeTasksPercent, 100f)
    }
}
  1. जांच चलाएं (StatisticsUtilsTest पर राइट क्लिक करें और चलाएं चुनें).

यह पास होना चाहिए:

तीसरा चरण: Hamcrest डिपेंडेंसी जोड़ना

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

assertEquals(result.completedTasksPercent, 0f)

// versus

assertThat(result.completedTasksPercent, `is`(0f))

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

  1. build.grade (Module: app) खोलें और यह डिपेंडेंसी जोड़ें.

app/build.gradle

dependencies {
    // Other dependencies
    testImplementation "org.hamcrest:hamcrest-all:$hamcrestVersion"
}

आम तौर पर, किसी डिपेंडेंसी को जोड़ते समय implementation का इस्तेमाल किया जाता है. हालांकि, यहां testImplementation का इस्तेमाल किया जा रहा है. जब आपका ऐप्लिकेशन दुनिया के साथ शेयर करने के लिए तैयार हो जाए, तो बेहतर होगा कि आप अपने ऐप्लिकेशन में मौजूद किसी भी टेस्ट कोड या डिपेंडेंसी से अपने APK का साइज़ न बढ़ाएं. Gradle कॉन्फ़िगरेशन का इस्तेमाल करके, यह तय किया जा सकता है कि किसी लाइब्रेरी को मुख्य कोड में शामिल किया जाना चाहिए या टेस्ट कोड में. सबसे सामान्य कॉन्फ़िगरेशन ये हैं:

  • implementation—डिपेंडेंसी, टेस्ट सोर्स सेट के साथ-साथ सभी सोर्स सेट में उपलब्ध है.
  • testImplementation—डिपेंडेंसी सिर्फ़ टेस्ट सोर्स सेट में उपलब्ध है.
  • androidTestImplementation—डिपेंडेंसी सिर्फ़ androidTest सोर्स सेट में उपलब्ध है.

आपके इस्तेमाल किए गए कॉन्फ़िगरेशन से यह तय होता है कि डिपेंडेंसी का इस्तेमाल कहां किया जा सकता है. अगर आपने यह लिखा है:

testImplementation "org.hamcrest:hamcrest-all:$hamcrestVersion"

इसका मतलब है कि Hamcrest सिर्फ़ टेस्ट सोर्स सेट में उपलब्ध होगा. इससे यह भी पक्का होता है कि Hamcrest को आपके फ़ाइनल ऐप्लिकेशन में शामिल नहीं किया जाएगा.

चौथा चरण: दावे लिखने के लिए Hamcrest का इस्तेमाल करना

  1. assertEquals के बजाय Hamcrest के assertThat का इस्तेमाल करने के लिए, getActiveAndCompletedStats_noCompleted_returnsHundredZero() टेस्ट को अपडेट करें.
// REPLACE
assertEquals(result.completedTasksPercent, 0f)
assertEquals(result.activeTasksPercent, 100f)

// WITH
assertThat(result.activeTasksPercent, `is`(100f))
assertThat(result.completedTasksPercent, `is`(0f))

ध्यान दें कि अगर आपसे कहा जाए, तो इंपोर्ट import org.hamcrest.Matchers.`is` का इस्तेमाल किया जा सकता है.

फ़ाइनल टेस्ट, नीचे दिए गए कोड की तरह दिखेगा.

StatisticsUtilsTest.kt

import com.example.android.architecture.blueprints.todoapp.data.Task
import org.hamcrest.MatcherAssert.assertThat
import org.hamcrest.Matchers.`is`
import org.junit.Test

class StatisticsUtilsTest {

    @Test
    fun getActiveAndCompletedStats_noCompleted_returnsHundredZero {

        // Create an active tasks (the false makes this active)
        val tasks = listOf<Task>(
            Task("title", "desc", isCompleted = false)
        )
        // Call your function
        val result = getActiveAndCompletedStats(tasks)

        // Check the result
        assertThat(result.activeTasksPercent, `is`(100f))
        assertThat(result.completedTasksPercent, `is`(0f))

    }
}
  1. अपडेट किए गए टेस्ट को चलाकर देखें कि यह अब भी काम कर रहा है या नहीं!

इस कोडलैब में, आपको Hamcrest के बारे में पूरी जानकारी नहीं मिलेगी. इसलिए, अगर आपको इसके बारे में ज़्यादा जानना है, तो आधिकारिक ट्यूटोरियल देखें.

यह टास्क, प्रैक्टिस के लिए है. इसे पूरा करना ज़रूरी नहीं है.

इस टास्क में, JUnit और Hamcrest का इस्तेमाल करके ज़्यादा टेस्ट लिखे जाएंगे. आपको टेस्ट ड्रिवन डेवलपमेंट के प्रोग्राम प्रैक्टिस से ली गई रणनीति का इस्तेमाल करके टेस्ट भी लिखने होंगे. टेस्ट ड्रिवन डेवलपमेंट या टीडीडी, प्रोग्रामिंग का एक तरीका है. इसमें, फ़ीचर कोड लिखने से पहले टेस्ट लिखे जाते हैं. इसके बाद, जांच पास करने के मकसद से फ़ीचर कोड लिखा जाता है.

पहला चरण. जांच लिखना

जब आपके पास सामान्य टास्क की सूची हो, तब उसके लिए टेस्ट लिखें:

  1. अगर एक टास्क पूरा हो गया है और कोई भी टास्क चालू नहीं है, तो activeTasks प्रतिशत 0f होना चाहिए. साथ ही, पूरे किए गए टास्क का प्रतिशत 100f होना चाहिए.
  2. अगर दो टास्क पूरे हो गए हैं और तीन टास्क पर काम चल रहा है, तो पूरे हो चुके टास्क का प्रतिशत 40f और काम चल रहे टास्क का प्रतिशत 60f होना चाहिए.

दूसरा चरण. किसी गड़बड़ी के लिए टेस्ट लिखना

getActiveAndCompletedStats के लिए लिखे गए कोड में गड़बड़ी है. ध्यान दें कि अगर सूची खाली है या उसमें कोई वैल्यू नहीं है, तो यह फ़ंक्शन सही तरीके से काम नहीं करता. इन दोनों ही मामलों में, दोनों प्रतिशत शून्य होने चाहिए.

internal fun getActiveAndCompletedStats(tasks: List<Task>?): StatsResult {

   val totalTasks = tasks!!.size
   val numberOfActiveTasks = tasks.count { it.isActive }
   val activePercent = 100 * numberOfActiveTasks / totalTasks
   val completePercent = 100 * (totalTasks - numberOfActiveTasks) / totalTasks

   return StatsResult(
       activeTasksPercent = activePercent.toFloat(),
       completedTasksPercent = completePercent.toFloat()
   )
  
}

कोड को ठीक करने और जांच लिखने के लिए, टेस्ट ड्रिवन डेवलपमेंट का इस्तेमाल किया जाएगा. टेस्ट ड्रिवन डेवलपमेंट में यह तरीका अपनाया जाता है.

  1. Given, When, Then स्ट्रक्चर का इस्तेमाल करके टेस्ट लिखें. साथ ही, ऐसा नाम दें जो कन्वेंशन के मुताबिक हो.
  2. पुष्टि करें कि टेस्ट पूरा नहीं हुआ.
  3. टेस्ट पास करने के लिए, कम से कम कोड लिखें.
  4. सभी टेस्ट के लिए इस प्रोसेस को दोहराएं!

बग को ठीक करने के बजाय, सबसे पहले टेस्ट लिखे जाएंगे. इसके बाद, पुष्टि करें कि आपके पास ऐसे टेस्ट हैं जो आपको आने वाले समय में इन बग को गलती से फिर से शामिल करने से बचाते हैं.

  1. अगर कोई लिस्ट खाली है (emptyList()), तो दोनों प्रतिशत 0f होने चाहिए.
  2. अगर टास्क लोड करने में कोई गड़बड़ी हुई है, तो सूची null होगी. साथ ही, दोनों प्रतिशत 0f होने चाहिए.
  3. अपने टेस्ट चलाएं और पुष्टि करें कि वे काम नहीं कर रहे हैं:

तीसरा चरण. गड़बड़ी ठीक करना

अब आपके पास टेस्ट हैं, इसलिए गड़बड़ी को ठीक करें.

  1. getActiveAndCompletedStats में मौजूद गड़बड़ी को ठीक करें. इसके लिए, अगर tasks, null है या खाली है, तो 0f दिखाएं:
internal fun getActiveAndCompletedStats(tasks: List<Task>?): StatsResult {

    return if (tasks == null || tasks.isEmpty()) {
        StatsResult(0f, 0f)
    } else {
        val totalTasks = tasks.size
        val numberOfActiveTasks = tasks.count { it.isActive }
        StatsResult(
            activeTasksPercent = 100f * numberOfActiveTasks / tasks.size,
            completedTasksPercent = 100f * (totalTasks - numberOfActiveTasks) / tasks.size
        )
    }
}
  1. जांचों को फिर से चलाएं और पुष्टि करें कि अब सभी जांचें पास हो गई हैं!

टीडीडी को फ़ॉलो करके और पहले टेस्ट लिखकर, आपने यह पक्का करने में मदद की है कि:

  • नई सुविधा के साथ हमेशा उससे जुड़ी जांचें होती हैं. इसलिए, आपकी जांचें इस बात का दस्तावेज़ होती हैं कि आपका कोड क्या करता है.
  • आपके टेस्ट, सही नतीजों की जांच करते हैं. साथ ही, पहले से मौजूद गड़बड़ियों से बचाते हैं.

समाधान: ज़्यादा टेस्ट लिखना

यहां सभी टेस्ट और उनसे जुड़े फ़ीचर कोड दिए गए हैं.

StatisticsUtilsTest.kt

class StatisticsUtilsTest {

    @Test
    fun getActiveAndCompletedStats_noCompleted_returnsHundredZero {
        val tasks = listOf(
            Task("title", "desc", isCompleted = false)
        )
        // When the list of tasks is computed with an active task
        val result = getActiveAndCompletedStats(tasks)

        // Then the percentages are 100 and 0
        assertThat(result.activeTasksPercent, `is`(100f))
        assertThat(result.completedTasksPercent, `is`(0f))
    }

    @Test
    fun getActiveAndCompletedStats_noActive_returnsZeroHundred() {
        val tasks = listOf(
            Task("title", "desc", isCompleted = true)
        )
        // When the list of tasks is computed with a completed task
        val result = getActiveAndCompletedStats(tasks)

        // Then the percentages are 0 and 100
        assertThat(result.activeTasksPercent, `is`(0f))
        assertThat(result.completedTasksPercent, `is`(100f))
    }

    @Test
    fun getActiveAndCompletedStats_both_returnsFortySixty() {
        // Given 3 completed tasks and 2 active tasks
        val tasks = listOf(
            Task("title", "desc", isCompleted = true),
            Task("title", "desc", isCompleted = true),
            Task("title", "desc", isCompleted = true),
            Task("title", "desc", isCompleted = false),
            Task("title", "desc", isCompleted = false)
        )
        // When the list of tasks is computed
        val result = getActiveAndCompletedStats(tasks)

        // Then the result is 40-60
        assertThat(result.activeTasksPercent, `is`(40f))
        assertThat(result.completedTasksPercent, `is`(60f))
    }

    @Test
    fun getActiveAndCompletedStats_error_returnsZeros() {
        // When there's an error loading stats
        val result = getActiveAndCompletedStats(null)

        // Both active and completed tasks are 0
        assertThat(result.activeTasksPercent, `is`(0f))
        assertThat(result.completedTasksPercent, `is`(0f))
    }

    @Test
    fun getActiveAndCompletedStats_empty_returnsZeros() {
        // When there are no tasks
        val result = getActiveAndCompletedStats(emptyList())

        // Both active and completed tasks are 0
        assertThat(result.activeTasksPercent, `is`(0f))
        assertThat(result.completedTasksPercent, `is`(0f))
    }
}

StatisticsUtils.kt

internal fun getActiveAndCompletedStats(tasks: List<Task>?): StatsResult {

    return if (tasks == null || tasks.isEmpty()) {
        StatsResult(0f, 0f)
    } else {
        val totalTasks = tasks.size
        val numberOfActiveTasks = tasks.count { it.isActive }
        StatsResult(
            activeTasksPercent = 100f * numberOfActiveTasks / tasks.size,
            completedTasksPercent = 100f * (totalTasks - numberOfActiveTasks) / tasks.size
        )
    }
}

आपने टेस्ट लिखने और उन्हें चलाने के बुनियादी सिद्धांतों को समझ लिया है! इसके बाद, आपको सामान्य ViewModel और LiveData टेस्ट लिखने का तरीका बताया जाएगा.

इस कोडलैब के बाकी हिस्से में, आपको दो Android क्लास के लिए टेस्ट लिखने का तरीका बताया जाएगा. ये क्लास, ज़्यादातर ऐप्लिकेशन में इस्तेमाल की जाती हैं—ViewModel और LiveData.

सबसे पहले, TasksViewModel के लिए टेस्ट लिखे जाते हैं.


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



आपको ऐसा टेस्ट लिखना होगा जिससे यह पता चल सके कि addNewTask तरीके को कॉल करने पर, नई टास्क विंडो खोलने के लिए Event ट्रिगर होता है. यहां ऐप्लिकेशन का वह कोड दिया गया है जिसकी आपको जांच करनी है.

TasksViewModel.kt

fun addNewTask() {
   _newTaskEvent.value = Event(Unit)
}

पहला चरण. TasksViewModelTest क्लास बनाना

StatisticsUtilTest के लिए इस्तेमाल किया गया तरीका अपनाकर, इस चरण में TasksViewModelTest के लिए टेस्ट फ़ाइल बनाएं.

  1. tasks पैकेज में, वह क्लास खोलें जिसकी आपको जांच करनी है TasksViewModel.
  2. कोड में, क्लास के नाम TasksViewModel पर राइट क्लिक करें -> जनरेट करें -> टेस्ट.

  1. जांच बनाएं स्क्रीन पर, ठीक है पर क्लिक करके स्वीकार करें. आपको डिफ़ॉल्ट सेटिंग में कोई बदलाव करने की ज़रूरत नहीं है.
  2. डेस्टिनेशन डायरेक्ट्री चुनें डायलॉग में, test डायरेक्ट्री चुनें.

दूसरा चरण. ViewModel का टेस्ट लिखना शुरू करना

इस चरण में, व्यू मॉडल टेस्ट जोड़ा जाता है. इससे यह जांच की जाती है कि addNewTask तरीके को कॉल करने पर, नई टास्क विंडो खोलने के लिए Event ट्रिगर होता है.

  1. addNewTask_setsNewTaskEvent नाम का नया टेस्ट बनाएं.

TasksViewModelTest.kt

class TasksViewModelTest {

    @Test
    fun addNewTask_setsNewTaskEvent() {

        // Given a fresh TasksViewModel


        // When adding a new task


        // Then the new task event is triggered

    }
    
}

ऐप्लिकेशन के कॉन्टेक्स्ट के बारे में क्या जानकारी है?

जांच करने के लिए TasksViewModel का इंस्टेंस बनाते समय, इसके कंस्ट्रक्टर को ऐप्लिकेशन कॉन्टेक्स्ट की ज़रूरत होती है. हालांकि, इस टेस्ट में आपको गतिविधियों, यूज़र इंटरफ़ेस (यूआई), और फ़्रैगमेंट के साथ पूरा ऐप्लिकेशन नहीं बनाना है. इसलिए, आपको ऐप्लिकेशन कॉन्टेक्स्ट कैसे मिलेगा?

TasksViewModelTest.kt

// Given a fresh ViewModel
        val tasksViewModel = TasksViewModel(???)

AndroidX Test लाइब्रेरी में ऐसी क्लास और तरीके शामिल होते हैं जो आपको ऐप्लिकेशन और गतिविधियों जैसे कॉम्पोनेंट के ऐसे वर्शन उपलब्ध कराते हैं जिनका इस्तेमाल सिर्फ़ टेस्ट के लिए किया जाता है. अगर आपको लोकल टेस्ट करना है और इसके लिए, सिम्युलेट की गई Android फ़्रेमवर्क क्लास (जैसे कि ऐप्लिकेशन कॉन्टेक्स्ट) की ज़रूरत है, तो AndroidX Test को सही तरीके से सेट अप करने के लिए यह तरीका अपनाएं:

  1. AndroidX Test की कोर और एक्सटेंशन डिपेंडेंसी जोड़ना
  2. Robolectric Testing library की डिपेंडेंसी जोड़ें
  3. AndroidJunit4 टेस्ट रनर का इस्तेमाल करके क्लास को एनोटेट करें
  4. AndroidX Test कोड लिखना

आपको इन चरणों को पूरा करना होगा. इसके बाद, आपको यह समझना होगा कि ये चरण एक साथ क्या करते हैं.

तीसरा चरण. Gradle डिपेंडेंसी जोड़ना

  1. AndroidX Test core और ext डिपेंडेंसी के साथ-साथ Robolectric टेस्टिंग डिपेंडेंसी जोड़ने के लिए, इन डिपेंडेंसी को अपने ऐप्लिकेशन मॉड्यूल की build.gradle फ़ाइल में कॉपी करें.

app/build.gradle

    // AndroidX Test - JVM testing
testImplementation "androidx.test.ext:junit-ktx:$androidXTestExtKotlinRunnerVersion"

    testImplementation "androidx.test:core-ktx:$androidXTestCoreVersion"

 testImplementation "org.robolectric:robolectric:$robolectricVersion"

चरण 4. JUnit Test Runner जोड़ना

  1. अपनी टेस्ट क्लास के ऊपर @RunWith(AndroidJUnit4::class) जोड़ें.

TasksViewModelTest.kt

@RunWith(AndroidJUnit4::class)
class TasksViewModelTest {
    // Test code
}

चरण 5. AndroidX Test का इस्तेमाल करना

इस समय, AndroidX Test लाइब्रेरी का इस्तेमाल किया जा सकता है. इसमें ApplicationProvider.getApplicationContext तरीका शामिल है, जो ऐप्लिकेशन कॉन्टेक्स्ट को ऐक्सेस करता है.

  1. AndroidX टेस्ट लाइब्रेरी से ApplicationProvider.getApplicationContext() का इस्तेमाल करके, TasksViewModel बनाएं.

TasksViewModelTest.kt

// Given a fresh ViewModel
val tasksViewModel = TasksViewModel(ApplicationProvider.getApplicationContext())
  1. addNewTask को tasksViewModel पर कॉल करें.

TasksViewModelTest.kt

tasksViewModel.addNewTask()

इस समय, आपकी जांच का कोड यहां दिए गए कोड जैसा दिखना चाहिए.

TasksViewModelTest.kt

    @Test
    fun addNewTask_setsNewTaskEvent() {

        // Given a fresh ViewModel
        val tasksViewModel = TasksViewModel(ApplicationProvider.getApplicationContext())

        // When adding a new task
        tasksViewModel.addNewTask()

        // Then the new task event is triggered
        // TODO test LiveData
    }
  1. यह पुष्टि करने के लिए कि यह काम कर रहा है, अपना टेस्ट चलाएं.

कॉन्सेप्ट: AndroidX Test कैसे काम करता है?

AndroidX Test क्या है?

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

ApplicationProvider.getApplicationContext()

AndroidX Test API का एक फ़ायदा यह है कि इन्हें लोकल टेस्ट और इंस्ट्रुमेंटेड टेस्ट, दोनों के लिए बनाया गया है. यह इसलिए अच्छा है, क्योंकि:

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

उदाहरण के लिए, आपने AndroidX Test लाइब्रेरी का इस्तेमाल करके कोड लिखा है. इसलिए, TasksViewModelTest क्लास को test फ़ोल्डर से androidTest फ़ोल्डर में ले जाने पर भी, टेस्ट चलते रहेंगे. getApplicationContext(), लोकल टेस्ट या इंस्ट्रुमेंटेड टेस्ट के तौर पर चलाए जाने पर, थोड़ा अलग तरीके से काम करता है:

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

Robolectric क्या है?

AndroidX Test, लोकल टेस्ट के लिए जिस सिम्युलेटेड Android एनवायरमेंट का इस्तेमाल करता है उसे Robolectric उपलब्ध कराता है. Robolectric एक लाइब्रेरी है. यह टेस्ट के लिए, Android जैसा माहौल बनाती है. साथ ही, यह एम्युलेटर को बूट करने या किसी डिवाइस पर चलाने से ज़्यादा तेज़ी से काम करती है. Robolectric डिपेंडेंसी के बिना, आपको यह गड़बड़ी दिखेगी:

@RunWith(AndroidJUnit4::class) क्या करता है?

टेस्ट रनर एक JUnit कॉम्पोनेंट है, जो टेस्ट चलाता है. टेस्ट रनर के बिना, आपके टेस्ट नहीं चलेंगे. JUnit, डिफ़ॉल्ट टेस्ट रनर उपलब्ध कराता है. यह आपको अपने-आप मिल जाता है. @RunWith डिफ़ॉल्ट टेस्ट रनर को बदल देता है.

AndroidJUnit4 टेस्ट रनर की मदद से, AndroidX Test आपके टेस्ट को अलग-अलग तरीके से चला सकता है. यह इस बात पर निर्भर करता है कि वे इंस्ट्रुमेंटेड टेस्ट हैं या लोकल टेस्ट.

छठा चरण. Robolectric से जुड़ी चेतावनियां ठीक करना

कोड चलाने पर, ध्यान दें कि Robolectric का इस्तेमाल किया गया है.

AndroidX Test और AndroidJunit4 टेस्ट रनर की वजह से, यह काम बिना Robolectric कोड की एक भी लाइन लिखे किया जाता है!

आपको दो चेतावनियां दिख सकती हैं.

  • No such manifest file: ./AndroidManifest.xml
  • "WARN: Android SDK 29 requires Java 9..."

अपनी Gradle फ़ाइल को अपडेट करके, No such manifest file: ./AndroidManifest.xml चेतावनी को ठीक किया जा सकता है.

  1. सही Android मेनिफ़ेस्ट का इस्तेमाल करने के लिए, अपनी gradle फ़ाइल में यह लाइन जोड़ें. includeAndroidResources विकल्प की मदद से, यूनिट टेस्ट में Android के रिसॉर्स ऐक्सेस किए जा सकते हैं. इनमें आपकी AndroidManifest फ़ाइल भी शामिल है.

app/build.gradle

    // Always show the result of every unit test when running via command line, even if it passes.
    testOptions.unitTests {
        includeAndroidResources = true

        // ... 
    }

चेतावनी "WARN: Android SDK 29 requires Java 9..." ज़्यादा मुश्किल है. Android Q पर टेस्ट चलाने के लिए, Java 9 की ज़रूरत होती है. इस कोडलैब के लिए, Android Studio को Java 9 का इस्तेमाल करने के लिए कॉन्फ़िगर करने के बजाय, अपने टारगेट और कंपाइल एसडीके को 28 पर रखें.

खास जानकारी:

  • प्योर व्यू मॉडल टेस्ट को आम तौर पर test सोर्स सेट में रखा जा सकता है, क्योंकि इनके कोड के लिए आम तौर पर Android की ज़रूरत नहीं होती.
  • ऐप्लिकेशन और गतिविधियों जैसे कॉम्पोनेंट के टेस्ट वर्शन पाने के लिए, AndroidX testlibrary का इस्तेमाल किया जा सकता है.
  • अगर आपको अपने test सोर्स सेट में सिम्युलेट किया गया Android कोड चलाना है, तो Robolectric डिपेंडेंसी और @RunWith(AndroidJUnit4::class) एनोटेशन जोड़ा जा सकता है.

बधाई हो, आपने AndroidX टेस्टिंग लाइब्रेरी और Robolectric, दोनों का इस्तेमाल करके टेस्ट चलाया है. आपका टेस्ट पूरा नहीं हुआ है. आपने अब तक कोई दावा करने वाला स्टेटमेंट नहीं लिखा है. इसमें सिर्फ़ // TODO test LiveData लिखा है. अगले चरण में, LiveData की मदद से दावा करने वाले स्टेटमेंट लिखने के बारे में जानें.

इस टास्क में, आपको LiveData वैल्यू को सही तरीके से असर्ट करने का तरीका बताया जाएगा.

आपने यहाँ देखना छोड़ा था, लेकिन addNewTask_setsNewTaskEvent व्यू मॉडल टेस्ट नहीं किया था.

TasksViewModelTest.kt

    @Test
    fun addNewTask_setsNewTaskEvent() {

        // Given a fresh ViewModel
        val tasksViewModel = TasksViewModel(ApplicationProvider.getApplicationContext())

        // When adding a new task
        tasksViewModel.addNewTask()

        // Then the new task event is triggered
        // TODO test LiveData
    }
    

LiveData को आज़माने के लिए, हमारा सुझाव है कि आप ये दो काम करें:

  1. InstantTaskExecutorRule का इस्तेमाल करें
  2. LiveData की निगरानी की सुविधा चालू करना

पहला चरण. InstantTaskExecutorRule का इस्तेमाल करना

InstantTaskExecutorRule एक JUnit Rule है. @get:Rule एनोटेशन के साथ इसका इस्तेमाल करने पर, InstantTaskExecutorRule क्लास में मौजूद कुछ कोड, टेस्ट से पहले और बाद में चलता है. सटीक कोड देखने के लिए, कीबोर्ड शॉर्टकट Command+B का इस्तेमाल करके फ़ाइल देखी जा सकती है.

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

  1. Architecture Components की कोर टेस्टिंग लाइब्रेरी के लिए gradle डिपेंडेंसी जोड़ें. इसमें यह नियम शामिल है.

app/build.gradle

testImplementation "androidx.arch.core:core-testing:$archTestingVersion"
  1. TasksViewModelTest.kt खोलें
  2. TasksViewModelTest क्लास में InstantTaskExecutorRule को जोड़ें.

TasksViewModelTest.kt

class TasksViewModelTest {
    @get:Rule
    var instantExecutorRule = InstantTaskExecutorRule()
    
    // Other code...
}

दूसरा चरण. LiveDataTestUtil.kt क्लास जोड़ना

आपका अगला चरण यह पक्का करना है कि जिस LiveData की जांच की जा रही है वह काम कर रहा हो.

LiveData का इस्तेमाल करते समय, आम तौर पर किसी गतिविधि या फ़्रैगमेंट (LifecycleOwner) को LiveData की निगरानी करने के लिए कहा जाता है.

viewModel.resultLiveData.observe(fragment, Observer {
    // Observer code here
})

यह जानकारी अहम है. LiveData पर सक्रिय ऑब्ज़र्वर की ज़रूरत होती है, ताकि

अपने व्यू मॉडल के LiveData के लिए, अनुमानित LiveData पाने के लिए, आपको LifecycleOwner के साथ LiveData को देखना होगा.

इससे एक समस्या होती है: आपके TasksViewModel टेस्ट में, LiveData को मॉनिटर करने के लिए कोई गतिविधि या फ़्रैगमेंट नहीं है. इस समस्या से बचने के लिए, observeForever तरीके का इस्तेमाल किया जा सकता है. इससे यह पक्का होता है कि LiveData की लगातार निगरानी की जा रही है. इसके लिए, LifecycleOwner की ज़रूरत नहीं होती. जब observeForever, तो आपको अपने ऑब्ज़र्वर को हटाना होगा. ऐसा न करने पर, ऑब्ज़र्वर के साथ जानकारी शेयर हो सकती है.

यह कोड कुछ ऐसा दिखता है. इसकी जांच करें:

@Test
fun addNewTask_setsNewTaskEvent() {

    // Given a fresh ViewModel
    val tasksViewModel = TasksViewModel(ApplicationProvider.getApplicationContext())


    // Create observer - no need for it to do anything!
    val observer = Observer<Event<Unit>> {}
    try {

        // Observe the LiveData forever
        tasksViewModel.newTaskEvent.observeForever(observer)

        // When adding a new task
        tasksViewModel.addNewTask()

        // Then the new task event is triggered
        val value = tasksViewModel.newTaskEvent.value
        assertThat(value?.getContentIfNotHandled(), (not(nullValue())))

    } finally {
        // Whatever happens, don't forget to remove the observer!
        tasksViewModel.newTaskEvent.removeObserver(observer)
    }
}

टेस्ट में एक LiveData को देखने के लिए, बहुत ज़्यादा बॉयलरप्लेट कोड की ज़रूरत होती है! इस बॉयलरप्लेट को हटाने के कुछ तरीके हैं. ऑब्ज़र्वर को आसानी से जोड़ने के लिए, LiveDataTestUtil नाम का एक एक्सटेंशन फ़ंक्शन बनाया जाएगा.

  1. अपने test सोर्स सेट में, LiveDataTestUtil.kt नाम की नई Kotlin फ़ाइल बनाएं.


  1. यहां दिए गए कोड को कॉपी करके चिपकाएं.

LiveDataTestUtil.kt

import androidx.annotation.VisibleForTesting
import androidx.lifecycle.LiveData
import androidx.lifecycle.Observer
import java.util.concurrent.CountDownLatch
import java.util.concurrent.TimeUnit
import java.util.concurrent.TimeoutException


@VisibleForTesting(otherwise = VisibleForTesting.NONE)
fun <T> LiveData<T>.getOrAwaitValue(
    time: Long = 2,
    timeUnit: TimeUnit = TimeUnit.SECONDS,
    afterObserve: () -> Unit = {}
): T {
    var data: T? = null
    val latch = CountDownLatch(1)
    val observer = object : Observer<T> {
        override fun onChanged(o: T?) {
            data = o
            latch.countDown()
            this@getOrAwaitValue.removeObserver(this)
        }
    }
    this.observeForever(observer)

    try {
        afterObserve.invoke()

        // Don't wait indefinitely if the LiveData is not set.
        if (!latch.await(time, timeUnit)) {
            throw TimeoutException("LiveData value was never set.")
        }

    } finally {
        this.removeObserver(observer)
    }

    @Suppress("UNCHECKED_CAST")
    return data as T
}

यह तरीका काफ़ी मुश्किल है. यह getOrAwaitValue नाम का Kotlin एक्सटेंशन फ़ंक्शन बनाता है. यह फ़ंक्शन, ऑब्ज़र्वर को जोड़ता है, LiveData वैल्यू को पाता है, और फिर ऑब्ज़र्वर को हटा देता है. यह ऊपर दिखाए गए observeForever कोड का छोटा और दोबारा इस्तेमाल किया जा सकने वाला वर्शन है. इस क्लास के बारे में पूरी जानकारी पाने के लिए, यह ब्लॉग पोस्ट देखें.

तीसरा चरण. असर्शन लिखने के लिए, getOrAwaitValue का इस्तेमाल करना

इस चरण में, getOrAwaitValue तरीके का इस्तेमाल किया जाता है. साथ ही, एक ऐसा दावा लिखा जाता है जिससे यह पता चलता है कि newTaskEvent ट्रिगर हुआ था.

  1. getOrAwaitValue का इस्तेमाल करके, newTaskEvent के लिए LiveData वैल्यू पाएं.
val value = tasksViewModel.newTaskEvent.getOrAwaitValue()
  1. यह पुष्टि करता है कि वैल्यू शून्य नहीं है.
assertThat(value.getContentIfNotHandled(), (not(nullValue())))

पूरी जांच, नीचे दिए गए कोड की तरह दिखनी चाहिए.

import androidx.arch.core.executor.testing.InstantTaskExecutorRule
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.example.android.architecture.blueprints.todoapp.getOrAwaitValue
import org.hamcrest.MatcherAssert.assertThat
import org.hamcrest.Matchers.not
import org.hamcrest.Matchers.nullValue
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith

@RunWith(AndroidJUnit4::class)
class TasksViewModelTest {

    @get:Rule
    var instantExecutorRule = InstantTaskExecutorRule()


    @Test
    fun addNewTask_setsNewTaskEvent() {
        // Given a fresh ViewModel
        val tasksViewModel = TasksViewModel(ApplicationProvider.getApplicationContext())

        // When adding a new task
        tasksViewModel.addNewTask()

        // Then the new task event is triggered
        val value = tasksViewModel.newTaskEvent.getOrAwaitValue()

        assertThat(value.getContentIfNotHandled(), not(nullValue()))


    }

}
  1. अपना कोड चलाएं और देखें कि टेस्ट पास हो गया है!

अब जब आपने टेस्ट लिखने का तरीका देख लिया है, तो खुद से एक टेस्ट लिखें. इस चरण में, आपने जो कौशल सीखे हैं उनका इस्तेमाल करके, एक और TasksViewModel टेस्ट लिखने की प्रैक्टिस करें.

पहला चरण. अपना ViewModel टेस्ट लिखना

आपको setFilterAllTasks_tasksAddViewVisible() लिखना होगा. इस टेस्ट में यह जांच की जानी चाहिए कि अगर आपने फ़िल्टर टाइप को सभी टास्क दिखाने के लिए सेट किया है, तो टास्क जोड़ें बटन दिख रहा हो.

  1. addNewTask_setsNewTaskEvent() का इस्तेमाल करके, TasksViewModelTest में setFilterAllTasks_tasksAddViewVisible() नाम का एक टेस्ट लिखें. यह टेस्ट, फ़िल्टरिंग मोड को ALL_TASKS पर सेट करता है और यह पुष्टि करता है कि tasksAddViewVisible LiveData true है.


शुरू करने के लिए, नीचे दिए गए कोड का इस्तेमाल करें.

TasksViewModelTest

    @Test
    fun setFilterAllTasks_tasksAddViewVisible() {

        // Given a fresh ViewModel

        // When the filter type is ALL_TASKS

        // Then the "Add task" action is visible
        
    }

ध्यान दें:

  • सभी टास्क के लिए TasksFilterType enum, ALL_TASKS. है
  • टास्क जोड़ने वाले बटन के दिखने की सुविधा को LiveData tasksAddViewVisible. कंट्रोल करता है
  1. टेस्ट चलाएं.

दूसरा चरण. अपने टेस्ट की तुलना समाधान से करना

अपने जवाब की तुलना यहां दिए गए जवाब से करें.

TasksViewModelTest

    @Test
    fun setFilterAllTasks_tasksAddViewVisible() {

        // Given a fresh ViewModel
        val tasksViewModel = TasksViewModel(ApplicationProvider.getApplicationContext())

        // When the filter type is ALL_TASKS
        tasksViewModel.setFiltering(TasksFilterType.ALL_TASKS)

        // Then the "Add task" action is visible
        assertThat(tasksViewModel.tasksAddViewVisible.getOrAwaitValue(), `is`(true))
    }

देखें कि आपने ये काम किए हैं या नहीं:

  • AndroidX ApplicationProvider.getApplicationContext() स्टेटमेंट का इस्तेमाल करके, tasksViewModel बनाया जाता है.
  • ALL_TASKS फ़िल्टर टाइप enum को पास करके, setFiltering वाले तरीके को कॉल करें.
  • getOrAwaitNextValue तरीके का इस्तेमाल करके, यह जांच की जाती है कि tasksAddViewVisible सही है या नहीं.

तीसरा चरण. @Before नियम जोड़ना

ध्यान दें कि दोनों टेस्ट की शुरुआत में, आपने TasksViewModel को तय किया है.

TasksViewModelTest

        // Given a fresh ViewModel
        val tasksViewModel = TasksViewModel(ApplicationProvider.getApplicationContext())

जब आपके पास कई टेस्ट के लिए बार-बार सेटअप कोड होता है, तो सेटअप मेथड बनाने और बार-बार इस्तेमाल होने वाले कोड को हटाने के लिए, @Before एनोटेशन का इस्तेमाल किया जा सकता है. इन सभी टेस्ट में TasksViewModel की जांच की जाएगी. साथ ही, इसके लिए व्यू मॉडल की ज़रूरत होगी. इसलिए, इस कोड को @Before ब्लॉक में ले जाएं.

  1. lateinit नाम का एक tasksViewModel| इंस्टेंस वैरिएबल बनाएं.
  2. setupViewModel नाम का एक तरीका बनाएं.
  3. @Before का इस्तेमाल करके, इसमें एनोटेशन जोड़ें.
  4. व्यू मॉडल इंस्टैंटिएशन कोड को setupViewModel में ले जाएं.

TasksViewModelTest

    // Subject under test
    private lateinit var tasksViewModel: TasksViewModel

    @Before
    fun setupViewModel() {
        tasksViewModel = TasksViewModel(ApplicationProvider.getApplicationContext())
    }
  1. अपना कोड चलाएं!

चेतावनी

नहीं करना है, तो यह तरीका अपनाएं

tasksViewModel

और उसकी परिभाषा:

val tasksViewModel = TasksViewModel(ApplicationProvider.getApplicationContext())

इससे सभी टेस्ट के लिए एक ही इंस्टेंस का इस्तेमाल किया जाएगा. आपको ऐसा करने से बचना चाहिए, क्योंकि हर टेस्ट में, टेस्ट किए जा रहे विषय (इस मामले में ViewModel) का नया इंस्टेंस होना चाहिए.

TasksViewModelTest के लिए आपका फ़ाइनल कोड, यहां दिए गए कोड की तरह दिखना चाहिए.

TasksViewModelTest

@RunWith(AndroidJUnit4::class)
class TasksViewModelTest {

    // Subject under test
    private lateinit var tasksViewModel: TasksViewModel

    // Executes each task synchronously using Architecture Components.
    @get:Rule
    var instantExecutorRule = InstantTaskExecutorRule()

    @Before
    fun setupViewModel() {
        tasksViewModel = TasksViewModel(ApplicationProvider.getApplicationContext())
    }


    @Test
    fun addNewTask_setsNewTaskEvent() {

        // When adding a new task
        tasksViewModel.addNewTask()

        // Then the new task event is triggered
        val value = tasksViewModel.newTaskEvent.awaitNextValue()
        assertThat(
            value?.getContentIfNotHandled(), (not(nullValue()))
        )
    }

    @Test
    fun getTasksAddViewVisible() {

        // When the filter type is ALL_TASKS
        tasksViewModel.setFiltering(TasksFilterType.ALL_TASKS)

        // Then the "Add task" action is visible
        assertThat(tasksViewModel.tasksAddViewVisible.awaitNextValue(), `is`(true))
    }
    
}

आपने जो कोड शुरू किया था और फ़ाइनल कोड के बीच का अंतर देखने के लिए, यहां क्लिक करें.

पूरे कोडलैब का कोड डाउनलोड करने के लिए, यहां दिए गए git कमांड का इस्तेमाल करें:

$ git clone https://github.com/googlecodelabs/android-testing.git
$ cd android-testing
$ git checkout end_codelab_1


इसके अलावा, रिपॉज़िटरी को Zip फ़ाइल के तौर पर डाउनलोड किया जा सकता है. इसके बाद, इसे अनज़िप करके Android Studio में खोला जा सकता है.

ज़िप फ़ाइल डाउनलोड करें

इस कोडलैब में यह जानकारी दी गई है:

  • Android Studio से टेस्ट चलाने का तरीका.
  • लोकल (test) और इंस्ट्रूमेंटेशन टेस्ट (androidTest) के बीच अंतर.
  • JUnit और Hamcrest का इस्तेमाल करके, लोकल यूनिट टेस्ट लिखने का तरीका.
  • AndroidX Test Library की मदद से, ViewModel के टेस्ट सेट अप करना.

Udacity का कोर्स:

Android डेवलपर का दस्तावेज़:

वीडियो:

अन्य:

इस कोर्स में मौजूद अन्य कोडलैब के लिंक के लिए, Advanced Android in Kotlin कोडलैब का लैंडिंग पेज देखें.