टेस्ट डबल्स और डिपेंडेंसी इंजेक्शन के बारे में जानकारी

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

परिचय

यह दूसरा टेस्ट कोड टेस्ट डबल के बारे में है: इन्हें Android में कब इस्तेमाल करना है और डिपेंडेंसी इंजेक्शन, सर्विस लोकेटर पैटर्न, और लाइब्रेरी का इस्तेमाल करके इन्हें कैसे लागू करना है. ऐसा करने पर, आप #39 लिखना सीखेंगी:

  • रिपॉज़िटरी यूनिट की जांच
  • फ़्रैगमेंट और व्यू मॉडल इंटिग्रेशन टेस्ट
  • फ़्रैगमेंट नेविगेशन की जांच

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

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

आप इन चीज़ों के बारे में जानेंगे

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

आप नीचे दी गई लाइब्रेरी और कोड के सिद्धांतों का इस्तेमाल करेंगे:

आप क्या कर पाएंगे!

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

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

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

कोड डाउनलोड करें

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

पिन डाउनलोड करें

इसके अलावा, आप कोड के लिए GitHub डेटा स्टोर करने की जगह को क्लोन कर सकते हैं:

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

नीचे दिए गए निर्देशों का पालन करके, कोड के बारे में अच्छी तरह जानें.

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

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

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

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

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

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

यहां उन पैकेज की खास जानकारी दी गई है, जिन्हें आप #39:

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

.addedittask

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

.data

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

.statistics

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

.taskdetail

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

.tasks

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

.util

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

डेटा स्तर (.data)

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

DefaultTasksRepository नेटवर्किंग लेयर और डेटाबेस लेयर के बीच निर्देशांक या मीडिएट करता है. साथ ही, यह यूआई लेयर में डेटा दिखाता है.

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

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

नेविगेशन

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

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

इस सेक्शन में आम तौर पर जांच करने के कुछ सबसे सही तरीके बताए गए हैं, क्योंकि ये Android पर लागू होते हैं.

टेस्टिंग पिरामिड

जब आप किसी जांच की रणनीति के बारे में सोचते हैं, तो उसके तीन मिलते-जुलते टेस्टिंग पहलू होते हैं:

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

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

  • यूनिट टेस्ट—ये टेस्ट पर बहुत ज़्यादा फ़ोकस करते हैं, जो किसी एक क्लास में होते हैं. आम तौर पर, यह इस क्लास में एक ही तरीका होता है. अगर यूनिट की जांच नहीं हो पाती है, तो आपको पता चल सकता है कि आपके कोड में कहां की समस्या है. असल दुनिया में इनकी फ़िडेलिटी कम है. इसलिए, आपके ऐप्लिकेशन में एक ही तरीके या क्लास को लागू करने से कहीं ज़्यादा शामिल हैं. हर बार कोड बदलने पर, इन्हें चलाने के लिए ये काफ़ी तेज़ होते हैं. आम तौर पर, ये स्थानीय तौर पर टेस्ट किए जाएंगे (test सोर्स सेट में). उदाहरण: व्यू मॉडल और डेटा स्टोर करने की जगहों में एक ही तरीके की जांच करना.
  • इंटिग्रेशन टेस्ट—इन क्लास का इंटरैक्शन यह पक्का करने के लिए किया जाता है कि वे एक-दूसरे के साथ सही तरीके से काम कर रही हैं या नहीं. इंटिग्रेशन टेस्ट को व्यवस्थित करने का एक तरीका यह है कि आप उन्हें किसी एक सुविधा के लिए टेस्ट करें, जैसे कि कोई टास्क सेव करने की सुविधा. ये यूनिट जांच की तुलना में कोड के बड़े दायरे की जांच करते हैं. इसके बावजूद, इन्हें फ़िडेलिटी के मुकाबले तेज़ी से चलाने के लिए ऑप्टिमाइज़ किया जाता है. स्थिति के हिसाब से, इन्हें स्थानीय तौर पर या इंस्ट्रूमेंटेशन टेस्ट के तौर पर चलाया जा सकता है. उदाहरण: किसी एक फ़्रैगमेंट और व्यू मॉडल पेयर की सभी सुविधाओं की जांच करना.
  • खत्म होने की जांच (E2e)—साथ में काम करने वाली सुविधाओं के इस्तेमाल की जांच करें. वे ऐप्लिकेशन के बड़े हिस्सों की जांच करते हैं, असली इस्तेमाल को सिम्युलेट करते हैं, और इसलिए आम तौर पर ये धीरे काम करते हैं. उनमें सबसे ज़्यादा फ़िडेलिटी है और आपको बताती है कि आपका ऐप्लिकेशन पूरे काम करता है. अगर ये जांच बड़े पैमाने पर की जाती है, तो इन्हें androidTest सोर्स सेट में) लागू किया जाएगा.
    उदाहरण: पूरे ऐप्लिकेशन को शुरू करने के साथ-साथ कुछ सुविधाओं को टेस्ट करना.

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

आर्किटेक्चर ऐंड टेस्टिंग

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

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



इस लेसन में, आप ऊपर बताए गए आर्किटेक्चर के हिस्से के टेस्ट के लिए, सही तरीके से अलग-अलग तरीका देख सकते हैं:

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

आखिर में शुरू होने वाली टेस्टिंग की जानकारी अगले लेसन में शामिल की जाएगी.

जब आप किसी कक्षा के किसी हिस्से (तरीका या छोटे संग्रह) के लिए यूनिट टेस्ट लिखते हैं, तो आपका लक्ष्य सिर्फ़ उस क्लास में कोड की जांच करना होता है.

किसी खास क्लास या क्लास में सिर्फ़ कोड की जांच करना मुश्किल हो सकता है. आइए एक उदाहरण देखें. main सोर्स सेट में data.source.DefaultTaskRepository क्लास खोलें. यह ऐप्लिकेशन का रिपॉज़िटरी है और यह वह क्लास है जिसमें आप अगली इकाई के लिए टेस्ट लिखेंगे.

आपका लक्ष्य सिर्फ़ उस कक्षा में कोड की जांच करना है. हालांकि, DefaultTaskRepository, LocalTaskDataSource और RemoteTaskDataSource जैसी दूसरी क्लास पर निर्भर करती है. इसे कहने का एक और तरीका यह है कि LocalTaskDataSource और RemoteTaskDataSource डिपेंडेंसी DefaultTaskRepository हैं.

इस तरह, डेटा सोर्स क्लास में DefaultTaskRepository के तरीकों में ऐसे तरीके अपनाए जाते हैं जो डेटाबेस में जानकारी सेव करने या नेटवर्क से संपर्क करने के लिए, कॉल क्लास में बदलते हैं.



उदाहरण के लिए, DefaultTasksRepo में इस तरीके को देखें.

    suspend fun getTasks(forceUpdate: Boolean = false): Result<List<Task>> {
        if (forceUpdate) {
            try {
                updateTasksFromRemoteDataSource()
            } catch (ex: Exception) {
                return Result.Error(ex)
            }
        }
        return tasksLocalDataSource.getTasks()
    }

getTasks आपके डेटा स्टोर करने की जगह में किए जाने वाले सबसे ज़्यादा कोटेशन में से एक है. इस तरीके में, SQLite डेटाबेस से पढ़ना और नेटवर्क कॉल करना (updateTasksFromRemoteDataSource पर कॉल करना) शामिल है. इसमें सिर्फ़ रिपॉज़िटरी कोड से ज़्यादा कोड शामिल होता है.

डेटा स्टोर करने की जगह को टेस्ट करना मुश्किल होने की कुछ खास वजहें यहां दी गई हैं:

  • इस रिपॉज़िटरी के लिए सबसे आसान टेस्ट करने के लिए, आपको एक डेटाबेस बनाने और उसे मैनेज करने के बारे में सोचना होगा. इससे सवाल यहां आते हैं जैसे "क्या यह स्थानीय या इंस्ट्रूमेंटल टेस्टबॉट होना चाहिए; और अगर आप सिम्युलेटेड Android एनवायरमेंट पाने के लिए AndroidX टेस्ट का इस्तेमाल कर रहे हैं.
  • कोड के कुछ हिस्से, जैसे कि नेटवर्किंग कोड, चलने में लंबा समय ले सकते हैं या कभी-कभी असफल भी हो सकते हैं. यह लंबे समय के लिए चल सकने वाले टेस्ट करता है.
  • हो सकता है कि जांच में सफल न होने की वजह से, आपको यह पता न चले कि किस कोड में गड़बड़ी है. आपके टेस्ट, रिपॉज़िटरी कोड की जांच शुरू कर सकते हैं. उदाहरण के लिए, डेटाबेस कोड जैसे कुछ डिपेंडेंसी कोड में गड़बड़ी होने की वजह से, आपके यूनिट का कोटेशन अमान्य हो सकता है.

टेस्ट डबल

इसका समाधान यह है कि जब आप रिपॉज़िटरी का परीक्षण कर रहे होते हैं, तब आप वास्तविक नेटवर्किंग या डेटाबेस कोड का इस्तेमाल नहीं करते, लेकिन इसकी जगह टेस्ट डबल का इस्तेमाल करते हैं. डबल डबल, क्लास का एक वर्शन है, जिसे खास तौर पर जांच के लिए बनाया जाता है. यह टेस्ट में किसी क्लास के असली वर्शन की जगह लेगा. यह भी एक स्टंट डबल जैसा है. यह एक अभिनेता है जो स्टंट करने में माहिर है. यह खतरनाक कलाकार को असली अभिनेता की जगह लेता है.

यहां दो तरह के टेस्ट डबल दिए गए हैं:

नकली चीज़ें

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

मॉक

एक टेस्ट डबल, जो इसे कॉल करने के तरीकों को ट्रैक करता है. इसके बाद, यह टेस्ट पास करता है या नहीं करता है. इसलिए, यह इस बात पर निर्भर करता है कि तरीके को सही तरीके से कॉल किया गया है या नहीं.

स्टब

एक टेस्ट डबल जिसमें कोई लॉजिक नहीं होता और सिर्फ़ वही रिटर्न करता है जिसे आप रिटर्न करने के लिए प्रोग्राम करते हैं. उदाहरण के लिए, StubTaskRepository को getTasks से कुछ टास्क के कॉम्बिनेशन लौटाने के लिए प्रोग्राम किया जा सकता है.

डमी

ऐसा टेस्ट डबल जो पास हो जाता है, लेकिन इस्तेमाल नहीं किया जाता, जैसे कि अगर आपको इसे पैरामीटर के तौर पर देने की ज़रूरत हो. अगर आपके पास NoOpTaskRepository होता, तो वह किसी भी तरीके में नहीं कोड के साथ TaskRepository को लागू करता.

जासूसी

एक टेस्ट डबल जो कुछ और जानकारी का ट्रैक रखता है; उदाहरण के लिए, अगर आपने SpyTaskRepository बनाया है, तो यह addTask विधि को कॉल किए जाने की संख्या का ट्रैक रख सकता है.

टेस्ट डबल के बारे में ज़्यादा जानने के लिए, टॉयलेट पर जांच करना: अपने टेस्ट डबल के बारे में जानें देखें.

Android के लिए नकली और मॉक सबसे ज़्यादा इस्तेमाल किए जाने वाले टेस्ट डबल हैं.

इस टास्क में, आप #39 टेस्ट को FakeDataSource टेस्ट के तौर पर बनाने वाले हैं. यह असल डेटा सोर्स से अलग किया गया FakeDataSource टेस्ट होगा.

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

इस चरण में, आप FakeDataSouce नाम की एक क्लास बनाने जा रहे हैं. यह LocalDataSource और RemoteDataSource की दोहरा टेस्ट होगी.

  1. टेस्ट सोर्स सेट में, नया -> पैकेज पर दायां क्लिक करें.

  1. अंदर एक स्रोत पैकेज के साथ एक डेटा पैकेज बनाएं.
  2. डेटा/स्रोत पैकेज में FakeDataSource नाम की एक नई क्लास बनाएं.

दूसरा चरण: TasksDataSource इंटरफ़ेस लागू करना

अपनी नई कक्षा FakeDataSource को टेस्ट डबल के तौर पर इस्तेमाल करने के लिए, यह ज़रूरी है कि वह दूसरे डेटा सोर्स की जगह ले. वे डेटा स्रोत TasksLocalDataSource और TasksRemoteDataSource हैं.

  1. ध्यान दें कि ये दोनों ही TasksDataSource इंटरफ़ेस को कैसे लागू करते हैं.
class TasksLocalDataSource internal constructor(
    private val tasksDao: TasksDao,
    private val ioDispatcher: CoroutineDispatcher = Dispatchers.IO
) : TasksDataSource { ... }

object TasksRemoteDataSource : TasksDataSource { ... }
  1. FakeDataSource लागू करें TasksDataSource:
class FakeDataSource : TasksDataSource {

}

Android Studio शिकायत करेगा कि आपने TasksDataSource के लिए ज़रूरी तरीके लागू नहीं किए हैं.

  1. 'फटाफट चुनें' मेन्यू का इस्तेमाल करें और सदस्यों को लागू करें चुनें.


  1. सभी तरीकों को चुनें और ठीक है दबाएं.

तीसरा चरण: FakeDataSource में getTasks तरीका लागू करना

FakeDataSource खास तरह का टेस्ट है, जिसे नकली कहा जाता है. फ़र्ज़ी वह टेस्ट डबल होता है जिसमें &वर्क</a> है, लेकिन क्लास को लागू किया जाता है, लेकिन इसे #39; इस तरह से लागू किया जाता है जिससे यह टेस्ट के लिए अच्छा तो होता है, लेकिन प्रोडक्शन के लिए सही नहीं होता. & कोटेशन

उदाहरण के लिए, आपका नकली डेटा स्रोत नेटवर्क से नहीं जुड़ा होगा या किसी डेटाबेस में कुछ भी सेव नहीं करेगा—इसके बजाय यह सिर्फ़ एक इन-मेमोरी सूची का इस्तेमाल करेगा. यह आपकी उम्मीद के मुताबिक काम कर सकेगा & कोट; उन तरीकों से टास्क पाने या सेव करने के लिए उम्मीद के मुताबिक नतीजे मिलेंगे, लेकिन आप इसे कभी भी प्रोडक्शन में इस्तेमाल नहीं कर सकते, क्योंकि यह सर्वर या डेटाबेस में सेव नहीं होता.

FakeDataSource

  • आपको DefaultTasksRepository में कोड की जांच करने की ज़रूरत देता है और इसके लिए आपको किसी डेटाबेस या नेटवर्क पर निर्भर होने की ज़रूरत नहीं होती.
  • जांचों के लिए &real?-ज़रूरत के मुताबिक&कोटेशन; देता है.
  1. tasks बनाने वाला FakeDataSource बनाने के लिए, FakeDataSource कंस्ट्रक्टर को बदलें. यह MutableList<Task>?, खाली बदली जा सकने वाली सूची की डिफ़ॉल्ट वैल्यू के साथ MutableList<Task>? होता है.
class FakeDataSource(var tasks: MutableList<Task>? = mutableListOf()) : TasksDataSource { // Rest of class }


यह उन टास्क की सूची है जो &डेटा को नकली/कोट करते हैं; जो डेटाबेस या सर्वर का रिस्पॉन्स होता है. अभी के लिए, लक्ष्य repository's getTasks तरीके को आज़माना है. इससे डेटा सोर्स's getTasks, deleteAllTasks, और saveTask तरीकों को कॉल किया जाता है.

इन तरीकों का नकली वर्शन लिखें:

  1. getTasks लिखें: अगर tasks't null है, तो Success नतीजे दिखाएं. अगर tasks null है, तो Error का नतीजा दिखाएं.
  2. deleteAllTasks लिखें: बदले जा सकने वाले टास्क की सूची को मिटाएं.
  3. saveTask लिखें: टास्क को सूची में जोड़ें.

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

override suspend fun getTasks(): Result<List<Task>> {
    tasks?.let { return Success(ArrayList(it)) }
    return Error(
        Exception("Tasks not found")
    )
}


override suspend fun deleteAllTasks() {
    tasks?.clear()
}

override suspend fun saveTask(task: Task) {
    tasks?.add(task)
}

अगर ज़रूरी हो, तो यहां इंपोर्ट स्टेटमेंट दिए गए हैं:

import com.example.android.architecture.blueprints.todoapp.data.Result
import com.example.android.architecture.blueprints.todoapp.data.Result.Error
import com.example.android.architecture.blueprints.todoapp.data.Result.Success
import com.example.android.architecture.blueprints.todoapp.data.Task

यह असल लोकल और रिमोट डेटा सोर्स के काम करने का तरीका है.

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

मुख्य समस्या यह है कि आपके पास FakeDataSource है, लेकिन यूआरएल की जांच में इस्तेमाल करने के तरीके के बारे में साफ़ तौर पर पता नहीं है. इसे TasksRemoteDataSource और TasksLocalDataSource की जगह सिर्फ़ जांच में लागू करना होगा. TasksRemoteDataSource और TasksLocalDataSource, दोनों DefaultTasksRepository पर निर्भर करते हैं. इसका मतलब है कि DefaultTasksRepositories को चलाने के लिए इन क्लास पर निर्भर करना ज़रूरी है या उन्हें कोट करना होता है.

फ़िलहाल, डिपेंडेंसी DefaultTasksRepository के init तरीके में बनाई गई हैं.

DefaultTasksRepository.kt

class DefaultTasksRepository private constructor(application: Application) {

    private val tasksRemoteDataSource: TasksDataSource
    private val tasksLocalDataSource: TasksDataSource

   // Some other code

    init {
        val database = Room.databaseBuilder(application.applicationContext,
            ToDoDatabase::class.java, "Tasks.db")
            .build()

        tasksRemoteDataSource = TasksRemoteDataSource
        tasksLocalDataSource = TasksLocalDataSource(database.taskDao())
    }
    // Rest of class
}

आप taskLocalDataSource और tasksRemoteDataSource को DefaultTasksRepository में बना और असाइन कर रहे हैं, इसलिए वे पूरी तरह से हार्ड कोड किए गए हैं. अपने टेस्ट डबल में बदलने का कोई तरीका नहीं है.

इसके बजाय, आपको क्लास में इन डेटा सोर्स को हार्ड कोड करने के बजाय, उपलब्ध कराना होता है. डिपेंडेंसी उपलब्ध कराना, डिपेंडेंसी इंजेक्शन के तौर पर जाना जाता है. डिपेंडेंसी देने के कई तरीके हैं और डिपेंडेंसी अलग-अलग तरह की होती है.

कंस्ट्रक्टर डिपेंडेंसी इंजेक्शन आपको कंस्ट्रक्टर में पास करके, टेस्ट डबल में बदलने की सुविधा देता है.

कोई इंजेक्शन नहीं

इंजेक्ट

पहला चरण: DefaultTasksRepository में कंस्ट्रक्टर डिपेंडेंसी इंजेक्शन का इस्तेमाल करें

  1. DefaultTaskRepository' के कंस्ट्रक्टर को Application में लेने से लेकर डेटा सोर्स और कोरूटीन डिस्पैचर (जिसे आप #39; दोनों करते हैं) में बदलने से बदलें. आपको अपने टेस्ट के लिए भी स्वैप करना होगा - इसके बारे में कोरूटीन पर तीसरे लेसन के सेक्शन में ज़्यादा जानकारी दी गई है.

DefaultTasksRepository.kt

// REPLACE
class DefaultTasksRepository private constructor(application: Application) { // Rest of class }

// WITH

class DefaultTasksRepository(
    private val tasksRemoteDataSource: TasksDataSource,
    private val tasksLocalDataSource: TasksDataSource,
    private val ioDispatcher: CoroutineDispatcher = Dispatchers.IO) { // Rest of class }
  1. डिपेंडेंसी पास हो गई है. इसलिए, init तरीका हटाएं. आपको और डिपेंडेंसी बनाने की ज़रूरत नहीं है.
  2. पुराने इंस्टेंस वैरिएबल भी मिटाएं. आप कंस्ट्रक्टर में उन्हें परिभाषित कर रहे हैं:

DefaultTasksRepository.kt

// Delete these old variables
private val tasksRemoteDataSource: TasksDataSource
private val tasksLocalDataSource: TasksDataSource
private val ioDispatcher: CoroutineDispatcher = Dispatchers.IO
  1. आखिर में, नए कंस्ट्रक्टर का इस्तेमाल करने के लिए getRepository तरीका अपडेट करें:

DefaultTasksRepository.kt

    companion object {
        @Volatile
        private var INSTANCE: DefaultTasksRepository? = null

        fun getRepository(app: Application): DefaultTasksRepository {
            return INSTANCE ?: synchronized(this) {
                val database = Room.databaseBuilder(app,
                    ToDoDatabase::class.java, "Tasks.db")
                    .build()
                DefaultTasksRepository(TasksRemoteDataSource, TasksLocalDataSource(database.taskDao())).also {
                    INSTANCE = it
                }
            }
        }
    }

अब आप कंस्ट्रक्टर डिपेंडेंसी इंजेक्शन का इस्तेमाल कर रहे हैं!

दूसरा चरण: जांचों में अपने FakeDataSource का इस्तेमाल करना

अब आपका कोड कंस्ट्रक्टर डिपेंडेंसी का इस्तेमाल कर रहा है, इसलिए आपDefaultTasksRepository की जांच करने के लिए, अपने नकली डेटा स्रोत का इस्तेमाल कर सकते हैं.

  1. DefaultTasksRepository कक्षा के नाम पर दायां क्लिक करें और जनरेट करें चुनें. इसके बाद, जांच करें चुनें.
  2. DefaultTasksRepositoryTestटेस्ट स्रोत सेट में, DefaultTasksRepositoryTest बनाने के लिए निर्देशों का पालन करें.
  3. अपनी नई DefaultTasksRepositoryTest क्लास के ऊपर, अपने नकली डेटा सोर्स में डेटा दिखाने के लिए, मेंबर वैरिएबल जोड़ें.

DefaultTasksRepositoryTest.kt

    private val task1 = Task("Title1", "Description1")
    private val task2 = Task("Title2", "Description2")
    private val task3 = Task("Title3", "Description3")
    private val remoteTasks = listOf(task1, task2).sortedBy { it.id }
    private val localTasks = listOf(task3).sortedBy { it.id }
    private val newTasks = listOf(task3).sortedBy { it.id }
  1. तीन वैरिएबल बनाएं, दो FakeDataSource सदस्य वैरिएबल (आपके रिपॉज़िटरी के लिए हर डेटा स्रोत के लिए एक) और DefaultTasksRepository के लिए एक वैरिएबल बनाएं, जिसकी आप जांच करेंगे.

DefaultTasksRepositoryTest.kt

    private lateinit var tasksRemoteDataSource: FakeDataSource
    private lateinit var tasksLocalDataSource: FakeDataSource

    // Class under test
    private lateinit var tasksRepository: DefaultTasksRepository

टेस्ट करने लायक DefaultTasksRepository को सेट अप और शुरू करने का तरीका बनाएं. DefaultTasksRepository आपके टेस्ट डबल, FakeDataSource का इस्तेमाल करेगा.

  1. createRepository नाम का तरीका बनाएं और @Before के साथ इस बारे में बताएं.
  2. remoteTasks और localTasks सूचियों का इस्तेमाल करके, अपने नकली डेटा स्रोतों को इंस्टैंशिएट करें.
  3. अभी-अभी बनाए गए दो नकली डेटा स्रोतों और Dispatchers.Unconfined का इस्तेमाल करके, अपने tasksRepository को इंस्टैंशिएट करें.

आखिरी तरीका नीचे दिए गए कोड जैसा दिखना चाहिए.

DefaultTasksRepositoryTest.kt

    @Before
    fun createRepository() {
        tasksRemoteDataSource = FakeDataSource(remoteTasks.toMutableList())
        tasksLocalDataSource = FakeDataSource(localTasks.toMutableList())
        // Get a reference to the class under test
        tasksRepository = DefaultTasksRepository(
            // TODO Dispatchers.Unconfined should be replaced with Dispatchers.Main
            //  this requires understanding more about coroutines + testing
            //  so we will keep this as Unconfined for now.
            tasksRemoteDataSource, tasksLocalDataSource, Dispatchers.Unconfined
        )
    }

तीसरा चरण: DefaultTasksRepository getTasks() टेस्ट लिखना

DefaultTasksRepository टेस्ट लिखने का समय हो गया है!

  1. डेटा स्टोर करने की जगह's getTasks का तरीका लिखें. देखें कि जब आप true के साथ getTasks को कॉल करते हैं (यानी कि वह रिमोट डेटा सोर्स से फिर से लोड होता है), तो यह रिमोट डेटा सोर्स (न कि लोकल डेटा सोर्स) से डेटा दिखाता है.

DefaultTasksRepositoryTest.kt

@Test
    fun getTasks_requestsAllTasksFromRemoteDataSource(){
        // When tasks are requested from the tasks repository
        val tasks = tasksRepository.getTasks(true) as Success

        // Then tasks are loaded from the remote data source
        assertThat(tasks.data, IsEqual(remoteTasks))
    }

getTasks: को कॉल करने पर आपको गड़बड़ी का मैसेज मिलेगा

चौथा चरण: RunBlockTest को जोड़ना

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

  1. testImplementation का इस्तेमाल करके, टेस्ट सोर्स में कोरूटीन को टेस्ट करने के लिए ज़रूरी डिपेंडेंसी जोड़ें.

app/build.gradle

testImplementation "org.jetbrains.kotlinx:kotlinx-coroutines-test:$coroutinesVersion"

सिंक करना न भूलें!

kotlinx-coroutines-test कोरूटीन टेस्ट लाइब्रेरी है. यह खास तौर पर कोरूटीन को आज़माने के लिए है. अपनी जांच के लिए, runBlockingTest फ़ंक्शन का इस्तेमाल करें. यह कोरूटीन टेस्ट लाइब्रेरी से मिला एक फ़ंक्शन है. यह कोड का ब्लॉक ब्लॉक करता है और फिर कोड के इस ब्लॉक को एक खास कोरूटीन कॉन्टेक्स्ट में चलाता है, जो सिंक्रोनस और तुरंत चलता है. इसका मतलब है कि कार्रवाइयां तय किए गए क्रम में होंगी. इससे आपके कोरूटीन, नॉन-कोरूटीन की तरह चलते हैं, इसलिए यह टेस्ट कोड के लिए है.

जब आप suspend फ़ंक्शन को कॉल करते हैं, तो अपनी जांच क्लास में runBlockingTest का इस्तेमाल करें. आप इस सीरीज़ की अगली कोडलैब (कोड बनाना सीखना) में runBlockingTest के काम करने के तरीके और टेस्ट कोरूटीन को टेस्ट करने के तरीके के बारे में ज़्यादा जान पाएंगे.

  1. कक्षा के ऊपर @ExperimentalCoroutinesApi जोड़ें. इससे यह पता चलता है कि आप जानते हैं कि आप क्लास में प्रयोग के लिए कोरूटीन एपीआई (runBlockingTest) का इस्तेमाल कर रहे हैं. इसके बिना, आपको एक चेतावनी मिलेगी.
  2. DefaultTasksRepositoryTest में जाकर, runBlockingTest जोड़ें, ताकि यह आपकी पूरी जांच को कोड के रूप में &कोटेशन की सुविधा के तौर पर ले जाए

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

DefaultTasksRepositoryTest.kt

import com.example.android.architecture.blueprints.todoapp.data.Result.Success
import com.example.android.architecture.blueprints.todoapp.data.Task
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.runBlockingTest
import org.hamcrest.core.IsEqual
import org.junit.Assert.*
import org.junit.Before
import org.junit.Test


@ExperimentalCoroutinesApi
class DefaultTasksRepositoryTest {

    private val task1 = Task("Title1", "Description1")
    private val task2 = Task("Title2", "Description2")
    private val task3 = Task("Title3", "Description3")
    private val remoteTasks = listOf(task1, task2).sortedBy { it.id }
    private val localTasks = listOf(task3).sortedBy { it.id }
    private val newTasks = listOf(task3).sortedBy { it.id }

    private lateinit var tasksRemoteDataSource: FakeDataSource
    private lateinit var tasksLocalDataSource: FakeDataSource

    // Class under test
    private lateinit var tasksRepository: DefaultTasksRepository

    @Before
    fun createRepository() {
        tasksRemoteDataSource = FakeDataSource(remoteTasks.toMutableList())
        tasksLocalDataSource = FakeDataSource(localTasks.toMutableList())
        // Get a reference to the class under test
        tasksRepository = DefaultTasksRepository(
            // TODO Dispatchers.Unconfined should be replaced with Dispatchers.Main
            //  this requires understanding more about coroutines + testing
            //  so we will keep this as Unconfined for now.
            tasksRemoteDataSource, tasksLocalDataSource, Dispatchers.Unconfined
        )
    }

    @Test
    fun getTasks_requestsAllTasksFromRemoteDataSource() = runBlockingTest {
        // When tasks are requested from the tasks repository
        val tasks = tasksRepository.getTasks(true) as Success

        // Then tasks are loaded from the remote data source
        assertThat(tasks.data, IsEqual(remoteTasks))
    }

}
  1. अपना नया getTasks_requestsAllTasksFromRemoteDataSource टेस्ट करें और पुष्टि करें कि यह काम करता है और गड़बड़ी खत्म हो गई है!

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

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

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

इस टास्क में, मॉडल देखने के लिए डिपेंडेंसी इंजेक्शन लागू किया जाता है.

पहला चरण. TasksRepository इंटरफ़ेस बनाएं

कंस्ट्रक्टर डिपेंडेंसी इंजेक्शन का इस्तेमाल करने के लिए, सबसे पहले सामान्य और नकली क्लास के बीच एक सामान्य इंटरफ़ेस बनाना होता है.

यह कैसे काम करता है? TasksRemoteDataSource, TasksLocalDataSource, और FakeDataSource देखें और ध्यान दें कि उन सभी का इंटरफ़ेस एक ही है: TasksDataSource. इससे आप DefaultTasksRepository के कंस्ट्रक्टर में यह बता सकते हैं कि आप TasksDataSource का इस्तेमाल करते हैं.

DefaultTasksRepository.kt

class DefaultTasksRepository(
   private val tasksRemoteDataSource: TasksDataSource,
   private val tasksLocalDataSource: TasksDataSource,
   private val ioDispatcher: CoroutineDispatcher = Dispatchers.IO) {

इस तरह हमें आपके FakeDataSource में बदलने की सुविधा मिलती है!

इसके बाद, DefaultTasksRepository के लिए इंटरफ़ेस बनाएं, जैसा कि आपने डेटा सोर्स के लिए किया था. इसमें DefaultTasksRepository के सभी सार्वजनिक तरीके (सार्वजनिक एपीआई प्लैटफ़ॉर्म) शामिल होने चाहिए.

  1. DefaultTasksRepository खोलें और कक्षा के नाम पर दायां क्लिक करें. इसके बाद, रिफ़ैक्टर -> एक्स्ट्रैक्ट -> इंटरफ़ेस चुनें.

  1. अलग फ़ाइल के लिए निकालें चुनें.

  1. एक्सट्रैक्ट इंटरफ़ेस विंडो में, इंटरफ़ेस का नाम बदलकर TasksRepository करें.
  2. फ़ॉर्म के लिए सदस्य बनाएं सेक्शन में, दो साथी सदस्यों और निजी तरीकों के अलावा सभी सदस्यों की जांच करें.


  1. रेफ़रर पर क्लिक करें. नया TasksRepository इंटरफ़ेस, डेटा/स्रोत पैकेज में दिखना चाहिए.

साथ ही, DefaultTasksRepository अब TasksRepository को लागू करता है.

  1. अपना ऐप्लिकेशन अपना ऐप्लिकेशन (न कि जांच) चलाएं, ताकि यह पक्का हो सके कि सब कुछ अब भी ठीक से काम कर रहा है.

दूसरा चरण. FakeTestRepository बनाएं

अब जबकि आपके पास इंटरफ़ेस है, तो आप DefaultTaskRepository टेस्ट डबल बना सकते हैं.

  1. डेटा स्रोत सेट में, डेटा/स्रोत में Kotlin फ़ाइल और क्लास FakeTestRepository.kt बनाएं और इसे TasksRepository इंटरफ़ेस से बढ़ाएं.

FakeTestRepository.kt

class FakeTestRepository : TasksRepository  {
}

आपसे कहा जाएगा कि आपको इंटरफ़ेस के तरीके लागू करने होंगे.

  1. गड़बड़ी वाला मेन्यू दिखने तक गड़बड़ी पर माउस घुमाएं. इसके बाद, सदस्यों को लागू करें पर क्लिक करके उसे चुनें.
  1. सभी तरीकों को चुनें और ठीक है दबाएं.

तीसरा चरण. FakeTestRepository तरीके लागू करें

अब आपकी FakeTestRepository कक्षा में &कोटेशन लागू नहीं हुआ है. FakeDataSource को लागू करने के तरीके की तरह ही, FakeTestRepository का इस्तेमाल स्थानीय और रिमोट डेटा स्रोतों के बीच मुश्किल मीडिएशन से निपटने के बजाय, डेटा स्ट्रक्चर के हिसाब से किया जाएगा.

ध्यान दें कि FakeTestRepository को FakeDataSource या इस तरह की किसी चीज़ का इस्तेमाल करने की ज़रूरत नहीं होती; यह सिर्फ़ इनपुट के तौर पर असली नकली आउटपुट देने की ज़रूरत होती है. आप टास्क की सूची सेव करने के लिए LinkedHashMap और निगरानी वाले अपने टास्क के लिए, MutableLiveData इस्तेमाल करेंगे.

  1. FakeTestRepository में, LinkedHashMap वैरिएबल जोड़ें. इसमें टास्क की मौजूदा सूची को दिखाने वाला टास्क और आपके निगरानी किए जा सकने वाले टास्क के लिए MutableLiveData का इस्तेमाल किया जाता है.

FakeTestRepository.kt

class FakeTestRepository : TasksRepository {

    var tasksServiceData: LinkedHashMap<String, Task> = LinkedHashMap()

    private val observableTasks = MutableLiveData<Result<List<Task>>>()


    // Rest of class
}

इन तरीकों का इस्तेमाल करें:

  1. getTasks—इस तरीके को tasksServiceData का इस्तेमाल करके, tasksServiceData.values.toList() का इस्तेमाल करके एक सूची में बदलना चाहिए. इसके बाद, इसे Success नतीजे के तौर पर दिखाना चाहिए.
  2. refreshTasksobservableTasks की वैल्यू को getTasks() से अपडेट कर देता है.
  3. observeTasksrunBlocking का इस्तेमाल करके कोरूटीन बनाता है और refreshTasks चलाता है. इसके बाद, observableTasks दिखाता है.

नीचे उन तरीकों के लिए कोड दिया गया है.

FakeTestRepository.kt

class FakeTestRepository : TasksRepository {

    var tasksServiceData: LinkedHashMap<String, Task> = LinkedHashMap()

    private val observableTasks = MutableLiveData<Result<List<Task>>>()

    override suspend fun getTasks(forceUpdate: Boolean): Result<List<Task>> {
        return Result.Success(tasksServiceData.values.toList())
    }

    override suspend fun refreshTasks() {
        observableTasks.value = getTasks()
    }

    override fun observeTasks(): LiveData<Result<List<Task>>> {
        runBlocking { refreshTasks() }
        return observableTasks
    }

    // Rest of class

}

चरण 4. जोड़ने के लिए परीक्षण के लिए कोई तरीका जोड़ें

जांच करते समय, बेहतर होगा कि आप अपने डेटा स्टोर करने की जगह में पहले से कुछ Tasks रखें. आप saveTask को कई बार कॉल कर सकते हैं, लेकिन इस काम को आसान बनाने के लिए, खास तौर पर टेस्ट के लिए हेल्पर वाला तरीका जोड़ें. इससे, आपको टास्क जोड़ने में मदद मिलती है.

  1. addTasks तरीका जोड़ें. यह vararg टास्क में होता है और हर एक को HashMap में जोड़ देता है. इसके बाद, टास्क रीफ़्रेश करता है.

FakeTestRepository.kt

    fun addTasks(vararg tasks: Task) {
        for (task in tasks) {
            tasksServiceData[task.id] = task
        }
        runBlocking { refreshTasks() }
    }

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

इस टास्क में, आप ViewModel में मौजूद नकली क्लास का इस्तेमाल करते हैं. TasksViewModel's कंस्ट्रक्टर में TasksRepository वैरिएबल जोड़कर, कंस्ट्रक्टर डिपेंडेंसी इंजेक्शन से दो डेटा सोर्स में लेने के लिए, कंस्ट्रक्टर डिपेंडेंसी इंजेक्शन का इस्तेमाल करें.

यह प्रोसेस, व्यू मॉडल से थोड़ी अलग है, क्योंकि आप उन्हें सीधे नहीं बनाते. उदाहरण के लिए:

class TasksFragment : Fragment() {

    private val viewModel by viewModels<TasksViewModel>()
    
    // Rest of class...

}


ऊपर दिए गए कोड की तरह, आप viewModel'sप्रॉपर्टी का प्रतिनिधि इस्तेमाल करके, व्यू मॉडल बना सकते हैं. व्यू मॉडल के बनाने का तरीका बदलने के लिए, आपको ViewModelProvider.Factory जोड़ना होगा और उसका इस्तेमाल करना होगा. अगर आप ViewModelProvider.Factory# नहीं जानते, तो इसके बारे में यहां ज़्यादा जानें.

पहला चरण. TasksViewModel में ViewModelFactory बनाना और उसका इस्तेमाल करना

आप Tasks स्क्रीन से जुड़ी कक्षाओं और टेस्ट को अपडेट करके शुरू करते हैं.

  1. TasksViewModel खोलें.
  2. TasksRepository को क्लास में लेने के बजाय, TasksViewModel के कंस्ट्रक्टर में बदलाव करें.

TasksViewModel.kt

// REPLACE
class TasksViewModel(application: Application) : AndroidViewModel(application) {

    private val tasksRepository = DefaultTasksRepository.getRepository(application)

    // Rest of class
}

// WITH

class TasksViewModel( private val tasksRepository: TasksRepository ) : ViewModel() { 
    // Rest of class 
}

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

  1. कक्षा के बाहर, TasksViewModel फ़ाइल के नीचे, TasksViewModelFactory जोड़ें. यह TasksRepository तक जाती है.

TasksViewModel.kt

@Suppress("UNCHECKED_CAST")
class TasksViewModelFactory (
    private val tasksRepository: TasksRepository
) : ViewModelProvider.NewInstanceFactory() {
    override fun <T : ViewModel> create(modelClass: Class<T>) =
        (TasksViewModel(tasksRepository) as T)
}


यह ViewModel बनाने के तरीके को बदलने का स्टैंडर्ड तरीका है. अब आपके पास फ़ैक्ट्री है, इसलिए जहां भी आप अपना व्यू मॉडल बनाएं, उसका इस्तेमाल करें.

  1. फ़ैक्ट्री का इस्तेमाल करने के लिए TasksFragment अपडेट करें.

TasksFragment.kt

// REPLACE
private val viewModel by viewModels<TasksViewModel>()

// WITH

private val viewModel by viewModels<TasksViewModel> {
    TasksViewModelFactory(DefaultTasksRepository.getRepository(requireActivity().application))
}
  1. अपना ऐप्लिकेशन कोड चलाएं और पक्का करें कि सब कुछ अब भी काम कर रहा हो!

दूसरा चरण. TasksViewModelTest में, FakeTestRepository का इस्तेमाल करना

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

  1. TasksViewModelTest ऊपर खोलें.
  2. TasksViewModelTest में FakeTestRepository प्रॉपर्टी जोड़ें.

TaskViewModelTest.kt

@RunWith(AndroidJUnit4::class)
class TasksViewModelTest {

    // Use a fake repository to be injected into the viewmodel
    private lateinit var tasksRepository: FakeTestRepository
    
    // Rest of class
}
  1. तीन टास्क वाला FakeTestRepository बनाने के लिए, setupViewModel का तरीका अपडेट करें. इसके बाद, इस रिपॉज़िटरी के साथ tasksViewModel बनाएं.

TasksViewModelTest.kt

    @Before
    fun setupViewModel() {
        // We initialise the tasks to 3, with one active and two completed
        tasksRepository = FakeTestRepository()
        val task1 = Task("Title1", "Description1")
        val task2 = Task("Title2", "Description2", true)
        val task3 = Task("Title3", "Description3", true)
        tasksRepository.addTasks(task1, task2, task3)

        tasksViewModel = TasksViewModel(tasksRepository)
        
    }
  1. अब आप AndroidX टेस्ट ApplicationProvider.getApplicationContext कोड का इस्तेमाल नहीं कर रहे हैं. इसलिए, आप @RunWith(AndroidJUnit4::class) एनोटेशन भी हटा सकते हैं.
  2. अपने टेस्ट करें. पक्का करें कि वे सभी अब भी काम कर रहे हों!

कंस्ट्रक्टर डिपेंडेंसी इंजेक्शन का इस्तेमाल करके, आपने'DefaultTasksRepository को डिपेंडेंसी के तौर पर हटा दिया है. साथ ही, टेस्ट में इसे FakeTestRepository से बदल दिया है.

तीसरा चरण. TaskDetail का फ़्रैगमेंट और ViewModel भी अपडेट करें

TaskDetailFragment और TaskDetailViewModel के लिए एक जैसे बदलाव करें. आगे से, TaskDetail टेस्ट लिखने पर यह कोड तैयार हो जाएगा.

  1. TaskDetailViewModel खोलें.
  2. कंस्ट्रक्टर अपडेट करें:

TaskDetailViewModel.kt

// REPLACE
class TaskDetailViewModel(application: Application) : AndroidViewModel(application) {

    private val tasksRepository = DefaultTasksRepository.getRepository(application)

    // Rest of class
}

// WITH

class TaskDetailViewModel(
    private val tasksRepository: TasksRepository
) : ViewModel() { // Rest of class }
  1. TaskDetailViewModel फ़ाइल के सबसे नीचे, कक्षा के बाहर, एक TaskDetailViewModelFactory जोड़ें.

TaskDetailViewModel.kt

@Suppress("UNCHECKED_CAST")
class TaskDetailViewModelFactory (
    private val tasksRepository: TasksRepository
) : ViewModelProvider.NewInstanceFactory() {
    override fun <T : ViewModel> create(modelClass: Class<T>) =
        (TaskDetailViewModel(tasksRepository) as T)
}
  1. फ़ैक्ट्री का इस्तेमाल करने के लिए TasksFragment अपडेट करें.

TasksFragment.kt

// REPLACE
private val viewModel by viewModels<TaskDetailViewModel>()

// WITH

private val viewModel by viewModels<TaskDetailViewModel> {
    TaskDetailViewModelFactory(DefaultTasksRepository.getRepository(requireActivity().application))
}
  1. अपना कोड चलाएं और पक्का करें कि सभी चीज़ें काम कर रही हों.

अब आप TasksFragment और TasksDetailFragment में वास्तविक डेटा संग्रह स्थान के बजाय FakeTestRepository का इस्तेमाल कर सकते हैं.

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

  • ServiceLocator पैटर्न
  • एस्प्रेसो और मॉकिटो लाइब्रेरी

इंटिग्रेशन टेस्ट कई कक्षाओं के इंटरैक्शन का परीक्षण करके यह पक्का करते हैं कि वे एक साथ उपयोग किए जाने पर अपेक्षित तरीके से काम कर रहे हैं. ये टेस्ट, स्थानीय तौर पर (test सोर्स सेट) या इंस्ट्रूमेंटेशन टेस्ट (androidTest सोर्स सेट) के रूप में चलाए जा सकते हैं.

आपके केस में #39

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

  1. ये ग्रेडल डिपेंडेंसी जोड़ें.

app/build.gradle

    // Dependencies for Android instrumented unit tests
    androidTestImplementation "junit:junit:$junitVersion"
    androidTestImplementation "org.jetbrains.kotlinx:kotlinx-coroutines-test:$coroutinesVersion"

    // Testing code should not be included in the main code.
    // Once https://issuetracker.google.com/128612536 is fixed this can be fixed.

    implementation "androidx.fragment:fragment-testing:$fragmentVersion"
    implementation "androidx.test:core:$androidXTestCoreVersion"

इन डिपेंडेंसी में शामिल हैं:

  • junit:junit—JUnit, जो बेसिक टेस्ट स्टेटमेंट लिखने के लिए ज़रूरी है.
  • androidx.test:core—कोर AndroidX टेस्ट लाइब्रेरी
  • kotlinx-coroutines-test—कोरूटीन टेस्टिंग लाइब्रेरी
  • androidx.fragment:fragment-testing—टेस्ट में फ़्रैगमेंट बनाने और उनकी स्थिति बदलने के लिए, AndroidX टेस्ट लाइब्रेरी.

आप अपने androidTest सोर्स सेट में इन लाइब्रेरी का इस्तेमाल करेंगे. इसलिए, इन्हें डिपेंडेंसी के तौर पर जोड़ने के लिए androidTestImplementation का इस्तेमाल करें.

दूसरा चरण. TaskDetailFragmentTest क्लास बनाएं

TaskDetailFragment में, किसी एक टास्क के बारे में जानकारी दिखती है.

आप TaskDetailFragment के लिए, फ़्रैगमेंट टेस्ट लिखना शुरू करेंगे. ऐसा इसलिए, क्योंकि इसमें अन्य फ़्रैगमेंट की तुलना में काफ़ी बुनियादी फ़ंक्शन है.

  1. taskdetail.TaskDetailFragment खोलें.
  2. TaskDetailFragment के लिए एक टेस्ट जनरेट करें, जैसा कि आपने पहले किया है'. डिफ़ॉल्ट विकल्पों को स्वीकार करें और उन्हें androidTest सोर्स सेट में रखें (test सोर्स सेट पर नहीं).

  1. इन एनोटेशन को TaskDetailFragmentTest क्लास में जोड़ें.

TaskDetailFragmentTest.kt

@MediumTest
@RunWith(AndroidJUnit4::class)
class TaskDetailFragmentTest {

}

इस एनोटेशन का मकसद यह है:

  • @MediumTest—जांच को "मध्यम रन-टाइम और कोट; इंटिग्रेशन टेस्ट (@SmallTest यूनिट टेस्ट और @LargeTest बड़े पैमाने पर एंड-टू-डेट टेस्ट) के तौर पर मार्क करता है. इसकी मदद से आप ग्रुप बना सकते हैं और यह चुन सकते हैं कि किस साइज़ की जांच करनी है.
  • @RunWith(AndroidJUnit4::class)—AndroidX टेस्ट का इस्तेमाल करके, किसी भी कक्षा में इस्तेमाल किया जाता है.

तीसरा चरण. किसी टेस्ट से फ़्रैगमेंट लॉन्च करना

इस टास्क में, आप TaskDetailFragment को AndroidX टेस्टिंग लाइब्रेरी का इस्तेमाल करके लॉन्च करने वाले हैं. FragmentScenario, AndroidX टेस्ट की एक क्लास है, जो फ़्रैगमेंट के आस-पास रैप करती है और आपको फ़्रैगमेंट' के लाइफ़साइकल पर सीधे कंट्रोल देती है. फ़्रैगमेंट के लिए टेस्ट लिखने के लिए, FragmentScenario आपके बनाए गए फ़्रैगमेंट के लिए टेस्ट किया जाता है're टेस्टिंग (TaskDetailFragment).

  1. इस जांच को TaskDetailFragmentTest में कॉपी करें.

TaskDetailFragmentTest.kt

    @Test
    fun activeTaskDetails_DisplayedInUi() {
        // GIVEN - Add active (incomplete) task to the DB
        val activeTask = Task("Active Task", "AndroidX Rocks", false)

        // WHEN - Details fragment launched to display task
        val bundle = TaskDetailFragmentArgs(activeTask.id).toBundle()
        launchFragmentInContainer<TaskDetailFragment>(bundle, R.style.AppTheme)

    }

यह कोड ऊपर दिया गया है:

  • टास्क बनाता है.
  • Bundle बनाता है, जो फ़्रैगमेंट में भेजे जाने वाले टास्क के लिए फ़्रैगमेंट आर्ग्युमेंट दिखाता है.
  • launchFragmentInContainer फ़ंक्शन, इस बंडल और थीम के साथ एक FragmentScenario बनाता है.

यह अभी तक एक पूरा परीक्षण नहीं है, क्योंकि यह किसी भी चीज़ का दावा नहीं कर रहा है. अभी के लिए, जांच करें और देखें कि क्या होता है.

  1. यह इंस्ट्रूमेंटल टेस्ट है, इसलिए पक्का करें कि एम्युलेटर या आपका डिवाइस दिखे.
  2. जांच चलाएं.

कुछ चीज़ें होनी चाहिए.

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

आखिर में, ध्यान से देखें कि फ़्रैगमेंट में &डेटा नहीं है &कोटेशन; क्योंकि यह काम का डेटा लोड नहीं करता.

आपकी जांच में दोनों को TaskDetailFragment (जो आपने और #39;हो गया है) लोड करना होगा. साथ ही, यह दावा भी करना होगा कि डेटा सही तरीके से लोड किया गया है. डेटा क्यों नहीं है? ऐसा इसलिए है, क्योंकि आपने टास्क बनाया था, लेकिन उसे 'डेटा स्टोर करने की जगह' में सेव नहीं किया था.

    @Test
    fun activeTaskDetails_DisplayedInUi() {
        // This DOES NOT save the task anywhere
        val activeTask = Task("Active Task", "AndroidX Rocks", false)

        val bundle = TaskDetailFragmentArgs(activeTask.id).toBundle()
        launchFragmentInContainer<TaskDetailFragment>(bundle, R.style.AppTheme)

    }

आपके पास यह FakeTestRepository है, लेकिन आपको अपने फ़्रैगमेंट के लिए अपने असली रिपॉज़िटरी को बदलने के लिए कुछ तरीके चाहिए. इसके बाद यह काम आप करेंगे!

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

आप यहां कंस्ट्रक्टर डिपेंडेंसी इंजेक्शन का इस्तेमाल तब तक नहीं कर सकते, जैसा कि आपने पहले किया था. व्यू मॉडल या रिपॉज़िटरी में डिपेंडेंसी देने के लिए, ऐसा करना ज़रूरी था. कंस्ट्रक्टर डिपेंडेंसी इंजेक्शन के लिए, क्लास क्लास बनाना ज़रूरी है. फ़्रैगमेंट और गतिविधियां, उन क्लास के उदाहरण हैं जिन्हें आप नहीं बनाते और #39; नहीं बनाते. आम तौर पर, आपके पास #33 का ऐक्सेस नहीं होता.

फ़्रैगमेंट को बनाने के लिए आपको #33; नहीं बनाना है, इसलिए आप रिपॉज़िटरी टेस्ट डबल (FakeTestRepository) को फ़्रैगमेंट में बदलने के लिए, कंस्ट्रक्टर डिपेंडेंसी का इस्तेमाल नहीं कर सकते. इसके बजाय, सेवा लोकेटर पैटर्न का इस्तेमाल करें. सर्विस लोकेटर पैटर्न, डिपेंडेंसी इंजेक्शन के लिए विकल्प है. इसमें &tot;सेवा लोकेटर&कोटेशन नाम की सिंगलटन क्लास बनाना है, जिसका मकसद सामान्य और टेस्ट कोड, दोनों के लिए डिपेंडेंसी देना है. सामान्य ऐप्लिकेशन कोड (main सोर्स सेट) में, ये सभी डिपेंडेंसी नियमित ऐप्लिकेशन डिपेंडेंसी हैं. जांच के लिए, आप सेवा लोकेटर में बदलाव करते हैं, ताकि डिपेंडेंसी के टेस्ट के लिए दो बार वर्शन उपलब्ध कराए जा सकें.

सेवा लोकेटर का इस्तेमाल नहीं करना


सेवा लोकेटर का इस्तेमाल करना

इस कोडलैब के लिए, ये काम करें:

  1. रिसोर्स स्टोर बनाने और सेव करने के लिए, सेवा लोकेटर क्लास बनाएं. डिफ़ॉल्ट रूप से, यह एक "सामान्य&कोटेशन; डेटा स्टोर करने की जगह बनाता है.
  2. अपने कोड को रीफ़ैक्टर करें, ताकि जब आपको रिपॉज़िटरी की ज़रूरत हो, तो सर्विस लोकेटर का इस्तेमाल करें.
  3. अपनी टेस्टिंग क्लास में, सेवा लोकेटर पर एक ऐसे तरीके को कॉल करें जो &&tt>सामान्य&कोटेशन को, आपके टेस्ट डबल से बदल देता है.

पहला चरण. ServiceLocator बनाएं

आइए ServiceLocator की एक क्लास बनाते हैं. यह बाकी ऐप्लिकेशन कोड के साथ मुख्य स्रोत सेट में लाइव रहेगा, क्योंकि यह मुख्य ऐप्लिकेशन कोड में इस्तेमाल होता है.

ध्यान दें: ServiceLocator एक सिंगलटन है, इसलिए क्लास के लिए Kotlin object कीवर्ड का इस्तेमाल करें.

  1. मुख्य स्रोत सेट के सबसे ऊपर के लेवल पर ServiceLocator.kt फ़ाइल बनाएं.
  2. ServiceLocator नाम की object तय करें.
  3. database और repository इंस्टेंस वैरिएबल बनाएं और दोनों को null पर सेट करें.
  4. @Volatile के साथ रिपॉज़िटरी के बारे में बताएं, क्योंकि इसे कई थ्रेड में इस्तेमाल किया जा सकता है (@Volatile को यहां बताया गया है).

आपका कोड नीचे दिखाए गए कोड के मुताबिक दिखना चाहिए.

object ServiceLocator {

    private var database: ToDoDatabase? = null
    @Volatile
    var tasksRepository: TasksRepository? = null

}

फ़िलहाल, ServiceLocator को सिर्फ़ TasksRepository में आने का तरीका जानना है. यह पहले से मौजूद DefaultTasksRepository का इस्तेमाल करेगा या ज़रूरत पड़ने पर, नया DefaultTasksRepository करेगा और वापस करेगा.

ये फ़ंक्शन तय करें:

  1. provideTasksRepository—इनमें से कोई भी डेटा स्टोर करने की पहले से मौजूदा जगह उपलब्ध है या नई जगह बनाई जाती है. इस तरीके का इस्तेमाल करने के लिए, this पर synchronized होना चाहिए. ऐसा तब होता है, जब एक से ज़्यादा थ्रेड चल रहे हों. ऐसे में, कभी-कभी डेटा स्टोर करने के दो इंस्टेंस गलती से बन जाते हैं.
  2. createTasksRepository—डेटा स्टोर करने की नई जगह बनाने के लिए कोड. createTaskLocalDataSource पर कॉल करेगा और एक नया TasksRemoteDataSource बनाएगा.
  3. createTaskLocalDataSource—नया स्थानीय डेटा सोर्स बनाने के लिए कोड. createDataBase पर कॉल किया जाएगा.
  4. createDataBase—नया डेटाबेस बनाने के लिए कोड.

पूरा हो चुका कोड नीचे दिया गया है.

ServiceLocator.kt

object ServiceLocator {

    private var database: ToDoDatabase? = null
    @Volatile
    var tasksRepository: TasksRepository? = null

    fun provideTasksRepository(context: Context): TasksRepository {
        synchronized(this) {
            return tasksRepository ?: createTasksRepository(context)
        }
    }

    private fun createTasksRepository(context: Context): TasksRepository {
        val newRepo = DefaultTasksRepository(TasksRemoteDataSource, createTaskLocalDataSource(context))
        tasksRepository = newRepo
        return newRepo
    }

    private fun createTaskLocalDataSource(context: Context): TasksDataSource {
        val database = database ?: createDataBase(context)
        return TasksLocalDataSource(database.taskDao())
    }

    private fun createDataBase(context: Context): ToDoDatabase {
        val result = Room.databaseBuilder(
            context.applicationContext,
            ToDoDatabase::class.java, "Tasks.db"
        ).build()
        database = result
        return result
    }
}

दूसरा चरण. ऐप्लिकेशन में ServiceLocator का इस्तेमाल करें

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

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

  1. अपने पैकेज के क्रम के सबसे ऊपर वाले लेवल पर TodoApplication खोलें और अपने रिपॉज़िटरी के लिए val बनाएं और इसे ServiceLocator.provideTaskRepository का इस्तेमाल करके हासिल किया जाने वाला रिपॉज़िटरी असाइन करें.

TodoApplication.kt

class TodoApplication : Application() {

    val taskRepository: TasksRepository
        get() = ServiceLocator.provideTasksRepository(this)

    override fun onCreate() {
        super.onCreate()
        if (BuildConfig.DEBUG) Timber.plant(DebugTree())
    }
}

अब आपने ऐप्लिकेशन में रिपॉज़िटरी बना लिया है, इसलिए आप DefaultTasksRepository में getRepository का पुराना तरीका हटा सकते हैं.

  1. DefaultTasksRepository खोलें और कंपैनियन ऑब्जेक्ट को मिटाएं.

DefaultTasksRepository.kt

// DELETE THIS COMPANION OBJECT
companion object {
    @Volatile
    private var INSTANCE: DefaultTasksRepository? = null

    fun getRepository(app: Application): DefaultTasksRepository {
        return INSTANCE ?: synchronized(this) {
            val database = Room.databaseBuilder(app,
                ToDoDatabase::class.java, "Tasks.db")
                .build()
            DefaultTasksRepository(TasksRemoteDataSource, TasksLocalDataSource(database.taskDao())).also {
                INSTANCE = it
            }
        }
    }
}

अब आप जहां भी getRepository का इस्तेमाल कर रहे थे, अब उसके बजाय ऐप्लिकेशन का #taskRepository इस्तेमाल करें. इससे यह पक्का हो जाता है कि सीधे तौर पर रिपॉज़िटरी बनाने के बजाय, आपको वह डेटा स्टोर मिल रहा है जो ServiceLocator ने दिया है.

  1. TaskDetailFragement खोलें और कक्षा में सबसे ऊपर मौजूद getRepository को कॉल करें.
  2. इस कॉल को ऐसे कॉल से बदलें जो TodoApplication से डेटा पाने की जगह लेता है.

TaskDetailFragment.kt

// REPLACE this code
private val viewModel by viewModels<TaskDetailViewModel> {
    TaskDetailViewModelFactory(DefaultTasksRepository.getRepository(requireActivity().application))
}

// WITH this code

private val viewModel by viewModels<TaskDetailViewModel> {
    TaskDetailViewModelFactory((requireContext().applicationContext as TodoApplication).taskRepository)
}
  1. TasksFragment के लिए भी यही करें.

TasksFragment.kt

// REPLACE this code
    private val viewModel by viewModels<TasksViewModel> {
        TasksViewModelFactory(DefaultTasksRepository.getRepository(requireActivity().application))
    }


// WITH this code

    private val viewModel by viewModels<TasksViewModel> {
        TasksViewModelFactory((requireContext().applicationContext as TodoApplication).taskRepository)
    }
  1. StatisticsViewModel और AddEditTaskViewModel के लिए, वह कोड अपडेट करें जो रिपॉज़िटरी को हासिल करता है, ताकि TodoApplication से रिपॉज़िटरी का इस्तेमाल किया जा सके.

TasksFragment.kt

// REPLACE this code
    private val tasksRepository = DefaultTasksRepository.getRepository(application)



// WITH this code

    private val tasksRepository = (application as TodoApplication).taskRepository

  1. अपना ऐप्लिकेशन चलाएं (टेस्ट नहीं)!

आपको सिर्फ़ सुरक्षा के फ़ायदे मिलेंगे, इसलिए ऐप्लिकेशन में कोई समस्या नहीं होनी चाहिए.

तीसरा चरण. FakeAndroidTestRepository बनाएं

टेस्ट सोर्स सेट में FakeTestRepository पहले से मौजूद है. आप डिफ़ॉल्ट रूप से test और androidTest सोर्स सेट के बीच टेस्ट क्लास शेयर नहीं कर सकते. इसलिए, आपको androidTest सोर्स सेट में एक डुप्लीकेट FakeTestRepository क्लास बनानी होगी और उसे FakeAndroidTestRepository नाम देना होगा.

  1. androidTest सोर्स सेट पर दायां क्लिक करें और डेटा पैकेज बनाएं. फिर से दायां क्लिक करें और सोर्स पैकेज बनाएं.
  2. इस स्रोत पैकेज में FakeAndroidTestRepository.kt नाम की एक नई क्लास बनाएं.
  3. दिए गए कोड को उस कक्षा में कॉपी करें.

FakeAndroidTestRepository.kt

import androidx.annotation.VisibleForTesting
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.map
import com.example.android.architecture.blueprints.todoapp.data.Result
import com.example.android.architecture.blueprints.todoapp.data.Result.Error
import com.example.android.architecture.blueprints.todoapp.data.Result.Success
import com.example.android.architecture.blueprints.todoapp.data.Task
import kotlinx.coroutines.runBlocking
import java.util.LinkedHashMap



class FakeAndroidTestRepository : TasksRepository {

    var tasksServiceData: LinkedHashMap<String, Task> = LinkedHashMap()

    private var shouldReturnError = false

    private val observableTasks = MutableLiveData<Result<List<Task>>>()

    fun setReturnError(value: Boolean) {
        shouldReturnError = value
    }

    override suspend fun refreshTasks() {
        observableTasks.value = getTasks()
    }

    override suspend fun refreshTask(taskId: String) {
        refreshTasks()
    }

    override fun observeTasks(): LiveData<Result<List<Task>>> {
        runBlocking { refreshTasks() }
        return observableTasks
    }

    override fun observeTask(taskId: String): LiveData<Result<Task>> {
        runBlocking { refreshTasks() }
        return observableTasks.map { tasks ->
            when (tasks) {
                is Result.Loading -> Result.Loading
                is Error -> Error(tasks.exception)
                is Success -> {
                    val task = tasks.data.firstOrNull() { it.id == taskId }
                        ?: return@map Error(Exception("Not found"))
                    Success(task)
                }
            }
        }
    }

    override suspend fun getTask(taskId: String, forceUpdate: Boolean): Result<Task> {
        if (shouldReturnError) {
            return Error(Exception("Test exception"))
        }
        tasksServiceData[taskId]?.let {
            return Success(it)
        }
        return Error(Exception("Could not find task"))
    }

    override suspend fun getTasks(forceUpdate: Boolean): Result<List<Task>> {
        if (shouldReturnError) {
            return Error(Exception("Test exception"))
        }
        return Success(tasksServiceData.values.toList())
    }

    override suspend fun saveTask(task: Task) {
        tasksServiceData[task.id] = task
    }

    override suspend fun completeTask(task: Task) {
        val completedTask = Task(task.title, task.description, true, task.id)
        tasksServiceData[task.id] = completedTask
    }

    override suspend fun completeTask(taskId: String) {
        // Not required for the remote data source.
        throw NotImplementedError()
    }

    override suspend fun activateTask(task: Task) {
        val activeTask = Task(task.title, task.description, false, task.id)
        tasksServiceData[task.id] = activeTask
    }

    override suspend fun activateTask(taskId: String) {
        throw NotImplementedError()
    }

    override suspend fun clearCompletedTasks() {
        tasksServiceData = tasksServiceData.filterValues {
            !it.isCompleted
        } as LinkedHashMap<String, Task>
    }

    override suspend fun deleteTask(taskId: String) {
        tasksServiceData.remove(taskId)
        refreshTasks()
    }

    override suspend fun deleteAllTasks() {
        tasksServiceData.clear()
        refreshTasks()
    }

   
    fun addTasks(vararg tasks: Task) {
        for (task in tasks) {
            tasksServiceData[task.id] = task
        }
        runBlocking { refreshTasks() }
    }
}

चरण 4. जांच के लिए अपना ServiceLocator तैयार करें

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

  1. ServiceLocator.kt खोलें.
  2. tasksRepository के लिए सेटर को @VisibleForTesting के तौर पर मार्क करें. यह एनोटेशन यह बताने का एक तरीका है कि जांच करने की वजह से सेटर सार्वजनिक है.

ServiceLocator.kt

    @Volatile
    var tasksRepository: TasksRepository? = null
        @VisibleForTesting set

चाहे आप जांच को अकेले या जांच के किसी ग्रुप में चलाएं, आपकी जांच बिल्कुल एक जैसी होनी चाहिए. इसका मतलब यह है कि आपके जांच का कोई ऐसा व्यवहार नहीं होना चाहिए जो एक-दूसरे पर निर्भर हो (यानी जांचों के बीच ऑब्जेक्ट शेयर करने से बचना).

ServiceLocator एक सिंगलटन है, इसलिए इसमें टेस्ट के बीच गलती से शेयर होने की संभावना है. इससे बचने के लिए, एक ऐसा तरीका बनाएं जो जांचों के बीच ServiceLocator स्थिति को ठीक से रीसेट कर दे.

  1. lock वैल्यू वाला इंस्टेंस वैरिएबल जोड़ें, जिसमें Any वैल्यू होगी.

ServiceLocator.kt

private val lock = Any()
  1. जांच के लिए resetRepository नाम का ऐसा तरीका जोड़ें जो डेटाबेस को साफ़ कर दे और रिपॉज़िटरी और डेटाबेस, दोनों को शून्य पर सेट करे.

ServiceLocator.kt

    @VisibleForTesting
    fun resetRepository() {
        synchronized(lock) {
            runBlocking {
                TasksRemoteDataSource.deleteAllTasks()
            }
            // Clear all data to avoid test pollution.
            database?.apply {
                clearAllTables()
                close()
            }
            database = null
            tasksRepository = null
        }
    }

चरण 5. ServiceService का इस्तेमाल करें

इस चरण में, आप ServiceLocator का इस्तेमाल करते हैं.

  1. TaskDetailFragmentTest खोलें.
  2. lateinit TasksRepository वैरिएबल का एलान करें.
  3. हर जांच से पहले, FakeAndroidTestRepository को सेट अप करने के लिए, सेट अप और आंसू अलग करने का तरीका जोड़ें. साथ ही, हर जांच के बाद उसे साफ़ कर दें.

TaskDetailFragmentTest.kt

    private lateinit var repository: TasksRepository

    @Before
    fun initRepository() {
        repository = FakeAndroidTestRepository()
        ServiceLocator.tasksRepository = repository
    }

    @After
    fun cleanupDb() = runBlockingTest {
        ServiceLocator.resetRepository()
    }
  1. runBlockingTest में activeTaskDetails_DisplayedInUi() का फ़ंक्शन मुख्य हिस्सा रैप करें.
  2. फ़्रैगमेंट को लॉन्च करने से पहले, डेटा स्टोर करने की जगह में activeTask सेव करें.
repository.saveTask(activeTask)

नीचे दी गई जांच, इस कोड की तरह है.

TaskDetailFragmentTest.kt

    @Test
    fun activeTaskDetails_DisplayedInUi()  = runBlockingTest{
        // GIVEN - Add active (incomplete) task to the DB
        val activeTask = Task("Active Task", "AndroidX Rocks", false)
        repository.saveTask(activeTask)

        // WHEN - Details fragment launched to display task
        val bundle = TaskDetailFragmentArgs(activeTask.id).toBundle()
        launchFragmentInContainer<TaskDetailFragment>(bundle, R.style.AppTheme)

    }
  1. @ExperimentalCoroutinesApi के साथ पूरी क्लास एनोटेट करें.

पूरा हो जाने पर, कोड ऐसा दिखेगा.

TaskDetailFragmentTest.kt

@MediumTest
@ExperimentalCoroutinesApi
@RunWith(AndroidJUnit4::class)
class TaskDetailFragmentTest {

    private lateinit var repository: TasksRepository

    @Before
    fun initRepository() {
        repository = FakeAndroidTestRepository()
        ServiceLocator.tasksRepository = repository
    }

    @After
    fun cleanupDb() = runBlockingTest {
        ServiceLocator.resetRepository()
    }


    @Test
    fun activeTaskDetails_DisplayedInUi()  = runBlockingTest{
        // GIVEN - Add active (incomplete) task to the DB
        val activeTask = Task("Active Task", "AndroidX Rocks", false)
        repository.saveTask(activeTask)

        // WHEN - Details fragment launched to display task
        val bundle = TaskDetailFragmentArgs(activeTask.id).toBundle()
        launchFragmentInContainer<TaskDetailFragment>(bundle, R.style.AppTheme)

    }

}
  1. activeTaskDetails_DisplayedInUi() की जांच करें.

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


इस चरण में, आप अपनी पहली इंटिग्रेशन जांच पूरी करने के लिए, Espresso यूज़र इंटरफ़ेस (यूआई) टेस्टिंग लाइब्रेरी का इस्तेमाल करेंगे. आपने अपना कोड व्यवस्थित कर लिया है, ताकि आप अपने यूज़र इंटरफ़ेस (यूआई) के लिए दावों के साथ जांच जोड़ सकें. ऐसा करने के लिए, आपEspresso टेस्टिंग लाइब्रेरी का इस्तेमाल करेंगे.

Espresso की मदद से आप ये काम कर सकते हैं:

  • व्यू पर कार्रवाई कर सकते हैं, जैसे कि बटन पर क्लिक करना, बार को स्लाइड करना या स्क्रीन पर नीचे की ओर स्क्रोल करना.
  • दावा करें कि कुछ व्यू स्क्रीन पर हैं या किसी खास स्थिति में हैं (जैसे, खास टेक्स्ट शामिल है या चेकबॉक्स चुना गया है वगैरह).

पहला चरण. नोट ग्रेडल डिपेंडेंसी

आपके पास #39; मुख्य एस्प्रेसो डिपेंडेंसी पहले से ही होगी, क्योंकि यह Android प्रोजेक्ट में डिफ़ॉल्ट रूप से शामिल होती है.

app/build.gradle

dependencies {

  // ALREADY in your code
    androidTestImplementation "androidx.test.espresso:espresso-core:$espressoVersion"
   
 // Other dependencies
}

androidx.test.espresso:espresso-core—नया Android प्रोजेक्ट बनाते समय, Espresso की यह मुख्य सुविधा डिफ़ॉल्ट रूप से शामिल होती है. इसमें ज़्यादातर व्यू और उनकी कार्रवाइयों के लिए बेसिक टेस्टिंग कोड होता है.

दूसरा चरण. ऐनिमेशन बंद करना

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

एस्प्रेसो यूज़र इंटरफ़ेस (यूआई) की जांच के लिए, ऐनिमेशन बंद करने का सबसे सही तरीका यह है# (आपका टेस्ट भी तेज़ी से चलेगा!):

  1. अपने टेस्टिंग डिवाइस पर, सेटिंग > डेवलपर के लिए सेटिंग और टूल पर जाएं.
  2. ये तीन सेटिंग बंद करें: विंडो ऐनिमेशन स्केल, ट्रांज़िशन ऐनिमेशन स्केल, और एनिमेटर अवधि का स्केल.

तीसरा चरण. एस्प्रेसो टेस्ट देखें

एस्प्रेसो टेस्ट लिखने से पहले, कुछ एस्प्रेसो कोड पर नज़र डालें.

onView(withId(R.id.task_detail_complete_checkbox)).perform(click()).check(matches(isChecked()))

यह स्टेटमेंट आईडी task_detail_complete_checkbox के साथ चेकबॉक्स व्यू को खोजता है, उस पर क्लिक करता है, फिर यह पक्का करता है कि उसे चुना गया है.

ज़्यादातर एस्प्रेसो स्टेटमेंट के चार हिस्से होते हैं:

1. स्टैटिक एस्प्रेसो तरीका

onView

onView एक स्टैटिक एस्प्रेसो मेथड का उदाहरण है. यह एस्प्रेसो स्टेटमेंट शुरू करता है. onView सबसे ज़्यादा इस्तेमाल होने वाले विकल्पों में से एक है, लेकिन इसके दूसरे विकल्प हैं, जैसे कि onData.

2. व्यू मैचर

withId(R.id.task_detail_title_text)

withId, ViewMatcher का एक उदाहरण है, जिसे उसके आईडी के हिसाब से एक व्यू मिलता है. कुछ दूसरे व्यू मैचर हैं, जिन्हें आप दस्तावेज़ में देख सकते हैं.

3. ViewAction

perform(click())

perform वाला तरीका, जो ViewAction का इस्तेमाल करता है. ViewAction एक ऐसा काम है जो व्यू के लिए किया जा सकता है, उदाहरण के लिए, यहां व्यू पर क्लिक करके #39.

4. ViewAssertion

check(matches(isChecked()))

check जो ViewAssertion लेता है. ViewAssertion, व्यू के बारे में कुछ खोजता है या उस पर दावा करता है. आप ViewAssertion का सबसे ज़्यादा इस्तेमाल करने के लिए matches दावा करते हैं. दावा पूरा करने के लिए, दूसरे ViewMatcher का इस्तेमाल करें, ऐसे में isChecked.

ध्यान दें कि आप एस्प्रेसो स्टेटमेंट में हमेशा perform और check, दोनों को कॉल नहीं करते. आपके पास ऐसे स्टेटमेंट हो सकते हैं जो check का इस्तेमाल करके दावा करते हैं या perform का इस्तेमाल करके, ViewAction करते हैं.

  1. TaskDetailFragmentTest.kt खोलें.
  2. activeTaskDetails_DisplayedInUi टेस्ट अपडेट करें.

TaskDetailFragmentTest.kt

    @Test
    fun activeTaskDetails_DisplayedInUi() = runBlockingTest{
        // GIVEN - Add active (incomplete) task to the DB
        val activeTask = Task("Active Task", "AndroidX Rocks", false)
        repository.saveTask(activeTask)

        // WHEN - Details fragment launched to display task
        val bundle = TaskDetailFragmentArgs(activeTask.id).toBundle()
        launchFragmentInContainer<TaskDetailFragment>(bundle, R.style.AppTheme)

        // THEN - Task details are displayed on the screen
        // make sure that the title/description are both shown and correct
        onView(withId(R.id.task_detail_title_text)).check(matches(isDisplayed()))
        onView(withId(R.id.task_detail_title_text)).check(matches(withText("Active Task")))
        onView(withId(R.id.task_detail_description_text)).check(matches(isDisplayed()))
        onView(withId(R.id.task_detail_description_text)).check(matches(withText("AndroidX Rocks")))
        // and make sure the "active" checkbox is shown unchecked
        onView(withId(R.id.task_detail_complete_checkbox)).check(matches(isDisplayed()))
        onView(withId(R.id.task_detail_complete_checkbox)).check(matches(not(isChecked())))
    }

अगर ज़रूरी हो, तो यहां इंपोर्ट स्टेटमेंट दिए गए हैं:

import androidx.test.espresso.Espresso.onView
import androidx.test.espresso.assertion.ViewAssertions.matches
import androidx.test.espresso.matcher.ViewMatchers.isChecked
import androidx.test.espresso.matcher.ViewMatchers.isDisplayed
import androidx.test.espresso.matcher.ViewMatchers.withId
import androidx.test.espresso.matcher.ViewMatchers.withText
import org.hamcrest.core.IsNot.not
  1. // THEN टिप्पणी के बाद की हर चीज़ Espresso का इस्तेमाल करती है. टेस्ट स्ट्रक्चर और withId के इस्तेमाल की जांच करें. साथ ही, यह पुष्टि करें कि जानकारी वाला पेज कैसा दिखना चाहिए.
  2. टेस्ट चलाएं और पुष्टि करें कि यह पास हो गया है.

चरण 4. वैकल्पिक रूप से, अपना खुद का Espresso टेस्ट लिखें

अब खुद एक टेस्ट लिखें.

  1. completedTaskDetails_DisplayedInUi नाम की नई जांच करें और इस कंकाल कोड को कॉपी करें.

TaskDetailFragmentTest.kt

    @Test
    fun completedTaskDetails_DisplayedInUi() = runBlockingTest{
        // GIVEN - Add completed task to the DB
       
        // WHEN - Details fragment launched to display task
        
        // THEN - Task details are displayed on the screen
        // make sure that the title/description are both shown and correct
}
  1. पिछले टेस्ट को देखते हुए, इस टेस्ट को पूरा करें.
  2. चलाएं और टेस्ट पास की पुष्टि करें.

खत्म किया गया completedTaskDetails_DisplayedInUi ऐसा दिखना चाहिए.

TaskDetailFragmentTest.kt

    @Test
    fun completedTaskDetails_DisplayedInUi() = runBlockingTest{
        // GIVEN - Add completed task to the DB
        val completedTask = Task("Completed Task", "AndroidX Rocks", true)
        repository.saveTask(completedTask)

        // WHEN - Details fragment launched to display task
        val bundle = TaskDetailFragmentArgs(completedTask.id).toBundle()
        launchFragmentInContainer<TaskDetailFragment>(bundle, R.style.AppTheme)

        // THEN - Task details are displayed on the screen
        // make sure that the title/description are both shown and correct
        onView(withId(R.id.task_detail_title_text)).check(matches(isDisplayed()))
        onView(withId(R.id.task_detail_title_text)).check(matches(withText("Completed Task")))
        onView(withId(R.id.task_detail_description_text)).check(matches(isDisplayed()))
        onView(withId(R.id.task_detail_description_text)).check(matches(withText("AndroidX Rocks")))
        // and make sure the "active" checkbox is shown unchecked
        onView(withId(R.id.task_detail_complete_checkbox)).check(matches(isDisplayed()))
        onView(withId(R.id.task_detail_complete_checkbox)).check(matches(isChecked()))
    }

इस आखिरी कदम में आप #नेविगेशन कॉम्पोनेंट को टेस्ट करने का तरीका जानेंगे. इसके लिए, आप टेस्ट डबल के तौर पर इस्तेमाल किए जाने वाले मॉक (नकली कैंपेन) और टेस्टिंग लाइब्रेरी मॉकीटो का इस्तेमाल कर सकते हैं.

इस कोडलैब में आपने नकली टेस्ट का इस्तेमाल करके, डुप्लीकेट (नकली) कहा है. टेस्ट कई तरह के टेस्ट डबल में से एक है. नेविगेशन कॉम्पोनेंट की जांच करने के लिए, आपको कौनसा टेस्ट डबल इस्तेमाल करना चाहिए?

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

TasksFragment में मौजूद कोड, TasksFragment को दबाने पर टास्क की जानकारी वाली स्क्रीन पर चला जाता है.

TasksFragment.kt

private fun openTaskDetails(taskId: String) {
    val action = TasksFragmentDirections.actionTasksFragmentToTaskDetailFragment(taskId)
    findNavController().navigate(action)
}


navigate मोड में कॉल करने की वजह से नेविगेशन होता है. अगर आपको दावा वाला स्टेटमेंट लिखना है, तो यह आसान तरीका है कि आप टेस्ट कर सकते हैं कि क्या आपने #39;TaskDetailFragment में नेविगेट किया है. नेविगेट करना एक जटिल कार्रवाई है, जो TaskDetailFragment को शुरू करने के अलावा, आउटपुट या स्थिति में साफ़ तौर पर बदलाव नहीं करती.

आप यह दावा कर सकते हैं कि navigate तरीके को सही कार्रवाई पैरामीटर से कॉल किया गया था. मॉक टेस्ट ठीक यही काम करता है—इससे यह पता चलता है कि किसी खास तरीके को कॉल किया गया या नहीं.

Mockito टेस्ट डबल बनाने के लिए एक फ़्रेमवर्क है. एपीआई शब्द और नाम में मॉक शब्द इस्तेमाल किए जाते हैं. हालांकि, सिर्फ़ मॉक बनाने के लिए इसका इस्तेमाल नहीं किया जाता. इससे स्टब और जासूस भी बन सकते हैं.

आप मॉकटो का उपयोग करके एक नकली NavigationController बना सकते हैं, जो यह दावा कर सकता है कि नेविगेट करने का तरीका सही ढंग से कॉल किया गया था.

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

  1. Gradle डिपेंडेंसी जोड़ें.

app/build.gradle

    // Dependencies for Android instrumented unit tests
    androidTestImplementation "org.mockito:mockito-core:$mockitoVersion"

    androidTestImplementation "com.linkedin.dexmaker:dexmaker-mockito:$dexMakerVersion" 

    androidTestImplementation "androidx.test.espresso:espresso-contrib:$espressoVersion"



  • org.mockito:mockito-core—यह मॉकीटो निर्भरता है.
  • dexmaker-mockito—किसी Android प्रोजेक्ट में मॉकिटो का इस्तेमाल करने के लिए, यह लाइब्रेरी ज़रूरी है. मॉकिटो को रनटाइम के दौरान क्लास जनरेट करनी होंगी. Android पर, यह dex बाइट कोड का इस्तेमाल करके किया जाता है. इसलिए, यह लाइब्रेरी Android पर रनटाइम के दौरान मॉकिटो को ऑब्जेक्ट जनरेट करने देती है.
  • androidx.test.espresso:espresso-contrib—इस लाइब्रेरी में बाहरी योगदान (इस नाम से) शामिल हैं, जिनमें DatePicker और RecyclerView जैसे बेहतर व्यू के लिए जांच कोड शामिल है. इसमें सुलभता की जांच और CountingIdlingResource नाम की क्लास भी शामिल हैं, जो बाद में शामिल की जाती हैं.

दूसरा चरण. TasksFragmentTest बनाएं

  1. TasksFragment खोलें.
  2. TasksFragment कक्षा के नाम पर दायां क्लिक करें. इसके बाद, जनरेट करें और फिर जांच करें चुनें. androidTest सोर्स सेट में टेस्ट बनाएं.
  3. इस कोड को TasksFragmentTest में कॉपी करें.

TasksFragmentTest.kt

@RunWith(AndroidJUnit4::class)
@MediumTest
@ExperimentalCoroutinesApi
class TasksFragmentTest {

    private lateinit var repository: TasksRepository

    @Before
    fun initRepository() {
        repository = FakeAndroidTestRepository()
        ServiceLocator.tasksRepository = repository
    }

    @After
    fun cleanupDb() = runBlockingTest {
        ServiceLocator.resetRepository()
    }

}

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

  1. टेस्ट clickTask_navigateToDetailFragmentOne को जोड़ें.

TasksFragmentTest.kt

    @Test
    fun clickTask_navigateToDetailFragmentOne() = runBlockingTest {
        repository.saveTask(Task("TITLE1", "DESCRIPTION1", false, "id1"))
        repository.saveTask(Task("TITLE2", "DESCRIPTION2", true, "id2"))

        // GIVEN - On the home screen
        val scenario = launchFragmentInContainer<TasksFragment>(Bundle(), R.style.AppTheme)
        
    }
  1. मॉक बनाने के लिए Mockito's mock फ़ंक्शन का इस्तेमाल करें.

TasksFragmentTest.kt

 val navController = mock(NavController::class.java)

मॉकिटो में मॉक करने के लिए, उस क्लास को पास करें जिसे आप मॉक करना चाहते हैं.

इसके बाद, आपको फ़्रैगमेंट के साथ NavController को जोड़ना होगा. onFragment फ़्रैगमेंट पर आप कॉल करने के तरीके खुद कर सकते हैं.

  1. नए फ़्रैगमेंट को NavController का नया मॉक बनाएं.
scenario.onFragment {
    Navigation.setViewNavController(it.view!!, navController)
}
  1. RecyclerView में उस आइटम पर क्लिक करने के लिए कोड जोड़ें जिसमें टेक्स्ट &कोटेशन;TITLE1&कोटेशन है.
// WHEN - Click on the first list item
        onView(withId(R.id.tasks_list))
            .perform(RecyclerViewActions.actionOnItem<RecyclerView.ViewHolder>(
                hasDescendant(withText("TITLE1")), click()))

RecyclerViewActions, espresso-contrib लाइब्रेरी का हिस्सा है. इससे आप RecyclerView पर Espresso कार्रवाइयां कर सकते हैं.

  1. पुष्टि करें कि सही आर्ग्युमेंट के साथ navigate को कॉल किया गया है.
// THEN - Verify that we navigate to the first detail screen
verify(navController).navigate(
    TasksFragmentDirections.actionTasksFragmentToTaskDetailFragment( "id1")

मॉकिटो' की verify विधि इसे एक नकली बनाती है—आप एक खास तरीके (navigate) नाम के मॉक (navController) की पुष्टि करने में कामयाब होते हैं, जिसका एक पैरामीटर (actionTasksFragmentToTaskDetailFragment आईडी के साथ &kot;id1&कोटेशन है) है.

पूरा टेस्ट कुछ ऐसा दिखता है:

@Test
fun clickTask_navigateToDetailFragmentOne() = runBlockingTest {
    repository.saveTask(Task("TITLE1", "DESCRIPTION1", false, "id1"))
    repository.saveTask(Task("TITLE2", "DESCRIPTION2", true, "id2"))

    // GIVEN - On the home screen
    val scenario = launchFragmentInContainer<TasksFragment>(Bundle(), R.style.AppTheme)
    
                val navController = mock(NavController::class.java)
    scenario.onFragment {
        Navigation.setViewNavController(it.view!!, navController)
    }

    // WHEN - Click on the first list item
    onView(withId(R.id.tasks_list))
        .perform(RecyclerViewActions.actionOnItem<RecyclerView.ViewHolder>(
            hasDescendant(withText("TITLE1")), click()))


    // THEN - Verify that we navigate to the first detail screen
    verify(navController).navigate(
        TasksFragmentDirections.actionTasksFragmentToTaskDetailFragment( "id1")
    )
}
  1. अपना टेस्ट चलाएं!

खास जानकारी में, नेविगेशन की जांच करने के लिए आप यह कर सकते हैं:

  1. NavController मॉक बनाने के लिए मॉकिटो का इस्तेमाल करें.
  2. फ़्रैगमेंट में मॉक NavController को अटैच करें.
  3. पुष्टि करें कि नेविगेशन को सही कार्रवाई और पैरामीटर के साथ कॉल किया गया था.

तीसरा चरण. ज़रूरी नहीं है, ClickAddTaskButton_navigateToAddFragment को लिखें

नेविगेशन टेस्ट खुद लिखें या नहीं, यह देखने के लिए यह टास्क आज़माएं.

  1. टेस्ट clickAddTaskButton_navigateToAddEditFragment लिखें, जो यह जांच सके कि अगर आप + एफ़एबी पर क्लिक करते हैं, तो आप AddEditTaskFragment पर जाएं.

इसका जवाब नीचे दिया गया है.

TasksFragmentTest.kt

    @Test
    fun clickAddTaskButton_navigateToAddEditFragment() {
        // GIVEN - On the home screen
        val scenario = launchFragmentInContainer<TasksFragment>(Bundle(), R.style.AppTheme)
        val navController = mock(NavController::class.java)
        scenario.onFragment {
            Navigation.setViewNavController(it.view!!, navController)
        }

        // WHEN - Click on the "+" button
        onView(withId(R.id.add_task_fab)).perform(click())

        // THEN - Verify that we navigate to the add screen
        verify(navController).navigate(
            TasksFragmentDirections.actionTasksFragmentToAddEditTaskFragment(
                null, getApplicationContext<Context>().getString(R.string.add_task)
            )
        )
    }

शुरू किए गए कोड और आखिरी कोड के बीच का अंतर देखने के लिए यहां क्लिक करें.

खत्म हो चुके कोडलैब के लिए कोड डाउनलोड करने के लिए, नीचे दिए गए git कमांड का इस्तेमाल करें:

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


इसके अलावा, आप रिपॉज़िटरी को एक ZIP फ़ाइल के रूप में डाउनलोड कर सकते हैं, इसे अनज़िप कर सकते हैं, और Android Studio में खोल सकते हैं.

पिन डाउनलोड करें

इस कोडलैब में मैन्युअल डिपेंडेंसी इंजेक्शन और सेवा लोकेटर सेट अप करने का तरीका बताया गया है. साथ ही, Android Kotlin ऐप्लिकेशन में नकली और मॉक (नकली सामान) इस्तेमाल करने का तरीका भी बताया गया है. खास तौर पर:

  • आप किस जांच को टेस्ट करना चाहते हैं और अपनी जांच की रणनीति से यह तय करते हैं कि आप अपने ऐप्लिकेशन के लिए किस तरह की जांच लागू करेंगे. यूनिट जांच की सुविधा तेज़ और तेज़ है. इंटिग्रेशन टेस्ट आपके प्रोग्राम के हिस्सों के बीच इंटरैक्शन की पुष्टि करते हैं. पूरी तरह सुरक्षित (E2EE) टेस्ट में, सुविधाओं की पुष्टि की जाती है, सबसे अच्छी फ़िडेलिटी होती है, इन्हें अक्सर लागू किया जाता है, और इन्हें चलने में ज़्यादा समय लग सकता है.
  • आपके ऐप्लिकेशन का आर्किटेक्चर, इस बात पर असर डालता है कि इसे टेस्ट करना कितना मुश्किल है.
  • TDD या टेस्ट ड्रिवन डेवलपमेंट एक ऐसी रणनीति है जिसमें आप पहले टेस्ट लिखते हैं. फिर, टेस्ट पास करने के लिए यह सुविधा बनाते हैं.
  • अपने ऐप्लिकेशन के कुछ हिस्सों को टेस्टिंग के लिए अलग करने के लिए, आप टेस्ट डबल का इस्तेमाल कर सकते हैं. डबल डबल, क्लास का एक वर्शन है, जिसे खास तौर पर जांच के लिए बनाया जाता है. उदाहरण के लिए, आप किसी डेटाबेस या इंटरनेट से डेटा फ़र्ज़ी तरीके से पा सकते हैं.
  • किसी असली क्लास को टेस्टिंग क्लास से बदलने के लिए, डिपेंडेंसी इंजेक्शन का इस्तेमाल करें. उदाहरण के लिए, रिपॉज़िटरी या नेटवर्किंग लेयर.
  • यूज़र इंटरफ़ेस (यूआई) के कॉम्पोनेंट लॉन्च करने के लिए, जांच की गई जांच (androidTest) का इस्तेमाल करें.
  • जब आप कंस्ट्रक्टर डिपेंडेंसी का इस्तेमाल नहीं कर सकते, उदाहरण के लिए फ़्रैगमेंट को लॉन्च करना, तो आप अक्सर सर्विस लोकेटर का इस्तेमाल कर सकते हैं. सर्विस लोकेटर पैटर्न, डिपेंडेंसी इंजेक्शन के लिए एक विकल्प है. इसमें &tot;सेवा लोकेटर&कोटेशन नाम की सिंगलटन क्लास बनाना है, जिसका मकसद सामान्य और टेस्ट कोड, दोनों के लिए डिपेंडेंसी देना है.

Udcity कोर्स:

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

वीडियो:

अन्य:

इस कोर्स में दिए गए दूसरे कोडलैब के लिंक के लिए, Kotlin कोडलैब के लैंडिंग पेज में ऐडवांस्ड Android देखें.