यह कोडलैब, Kotlin में बेहतर Android का हिस्सा है. अगर आप कोडलैब के क्रम में काम करते हैं, लेकिन यह ज़रूरी नहीं है, तो आपको इस कोर्स का पूरा फ़ायदा मिलेगा. सभी कोर्स कोडलैब Kotlin कोडलैब के लैंडिंग पेज पर बेहतर Android पेज पर दिए गए हैं.
परिचय
यह दूसरा टेस्ट कोड टेस्ट डबल के बारे में है: इन्हें Android में कब इस्तेमाल करना है और डिपेंडेंसी इंजेक्शन, सर्विस लोकेटर पैटर्न, और लाइब्रेरी का इस्तेमाल करके इन्हें कैसे लागू करना है. ऐसा करने पर, आप #39 लिखना सीखेंगी:
- रिपॉज़िटरी यूनिट की जांच
- फ़्रैगमेंट और व्यू मॉडल इंटिग्रेशन टेस्ट
- फ़्रैगमेंट नेविगेशन की जांच
आपको क्या पता होना चाहिए
आपको इनके बारे में पता होना चाहिए:
- Kotlin प्रोग्रामिंग लैंग्वेज
- पहले कोडलैब में शामिल सिद्धांतों का टेस्ट करना: Android पर यूनिट टेस्ट लिखना और चलाना, JUnit, Hamcrest, AndroidX टेस्ट, Robolectric का इस्तेमाल करना. साथ ही, LiveData टेस्ट करना
- ये मुख्य Android Jetpack लाइब्रेरी:
ViewModel
,LiveData
, और नेविगेशन कॉम्पोनेंट - ऐप्लिकेशन आर्किटेक्चर की गाइड और Android की बुनियादी जानकारी वाले कोडलैब से मिले पैटर्न के हिसाब से, ऐप्लिकेशन का आर्किटेक्चर
- Android पर कोरूटीन की बुनियादी बातें
आप इन चीज़ों के बारे में जानेंगे
- जांच की रणनीति बनाने का तरीका
- टेस्ट डबल, नकली नकली और इस्तेमाल करने के तरीके
- यूनिट और इंटिग्रेशन टेस्ट के लिए, 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 का इस्तेमाल करता है. अगर आप इन उदाहरणों में से किसी एक के बारे में जानते हैं, तो इस ऐप्लिकेशन का आर्किटेक्चर भी ऐसा ही है:
- व्यू कोडलैब (कोड बनाना सीखना) वाला कमरा
- Android Kotlin से जुड़ी बुनियादी ट्रेनिंग के लिए कोडलैब
- Android ट्रेनिंग के लिए बेहतर कोडलैब
- Android सनफ़्लावर का नमूना
- Kotlin Udacity ट्रेनिंग कोर्स की मदद से, Android ऐप्लिकेशन डेवलप करना
किसी खास लेयर के लॉजिक की पूरी जानकारी देने के बजाय, यह ज़रूरी है कि आप ऐप्लिकेशन की सामान्य आर्किटेक्चर को समझें.
यहां उन पैकेज की खास जानकारी दी गई है, जिन्हें आप #39:
पैकेज: | |
| टास्क की स्क्रीन जोड़ना या उसमें बदलाव करना: किसी टास्क को जोड़ने या उसमें बदलाव करने के लिए, यूज़र इंटरफ़ेस (यूआई) का लेयर कोड. |
| डेटा लेयर: यह टास्क के डेटा लेयर से जुड़ा होता है. इसमें डेटाबेस, नेटवर्क, और रिपॉज़िटरी कोड शामिल होता है. |
| आंकड़ों की स्क्रीन: आंकड़ों की स्क्रीन के लिए यूज़र इंटरफ़ेस (यूआई) कोड. |
| टास्क की जानकारी वाली स्क्रीन: किसी एक टास्क के लिए यूज़र इंटरफ़ेस (यूआई) का लेयर कोड. |
| टास्क टास्क: सभी टास्क की सूची के लिए यूज़र इंटरफ़ेस (यूआई) का कोड. |
| उपयोगिता क्लास: ऐप्लिकेशन के अलग-अलग हिस्सों में इस्तेमाल की जाने वाली 'शेयर की गई क्लास', जैसे कि एक से ज़्यादा स्क्रीन पर इस्तेमाल किए जाने वाले स्वाइप रीफ़्रेश लेआउट के लिए. |
डेटा स्तर (.data)
इस ऐप्लिकेशन में, रिमोट पैकेज में एक सिम्युलेटेड नेटवर्किंग लेयर और लोकल पैकेज में एक डेटाबेस लेयर शामिल होती है. आसानी से कहें, तो इस प्रोजेक्ट में नेटवर्किंग लेयर को असल नेटवर्क अनुरोध करने के बजाय, देरी से सिर्फ़ HashMap
के साथ सिम्युलेट किया जाता है.
DefaultTasksRepository
नेटवर्किंग लेयर और डेटाबेस लेयर के बीच निर्देशांक या मीडिएट करता है. साथ ही, यह यूआई लेयर में डेटा दिखाता है.
यूज़र इंटरफ़ेस (यूआई) लेयर ( .addedittask, .आंकड़े, .taskdetail, .tasks)
हर यूज़र इंटरफ़ेस (यूआई) लेयर पैकेज में एक फ़्रैगमेंट और एक व्यू मॉडल होता है. इसके अलावा, यूज़र इंटरफ़ेस (यूआई) के लिए ज़रूरी अन्य क्लास भी होती हैं (जैसे कि टास्क सूची के लिए कोई अडैप्टर). TaskActivity
वह गतिविधि है जिसमें सभी फ़्रैगमेंट शामिल होते हैं.
नेविगेशन
ऐप्लिकेशन के लिए नेविगेशन को नेविगेशन कॉम्पोनेंट से कंट्रोल किया जाता है. यह nav_graph.xml
फ़ाइल में बताया गया है. नेविगेशन Event
क्लास का इस्तेमाल करके व्यू मॉडल में ट्रिगर होता है; व्यू मॉडल यह भी तय करते हैं कि कौनसे पास करने हैं. फ़्रैगमेंट Event
की निगरानी करते हैं और स्क्रीन के बीच असल नेविगेशन करते हैं.
इस कोडलैब में आप रिपॉज़िटरी, व्यू मॉडल, और फ़्रैगमेंट की जांच करने का तरीका जानेंगे. इसके लिए, टेस्ट डबल और डिपेंडेंसी इंजेक्ट का इस्तेमाल किया जाएगा. ये क्या हैं यह जानने से पहले, यह समझना ज़रूरी है कि ये टेस्ट क्या और कैसे लिखेंगे.
इस सेक्शन में आम तौर पर जांच करने के कुछ सबसे सही तरीके बताए गए हैं, क्योंकि ये Android पर लागू होते हैं.
टेस्टिंग पिरामिड
जब आप किसी जांच की रणनीति के बारे में सोचते हैं, तो उसके तीन मिलते-जुलते टेस्टिंग पहलू होते हैं:
- दायरा—जांच कोड कितना है? जांच एक ही तरीके से, पूरे ऐप्लिकेशन में या कहीं और की जा सकती है.
- गति—टेस्ट कितनी तेज़ चलता है? जांच करने की रफ़्तार मिलीसेकंड में अलग-अलग हो सकती है. इसमें कई मिनट लग सकते हैं.
- फ़िडेलिटी—यह टेस्ट कैसे दिखता है? उदाहरण के लिए, अगर आपकी जांच के दौरान मिलने वाले कोड का एक हिस्सा नेटवर्क अनुरोध करने की ज़रूरत है, तो क्या जांच कोड उस नेटवर्क का अनुरोध करता है या वह नतीजा नकली है? अगर टेस्ट, नेटवर्क के साथ असल में बात करता है, तो इसका मतलब है कि इसमें फ़िडेलिटी की संख्या ज़्यादा है. समस्या यह है कि टेस्ट को चलने में ज़्यादा समय लग सकता है. अगर नेटवर्क काम नहीं कर रहा है या इसे इस्तेमाल करने में महंगा हो सकता है, तो गड़बड़ियां हो सकती हैं.
इन पहलुओं के बीच स्वाभाविक ट्रेड-ऑफ़ हैं. उदाहरण के लिए, स्पीड और फ़िडेलिटी में सुधार किया जा सकता है. आम तौर पर, टेस्ट जितना तेज़ होगा, फ़िडेलिटी में उतना ही कम, और इसके उलट. अपने-आप होने वाली जांचों को अलग-अलग करने का एक आम तरीका यह है:
- यूनिट टेस्ट—ये टेस्ट पर बहुत ज़्यादा फ़ोकस करते हैं, जो किसी एक क्लास में होते हैं. आम तौर पर, यह इस क्लास में एक ही तरीका होता है. अगर यूनिट की जांच नहीं हो पाती है, तो आपको पता चल सकता है कि आपके कोड में कहां की समस्या है. असल दुनिया में इनकी फ़िडेलिटी कम है. इसलिए, आपके ऐप्लिकेशन में एक ही तरीके या क्लास को लागू करने से कहीं ज़्यादा शामिल हैं. हर बार कोड बदलने पर, इन्हें चलाने के लिए ये काफ़ी तेज़ होते हैं. आम तौर पर, ये स्थानीय तौर पर टेस्ट किए जाएंगे (
test
सोर्स सेट में). उदाहरण: व्यू मॉडल और डेटा स्टोर करने की जगहों में एक ही तरीके की जांच करना. - इंटिग्रेशन टेस्ट—इन क्लास का इंटरैक्शन यह पक्का करने के लिए किया जाता है कि वे एक-दूसरे के साथ सही तरीके से काम कर रही हैं या नहीं. इंटिग्रेशन टेस्ट को व्यवस्थित करने का एक तरीका यह है कि आप उन्हें किसी एक सुविधा के लिए टेस्ट करें, जैसे कि कोई टास्क सेव करने की सुविधा. ये यूनिट जांच की तुलना में कोड के बड़े दायरे की जांच करते हैं. इसके बावजूद, इन्हें फ़िडेलिटी के मुकाबले तेज़ी से चलाने के लिए ऑप्टिमाइज़ किया जाता है. स्थिति के हिसाब से, इन्हें स्थानीय तौर पर या इंस्ट्रूमेंटेशन टेस्ट के तौर पर चलाया जा सकता है. उदाहरण: किसी एक फ़्रैगमेंट और व्यू मॉडल पेयर की सभी सुविधाओं की जांच करना.
- खत्म होने की जांच (E2e)—साथ में काम करने वाली सुविधाओं के इस्तेमाल की जांच करें. वे ऐप्लिकेशन के बड़े हिस्सों की जांच करते हैं, असली इस्तेमाल को सिम्युलेट करते हैं, और इसलिए आम तौर पर ये धीरे काम करते हैं. उनमें सबसे ज़्यादा फ़िडेलिटी है और आपको बताती है कि आपका ऐप्लिकेशन पूरे काम करता है. अगर ये जांच बड़े पैमाने पर की जाती है, तो इन्हें
androidTest
सोर्स सेट में) लागू किया जाएगा.
उदाहरण: पूरे ऐप्लिकेशन को शुरू करने के साथ-साथ कुछ सुविधाओं को टेस्ट करना.
इन जांचों के सुझाए गए अनुपात को अक्सर पिरामिड के तौर पर दिखाया जाता है. इसमें ज़्यादातर जांच, यूनिट की जांच हैं.
आर्किटेक्चर ऐंड टेस्टिंग
टेस्टिंग पिरामिड के सभी अलग-अलग लेवल पर अपने ऐप्लिकेशन की जांच करने की सुविधा, आपके ऐप्लिकेशन और आर्किटेक्चर से मेल खाती है. उदाहरण के लिए, बहुत ज़्यादा खराब व्यवस्थित किए गए ऐप्लिकेशन अपने सभी लॉजिक को एक तरीके में डाल सकते हैं. आप इसके लिए एंड-टू-एंड टेस्ट लिख सकते हैं, क्योंकि ये टेस्ट ऐप्लिकेशन के बड़े हिस्से की जांच करते हैं, लेकिन लिखने की यूनिट या इंटिग्रेशन टेस्ट का क्या होगा? एक ही जगह पर सभी कोड के साथ, सिर्फ़ एक यूनिट या सुविधा से जुड़े कोड की जांच करना मुश्किल है.
एक बेहतर तरीका है कि आप ऐप्लिकेशन लॉजिक को कई तरीकों और क्लास में बांटें. इससे हर हिस्से को अलग से टेस्ट किया जाएगा. आर्किटेक्चर, अपने कोड को बांटने और व्यवस्थित करने का एक तरीका है. इससे यूनिट और इंटिग्रेशन की जांच करना आसान हो जाता है. आप जो #काम की सूची वाला ऐप्लिकेशन इस्तेमाल करेंगे वह एक खास आर्किटेक्चर को फ़ॉलो करेगा:
इस लेसन में, आप ऊपर बताए गए आर्किटेक्चर के हिस्से के टेस्ट के लिए, सही तरीके से अलग-अलग तरीका देख सकते हैं:
- सबसे पहले आपको रिपॉज़िटरी की यूनिट की जांच करनी होगी.
- इसके बाद, आप व्यू मॉडल में टेस्ट डबल का इस्तेमाल करेंगे. यह यूनिट टेस्टिंग और व्यू मॉडल के लिए इंटिग्रेशन टेस्टिंग के लिए ज़रूरी है.
- इसके बाद, आप फ़्रैगमेंट और उनके व्यू मॉडल के लिए, इंटिग्रेशन टेस्ट लिखना सीखेंगे.
- आखिर में, आप इंटिग्रेशन टेस्ट लिखना सीखेंगे, जिसमें नेविगेशन कॉम्पोनेंट शामिल होगा.
आखिर में शुरू होने वाली टेस्टिंग की जानकारी अगले लेसन में शामिल की जाएगी.
जब आप किसी कक्षा के किसी हिस्से (तरीका या छोटे संग्रह) के लिए यूनिट टेस्ट लिखते हैं, तो आपका लक्ष्य सिर्फ़ उस क्लास में कोड की जांच करना होता है.
किसी खास क्लास या क्लास में सिर्फ़ कोड की जांच करना मुश्किल हो सकता है. आइए एक उदाहरण देखें. 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 टेस्ट का इस्तेमाल कर रहे हैं.
- कोड के कुछ हिस्से, जैसे कि नेटवर्किंग कोड, चलने में लंबा समय ले सकते हैं या कभी-कभी असफल भी हो सकते हैं. यह लंबे समय के लिए चल सकने वाले टेस्ट करता है.
- हो सकता है कि जांच में सफल न होने की वजह से, आपको यह पता न चले कि किस कोड में गड़बड़ी है. आपके टेस्ट, रिपॉज़िटरी कोड की जांच शुरू कर सकते हैं. उदाहरण के लिए, डेटाबेस कोड जैसे कुछ डिपेंडेंसी कोड में गड़बड़ी होने की वजह से, आपके यूनिट का कोटेशन अमान्य हो सकता है.
टेस्ट डबल
इसका समाधान यह है कि जब आप रिपॉज़िटरी का परीक्षण कर रहे होते हैं, तब आप वास्तविक नेटवर्किंग या डेटाबेस कोड का इस्तेमाल नहीं करते, लेकिन इसकी जगह टेस्ट डबल का इस्तेमाल करते हैं. डबल डबल, क्लास का एक वर्शन है, जिसे खास तौर पर जांच के लिए बनाया जाता है. यह टेस्ट में किसी क्लास के असली वर्शन की जगह लेगा. यह भी एक स्टंट डबल जैसा है. यह एक अभिनेता है जो स्टंट करने में माहिर है. यह खतरनाक कलाकार को असली अभिनेता की जगह लेता है.
यहां दो तरह के टेस्ट डबल दिए गए हैं:
नकली चीज़ें | एक ऐसा टेस्ट डबल जिसमें क्लास को लागू किया जाता है, लेकिन यह इस तरह से लागू किया जाता है कि टेस्ट के लिए अच्छा होता है, लेकिन प्रोडक्शन के लिए सही नहीं होता. |
मॉक | एक टेस्ट डबल, जो इसे कॉल करने के तरीकों को ट्रैक करता है. इसके बाद, यह टेस्ट पास करता है या नहीं करता है. इसलिए, यह इस बात पर निर्भर करता है कि तरीके को सही तरीके से कॉल किया गया है या नहीं. |
स्टब | एक टेस्ट डबल जिसमें कोई लॉजिक नहीं होता और सिर्फ़ वही रिटर्न करता है जिसे आप रिटर्न करने के लिए प्रोग्राम करते हैं. उदाहरण के लिए, |
डमी | ऐसा टेस्ट डबल जो पास हो जाता है, लेकिन इस्तेमाल नहीं किया जाता, जैसे कि अगर आपको इसे पैरामीटर के तौर पर देने की ज़रूरत हो. अगर आपके पास |
जासूसी | एक टेस्ट डबल जो कुछ और जानकारी का ट्रैक रखता है; उदाहरण के लिए, अगर आपने |
टेस्ट डबल के बारे में ज़्यादा जानने के लिए, टॉयलेट पर जांच करना: अपने टेस्ट डबल के बारे में जानें देखें.
Android के लिए नकली और मॉक सबसे ज़्यादा इस्तेमाल किए जाने वाले टेस्ट डबल हैं.
इस टास्क में, आप #39 टेस्ट को FakeDataSource
टेस्ट के तौर पर बनाने वाले हैं. यह असल डेटा सोर्स से अलग किया गया FakeDataSource
टेस्ट होगा.
पहला चरण: FakeDataSource क्लास बनाना
इस चरण में, आप FakeDataSouce
नाम की एक क्लास बनाने जा रहे हैं. यह LocalDataSource
और RemoteDataSource
की दोहरा टेस्ट होगी.
- टेस्ट सोर्स सेट में, नया -> पैकेज पर दायां क्लिक करें.
- अंदर एक स्रोत पैकेज के साथ एक डेटा पैकेज बनाएं.
- डेटा/स्रोत पैकेज में
FakeDataSource
नाम की एक नई क्लास बनाएं.
दूसरा चरण: TasksDataSource इंटरफ़ेस लागू करना
अपनी नई कक्षा FakeDataSource
को टेस्ट डबल के तौर पर इस्तेमाल करने के लिए, यह ज़रूरी है कि वह दूसरे डेटा सोर्स की जगह ले. वे डेटा स्रोत TasksLocalDataSource
और TasksRemoteDataSource
हैं.
- ध्यान दें कि ये दोनों ही
TasksDataSource
इंटरफ़ेस को कैसे लागू करते हैं.
class TasksLocalDataSource internal constructor(
private val tasksDao: TasksDao,
private val ioDispatcher: CoroutineDispatcher = Dispatchers.IO
) : TasksDataSource { ... }
object TasksRemoteDataSource : TasksDataSource { ... }
FakeDataSource
लागू करेंTasksDataSource
:
class FakeDataSource : TasksDataSource {
}
Android Studio शिकायत करेगा कि आपने TasksDataSource
के लिए ज़रूरी तरीके लागू नहीं किए हैं.
- 'फटाफट चुनें' मेन्यू का इस्तेमाल करें और सदस्यों को लागू करें चुनें.
- सभी तरीकों को चुनें और ठीक है दबाएं.
तीसरा चरण: FakeDataSource में getTasks तरीका लागू करना
FakeDataSource
खास तरह का टेस्ट है, जिसे नकली कहा जाता है. फ़र्ज़ी वह टेस्ट डबल होता है जिसमें &वर्क</a> है, लेकिन क्लास को लागू किया जाता है, लेकिन इसे #39; इस तरह से लागू किया जाता है जिससे यह टेस्ट के लिए अच्छा तो होता है, लेकिन प्रोडक्शन के लिए सही नहीं होता. & कोटेशन
उदाहरण के लिए, आपका नकली डेटा स्रोत नेटवर्क से नहीं जुड़ा होगा या किसी डेटाबेस में कुछ भी सेव नहीं करेगा—इसके बजाय यह सिर्फ़ एक इन-मेमोरी सूची का इस्तेमाल करेगा. यह आपकी उम्मीद के मुताबिक काम कर सकेगा & कोट; उन तरीकों से टास्क पाने या सेव करने के लिए उम्मीद के मुताबिक नतीजे मिलेंगे, लेकिन आप इसे कभी भी प्रोडक्शन में इस्तेमाल नहीं कर सकते, क्योंकि यह सर्वर या डेटाबेस में सेव नहीं होता.
FakeDataSource
- आपको
DefaultTasksRepository
में कोड की जांच करने की ज़रूरत देता है और इसके लिए आपको किसी डेटाबेस या नेटवर्क पर निर्भर होने की ज़रूरत नहीं होती. - जांचों के लिए &real?-ज़रूरत के मुताबिक&कोटेशन; देता है.
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
तरीकों को कॉल किया जाता है.
इन तरीकों का नकली वर्शन लिखें:
getTasks
लिखें: अगरtasks
'tnull
है, तोSuccess
नतीजे दिखाएं. अगरtasks
null
है, तोError
का नतीजा दिखाएं.deleteAllTasks
लिखें: बदले जा सकने वाले टास्क की सूची को मिटाएं.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 में कंस्ट्रक्टर डिपेंडेंसी इंजेक्शन का इस्तेमाल करें
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 }
- डिपेंडेंसी पास हो गई है. इसलिए,
init
तरीका हटाएं. आपको और डिपेंडेंसी बनाने की ज़रूरत नहीं है. - पुराने इंस्टेंस वैरिएबल भी मिटाएं. आप कंस्ट्रक्टर में उन्हें परिभाषित कर रहे हैं:
DefaultTasksRepository.kt
// Delete these old variables
private val tasksRemoteDataSource: TasksDataSource
private val tasksLocalDataSource: TasksDataSource
private val ioDispatcher: CoroutineDispatcher = Dispatchers.IO
- आखिर में, नए कंस्ट्रक्टर का इस्तेमाल करने के लिए
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
की जांच करने के लिए, अपने नकली डेटा स्रोत का इस्तेमाल कर सकते हैं.
DefaultTasksRepository
कक्षा के नाम पर दायां क्लिक करें और जनरेट करें चुनें. इसके बाद, जांच करें चुनें.DefaultTasksRepositoryTest
टेस्ट स्रोत सेट में,DefaultTasksRepositoryTest
बनाने के लिए निर्देशों का पालन करें.- अपनी नई
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 }
- तीन वैरिएबल बनाएं, दो
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
का इस्तेमाल करेगा.
createRepository
नाम का तरीका बनाएं और@Before
के साथ इस बारे में बताएं.remoteTasks
औरlocalTasks
सूचियों का इस्तेमाल करके, अपने नकली डेटा स्रोतों को इंस्टैंशिएट करें.- अभी-अभी बनाए गए दो नकली डेटा स्रोतों और
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
टेस्ट लिखने का समय हो गया है!
- डेटा स्टोर करने की जगह'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
फ़ंक्शन है. इसे कॉल करने के लिए कोरूटीन लॉन्च करना ज़रूरी है. इसके लिए, आपको एक कोरूटीन स्कोप की ज़रूरत है. इस गड़बड़ी को ठीक करने के लिए, आपको अपने टेस्ट में लॉन्च करने वाले कोरूटीन को हैंडल करने के लिए, कुछ ग्रेडल डिपेंडेंसी जोड़नी होंगी.
testImplementation
का इस्तेमाल करके, टेस्ट सोर्स में कोरूटीन को टेस्ट करने के लिए ज़रूरी डिपेंडेंसी जोड़ें.
app/build.gradle
testImplementation "org.jetbrains.kotlinx:kotlinx-coroutines-test:$coroutinesVersion"
सिंक करना न भूलें!
kotlinx-coroutines-test
कोरूटीन टेस्ट लाइब्रेरी है. यह खास तौर पर कोरूटीन को आज़माने के लिए है. अपनी जांच के लिए, runBlockingTest
फ़ंक्शन का इस्तेमाल करें. यह कोरूटीन टेस्ट लाइब्रेरी से मिला एक फ़ंक्शन है. यह कोड का ब्लॉक ब्लॉक करता है और फिर कोड के इस ब्लॉक को एक खास कोरूटीन कॉन्टेक्स्ट में चलाता है, जो सिंक्रोनस और तुरंत चलता है. इसका मतलब है कि कार्रवाइयां तय किए गए क्रम में होंगी. इससे आपके कोरूटीन, नॉन-कोरूटीन की तरह चलते हैं, इसलिए यह टेस्ट कोड के लिए है.
जब आप suspend
फ़ंक्शन को कॉल करते हैं, तो अपनी जांच क्लास में runBlockingTest
का इस्तेमाल करें. आप इस सीरीज़ की अगली कोडलैब (कोड बनाना सीखना) में runBlockingTest
के काम करने के तरीके और टेस्ट कोरूटीन को टेस्ट करने के तरीके के बारे में ज़्यादा जान पाएंगे.
- कक्षा के ऊपर
@ExperimentalCoroutinesApi
जोड़ें. इससे यह पता चलता है कि आप जानते हैं कि आप क्लास में प्रयोग के लिए कोरूटीन एपीआई (runBlockingTest
) का इस्तेमाल कर रहे हैं. इसके बिना, आपको एक चेतावनी मिलेगी. 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))
}
}
- अपना नया
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
के सभी सार्वजनिक तरीके (सार्वजनिक एपीआई प्लैटफ़ॉर्म) शामिल होने चाहिए.
DefaultTasksRepository
खोलें और कक्षा के नाम पर दायां क्लिक करें. इसके बाद, रिफ़ैक्टर -> एक्स्ट्रैक्ट -> इंटरफ़ेस चुनें.
- अलग फ़ाइल के लिए निकालें चुनें.
- एक्सट्रैक्ट इंटरफ़ेस विंडो में, इंटरफ़ेस का नाम बदलकर
TasksRepository
करें. - फ़ॉर्म के लिए सदस्य बनाएं सेक्शन में, दो साथी सदस्यों और निजी तरीकों के अलावा सभी सदस्यों की जांच करें.
- रेफ़रर पर क्लिक करें. नया
TasksRepository
इंटरफ़ेस, डेटा/स्रोत पैकेज में दिखना चाहिए.
साथ ही, DefaultTasksRepository
अब TasksRepository
को लागू करता है.
- अपना ऐप्लिकेशन अपना ऐप्लिकेशन (न कि जांच) चलाएं, ताकि यह पक्का हो सके कि सब कुछ अब भी ठीक से काम कर रहा है.
दूसरा चरण. FakeTestRepository बनाएं
अब जबकि आपके पास इंटरफ़ेस है, तो आप DefaultTaskRepository
टेस्ट डबल बना सकते हैं.
- डेटा स्रोत सेट में, डेटा/स्रोत में Kotlin फ़ाइल और क्लास
FakeTestRepository.kt
बनाएं और इसेTasksRepository
इंटरफ़ेस से बढ़ाएं.
FakeTestRepository.kt
class FakeTestRepository : TasksRepository {
}
आपसे कहा जाएगा कि आपको इंटरफ़ेस के तरीके लागू करने होंगे.
- गड़बड़ी वाला मेन्यू दिखने तक गड़बड़ी पर माउस घुमाएं. इसके बाद, सदस्यों को लागू करें पर क्लिक करके उसे चुनें.
- सभी तरीकों को चुनें और ठीक है दबाएं.
तीसरा चरण. FakeTestRepository तरीके लागू करें
अब आपकी FakeTestRepository
कक्षा में &कोटेशन लागू नहीं हुआ है. FakeDataSource
को लागू करने के तरीके की तरह ही, FakeTestRepository
का इस्तेमाल स्थानीय और रिमोट डेटा स्रोतों के बीच मुश्किल मीडिएशन से निपटने के बजाय, डेटा स्ट्रक्चर के हिसाब से किया जाएगा.
ध्यान दें कि FakeTestRepository
को FakeDataSource
या इस तरह की किसी चीज़ का इस्तेमाल करने की ज़रूरत नहीं होती; यह सिर्फ़ इनपुट के तौर पर असली नकली आउटपुट देने की ज़रूरत होती है. आप टास्क की सूची सेव करने के लिए LinkedHashMap
और निगरानी वाले अपने टास्क के लिए, MutableLiveData
इस्तेमाल करेंगे.
FakeTestRepository
में,LinkedHashMap
वैरिएबल जोड़ें. इसमें टास्क की मौजूदा सूची को दिखाने वाला टास्क और आपके निगरानी किए जा सकने वाले टास्क के लिएMutableLiveData
का इस्तेमाल किया जाता है.
FakeTestRepository.kt
class FakeTestRepository : TasksRepository {
var tasksServiceData: LinkedHashMap<String, Task> = LinkedHashMap()
private val observableTasks = MutableLiveData<Result<List<Task>>>()
// Rest of class
}
इन तरीकों का इस्तेमाल करें:
getTasks
—इस तरीके कोtasksServiceData
का इस्तेमाल करके,tasksServiceData.values.toList()
का इस्तेमाल करके एक सूची में बदलना चाहिए. इसके बाद, इसेSuccess
नतीजे के तौर पर दिखाना चाहिए.refreshTasks
—observableTasks
की वैल्यू कोgetTasks()
से अपडेट कर देता है.observeTasks
—runBlocking
का इस्तेमाल करके कोरूटीन बनाता है और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
को कई बार कॉल कर सकते हैं, लेकिन इस काम को आसान बनाने के लिए, खास तौर पर टेस्ट के लिए हेल्पर वाला तरीका जोड़ें. इससे, आपको टास्क जोड़ने में मदद मिलती है.
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
स्क्रीन से जुड़ी कक्षाओं और टेस्ट को अपडेट करके शुरू करते हैं.
TasksViewModel
खोलें.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
है, लेकिन आप इसे अपनी फ़ाइल में भी रख सकते हैं.
- कक्षा के बाहर,
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
बनाने के तरीके को बदलने का स्टैंडर्ड तरीका है. अब आपके पास फ़ैक्ट्री है, इसलिए जहां भी आप अपना व्यू मॉडल बनाएं, उसका इस्तेमाल करें.
- फ़ैक्ट्री का इस्तेमाल करने के लिए
TasksFragment
अपडेट करें.
TasksFragment.kt
// REPLACE
private val viewModel by viewModels<TasksViewModel>()
// WITH
private val viewModel by viewModels<TasksViewModel> {
TasksViewModelFactory(DefaultTasksRepository.getRepository(requireActivity().application))
}
- अपना ऐप्लिकेशन कोड चलाएं और पक्का करें कि सब कुछ अब भी काम कर रहा हो!
दूसरा चरण. TasksViewModelTest में, FakeTestRepository का इस्तेमाल करना
अब अपने व्यू मॉडल की जांचों में असली रिपॉज़िटरी का इस्तेमाल करने के बजाय, आप नकली डेटा स्टोर करने की जगह का इस्तेमाल कर सकते हैं.
TasksViewModelTest
ऊपर खोलें.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
}
- तीन टास्क वाला
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)
}
- अब आप AndroidX टेस्ट
ApplicationProvider.getApplicationContext
कोड का इस्तेमाल नहीं कर रहे हैं. इसलिए, आप@RunWith(AndroidJUnit4::class)
एनोटेशन भी हटा सकते हैं. - अपने टेस्ट करें. पक्का करें कि वे सभी अब भी काम कर रहे हों!
कंस्ट्रक्टर डिपेंडेंसी इंजेक्शन का इस्तेमाल करके, आपने'DefaultTasksRepository
को डिपेंडेंसी के तौर पर हटा दिया है. साथ ही, टेस्ट में इसे FakeTestRepository
से बदल दिया है.
तीसरा चरण. TaskDetail का फ़्रैगमेंट और ViewModel भी अपडेट करें
TaskDetailFragment
और TaskDetailViewModel
के लिए एक जैसे बदलाव करें. आगे से, TaskDetail
टेस्ट लिखने पर यह कोड तैयार हो जाएगा.
TaskDetailViewModel
खोलें.- कंस्ट्रक्टर अपडेट करें:
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 }
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)
}
- फ़ैक्ट्री का इस्तेमाल करने के लिए
TasksFragment
अपडेट करें.
TasksFragment.kt
// REPLACE
private val viewModel by viewModels<TaskDetailViewModel>()
// WITH
private val viewModel by viewModels<TaskDetailViewModel> {
TaskDetailViewModelFactory(DefaultTasksRepository.getRepository(requireActivity().application))
}
- अपना कोड चलाएं और पक्का करें कि सभी चीज़ें काम कर रही हों.
अब आप TasksFragment
और TasksDetailFragment
में वास्तविक डेटा संग्रह स्थान के बजाय FakeTestRepository
का इस्तेमाल कर सकते हैं.
इसके बाद, अपने फ़्रैगमेंट और व्यू-मॉडल इंटरैक्शन की जांच करने के लिए, आपको इंटिग्रेशन टेस्ट लिखना होगा. आपको #39;पता चलेगा कि आपके व्यू मॉडल का कोड, आपके यूज़र इंटरफ़ेस (यूआई) को सही तरीके से अपडेट करता है या नहीं. ऐसा करने के लिए
- ServiceLocator पैटर्न
- एस्प्रेसो और मॉकिटो लाइब्रेरी
इंटिग्रेशन टेस्ट कई कक्षाओं के इंटरैक्शन का परीक्षण करके यह पक्का करते हैं कि वे एक साथ उपयोग किए जाने पर अपेक्षित तरीके से काम कर रहे हैं. ये टेस्ट, स्थानीय तौर पर (test
सोर्स सेट) या इंस्ट्रूमेंटेशन टेस्ट (androidTest
सोर्स सेट) के रूप में चलाए जा सकते हैं.
आपके केस में #39
पहला चरण. Gradle डिपेंडेंसी जोड़ना
- ये ग्रेडल डिपेंडेंसी जोड़ें.
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
के लिए, फ़्रैगमेंट टेस्ट लिखना शुरू करेंगे. ऐसा इसलिए, क्योंकि इसमें अन्य फ़्रैगमेंट की तुलना में काफ़ी बुनियादी फ़ंक्शन है.
taskdetail.TaskDetailFragment
खोलें.-
TaskDetailFragment
के लिए एक टेस्ट जनरेट करें, जैसा कि आपने पहले किया है'. डिफ़ॉल्ट विकल्पों को स्वीकार करें और उन्हें androidTest सोर्स सेट में रखें (test
सोर्स सेट पर नहीं).
- इन एनोटेशन को
TaskDetailFragmentTest
क्लास में जोड़ें.
TaskDetailFragmentTest.kt
@MediumTest
@RunWith(AndroidJUnit4::class)
class TaskDetailFragmentTest {
}
इस एनोटेशन का मकसद यह है:
@MediumTest
—जांच को "मध्यम रन-टाइम और कोट; इंटिग्रेशन टेस्ट (@SmallTest
यूनिट टेस्ट और@LargeTest
बड़े पैमाने पर एंड-टू-डेट टेस्ट) के तौर पर मार्क करता है. इसकी मदद से आप ग्रुप बना सकते हैं और यह चुन सकते हैं कि किस साइज़ की जांच करनी है.@RunWith(AndroidJUnit4::class)
—AndroidX टेस्ट का इस्तेमाल करके, किसी भी कक्षा में इस्तेमाल किया जाता है.
तीसरा चरण. किसी टेस्ट से फ़्रैगमेंट लॉन्च करना
इस टास्क में, आप TaskDetailFragment
को AndroidX टेस्टिंग लाइब्रेरी का इस्तेमाल करके लॉन्च करने वाले हैं. FragmentScenario
, AndroidX टेस्ट की एक क्लास है, जो फ़्रैगमेंट के आस-पास रैप करती है और आपको फ़्रैगमेंट' के लाइफ़साइकल पर सीधे कंट्रोल देती है. फ़्रैगमेंट के लिए टेस्ट लिखने के लिए, FragmentScenario
आपके बनाए गए फ़्रैगमेंट के लिए टेस्ट किया जाता है're टेस्टिंग (TaskDetailFragment
).
- इस जांच को
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
बनाता है.
यह अभी तक एक पूरा परीक्षण नहीं है, क्योंकि यह किसी भी चीज़ का दावा नहीं कर रहा है. अभी के लिए, जांच करें और देखें कि क्या होता है.
- यह इंस्ट्रूमेंटल टेस्ट है, इसलिए पक्का करें कि एम्युलेटर या आपका डिवाइस दिखे.
- जांच चलाएं.
कुछ चीज़ें होनी चाहिए.
- सबसे पहले, क्योंकि यह एक इंस्ट्रूमेंटल टेस्ट है, इसलिए यह टेस्ट आपके फ़िज़िकल डिवाइस (अगर कनेक्ट है) या एम्युलेटर पर चलेगा.
- फ़्रैगमेंट को लॉन्च करना चाहिए.
- ध्यान दें कि यह किसी दूसरे फ़्रैगमेंट की मदद से कैसे नेविगेट करता है या किसी गतिविधि से जुड़ा कोई मेन्यू कैसे रखता है - यह फ़्रैगमेंट सिर्फ़ है.
आखिर में, ध्यान से देखें कि फ़्रैगमेंट में &डेटा नहीं है &कोटेशन; क्योंकि यह काम का डेटा लोड नहीं करता.
आपकी जांच में दोनों को 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
सोर्स सेट) में, ये सभी डिपेंडेंसी नियमित ऐप्लिकेशन डिपेंडेंसी हैं. जांच के लिए, आप सेवा लोकेटर में बदलाव करते हैं, ताकि डिपेंडेंसी के टेस्ट के लिए दो बार वर्शन उपलब्ध कराए जा सकें.
सेवा लोकेटर का इस्तेमाल नहीं करना | सेवा लोकेटर का इस्तेमाल करना |
इस कोडलैब के लिए, ये काम करें:
- रिसोर्स स्टोर बनाने और सेव करने के लिए, सेवा लोकेटर क्लास बनाएं. डिफ़ॉल्ट रूप से, यह एक "सामान्य&कोटेशन; डेटा स्टोर करने की जगह बनाता है.
- अपने कोड को रीफ़ैक्टर करें, ताकि जब आपको रिपॉज़िटरी की ज़रूरत हो, तो सर्विस लोकेटर का इस्तेमाल करें.
- अपनी टेस्टिंग क्लास में, सेवा लोकेटर पर एक ऐसे तरीके को कॉल करें जो &&tt>सामान्य&कोटेशन को, आपके टेस्ट डबल से बदल देता है.
पहला चरण. ServiceLocator बनाएं
आइए ServiceLocator
की एक क्लास बनाते हैं. यह बाकी ऐप्लिकेशन कोड के साथ मुख्य स्रोत सेट में लाइव रहेगा, क्योंकि यह मुख्य ऐप्लिकेशन कोड में इस्तेमाल होता है.
ध्यान दें: ServiceLocator
एक सिंगलटन है, इसलिए क्लास के लिए Kotlin object
कीवर्ड का इस्तेमाल करें.
- मुख्य स्रोत सेट के सबसे ऊपर के लेवल पर ServiceLocator.kt फ़ाइल बनाएं.
ServiceLocator
नाम कीobject
तय करें.database
औरrepository
इंस्टेंस वैरिएबल बनाएं और दोनों कोnull
पर सेट करें.@Volatile
के साथ रिपॉज़िटरी के बारे में बताएं, क्योंकि इसे कई थ्रेड में इस्तेमाल किया जा सकता है (@Volatile
को यहां बताया गया है).
आपका कोड नीचे दिखाए गए कोड के मुताबिक दिखना चाहिए.
object ServiceLocator {
private var database: ToDoDatabase? = null
@Volatile
var tasksRepository: TasksRepository? = null
}
फ़िलहाल, ServiceLocator
को सिर्फ़ TasksRepository
में आने का तरीका जानना है. यह पहले से मौजूद DefaultTasksRepository
का इस्तेमाल करेगा या ज़रूरत पड़ने पर, नया DefaultTasksRepository
करेगा और वापस करेगा.
ये फ़ंक्शन तय करें:
provideTasksRepository
—इनमें से कोई भी डेटा स्टोर करने की पहले से मौजूदा जगह उपलब्ध है या नई जगह बनाई जाती है. इस तरीके का इस्तेमाल करने के लिए,this
परsynchronized
होना चाहिए. ऐसा तब होता है, जब एक से ज़्यादा थ्रेड चल रहे हों. ऐसे में, कभी-कभी डेटा स्टोर करने के दो इंस्टेंस गलती से बन जाते हैं.createTasksRepository
—डेटा स्टोर करने की नई जगह बनाने के लिए कोड.createTaskLocalDataSource
पर कॉल करेगा और एक नयाTasksRemoteDataSource
बनाएगा.createTaskLocalDataSource
—नया स्थानीय डेटा सोर्स बनाने के लिए कोड.createDataBase
पर कॉल किया जाएगा.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
) बना सकें.
यह ज़रूरी है कि आप रिपॉज़िटरी क्लास का सिर्फ़ एक इंस्टेंस बनाएं. यह पक्का करने के लिए, आप मेरी ऐप्लिकेशन क्लास में सेवा लोकेटर का इस्तेमाल करेंगे.
- अपने पैकेज के क्रम के सबसे ऊपर वाले लेवल पर
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
का पुराना तरीका हटा सकते हैं.
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
ने दिया है.
TaskDetailFragement
खोलें और कक्षा में सबसे ऊपर मौजूदgetRepository
को कॉल करें.- इस कॉल को ऐसे कॉल से बदलें जो
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)
}
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)
}
StatisticsViewModel
औरAddEditTaskViewModel
के लिए, वह कोड अपडेट करें जो रिपॉज़िटरी को हासिल करता है, ताकिTodoApplication
से रिपॉज़िटरी का इस्तेमाल किया जा सके.
TasksFragment.kt
// REPLACE this code
private val tasksRepository = DefaultTasksRepository.getRepository(application)
// WITH this code
private val tasksRepository = (application as TodoApplication).taskRepository
- अपना ऐप्लिकेशन चलाएं (टेस्ट नहीं)!
आपको सिर्फ़ सुरक्षा के फ़ायदे मिलेंगे, इसलिए ऐप्लिकेशन में कोई समस्या नहीं होनी चाहिए.
तीसरा चरण. FakeAndroidTestRepository बनाएं
टेस्ट सोर्स सेट में FakeTestRepository
पहले से मौजूद है. आप डिफ़ॉल्ट रूप से test
और androidTest
सोर्स सेट के बीच टेस्ट क्लास शेयर नहीं कर सकते. इसलिए, आपको androidTest
सोर्स सेट में एक डुप्लीकेट FakeTestRepository
क्लास बनानी होगी और उसे FakeAndroidTestRepository
नाम देना होगा.
androidTest
सोर्स सेट पर दायां क्लिक करें और डेटा पैकेज बनाएं. फिर से दायां क्लिक करें और सोर्स पैकेज बनाएं.- इस स्रोत पैकेज में
FakeAndroidTestRepository.kt
नाम की एक नई क्लास बनाएं. - दिए गए कोड को उस कक्षा में कॉपी करें.
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
कोड में कुछ कोड जोड़ना होगा.
ServiceLocator.kt
खोलें.tasksRepository
के लिए सेटर को@VisibleForTesting
के तौर पर मार्क करें. यह एनोटेशन यह बताने का एक तरीका है कि जांच करने की वजह से सेटर सार्वजनिक है.
ServiceLocator.kt
@Volatile
var tasksRepository: TasksRepository? = null
@VisibleForTesting set
चाहे आप जांच को अकेले या जांच के किसी ग्रुप में चलाएं, आपकी जांच बिल्कुल एक जैसी होनी चाहिए. इसका मतलब यह है कि आपके जांच का कोई ऐसा व्यवहार नहीं होना चाहिए जो एक-दूसरे पर निर्भर हो (यानी जांचों के बीच ऑब्जेक्ट शेयर करने से बचना).
ServiceLocator
एक सिंगलटन है, इसलिए इसमें टेस्ट के बीच गलती से शेयर होने की संभावना है. इससे बचने के लिए, एक ऐसा तरीका बनाएं जो जांचों के बीच ServiceLocator
स्थिति को ठीक से रीसेट कर दे.
lock
वैल्यू वाला इंस्टेंस वैरिएबल जोड़ें, जिसमेंAny
वैल्यू होगी.
ServiceLocator.kt
private val lock = Any()
- जांच के लिए
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
का इस्तेमाल करते हैं.
TaskDetailFragmentTest
खोलें.lateinit TasksRepository
वैरिएबल का एलान करें.- हर जांच से पहले,
FakeAndroidTestRepository
को सेट अप करने के लिए, सेट अप और आंसू अलग करने का तरीका जोड़ें. साथ ही, हर जांच के बाद उसे साफ़ कर दें.
TaskDetailFragmentTest.kt
private lateinit var repository: TasksRepository
@Before
fun initRepository() {
repository = FakeAndroidTestRepository()
ServiceLocator.tasksRepository = repository
}
@After
fun cleanupDb() = runBlockingTest {
ServiceLocator.resetRepository()
}
runBlockingTest
मेंactiveTaskDetails_DisplayedInUi()
का फ़ंक्शन मुख्य हिस्सा रैप करें.- फ़्रैगमेंट को लॉन्च करने से पहले, डेटा स्टोर करने की जगह में
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)
}
@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)
}
}
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 की यह मुख्य सुविधा डिफ़ॉल्ट रूप से शामिल होती है. इसमें ज़्यादातर व्यू और उनकी कार्रवाइयों के लिए बेसिक टेस्टिंग कोड होता है.
दूसरा चरण. ऐनिमेशन बंद करना
एस्प्रेसो टेस्ट एक असली डिवाइस पर किए जाते हैं और इसलिए यह इंस्ट्रुमेंटेशन टेस्ट हैं. एक समस्या जो सामने आती है वह है ऐनिमेशन: अगर कोई ऐनिमेशन देर से दिखता है और आप यह देखने की कोशिश करते हैं कि कोई व्यू स्क्रीन पर दिख रहा है या नहीं, लेकिन वह अब भी ऐनिमेट हो रहा है, तो एस्प्रेसो गलती से जांच में सफल नहीं हो पाएगा. इससे एस्प्रेसो टेस्ट ठीक से नहीं हो सकते.
एस्प्रेसो यूज़र इंटरफ़ेस (यूआई) की जांच के लिए, ऐनिमेशन बंद करने का सबसे सही तरीका यह है# (आपका टेस्ट भी तेज़ी से चलेगा!):
- अपने टेस्टिंग डिवाइस पर, सेटिंग > डेवलपर के लिए सेटिंग और टूल पर जाएं.
- ये तीन सेटिंग बंद करें: विंडो ऐनिमेशन स्केल, ट्रांज़िशन ऐनिमेशन स्केल, और एनिमेटर अवधि का स्केल.
तीसरा चरण. एस्प्रेसो टेस्ट देखें
एस्प्रेसो टेस्ट लिखने से पहले, कुछ एस्प्रेसो कोड पर नज़र डालें.
onView(withId(R.id.task_detail_complete_checkbox)).perform(click()).check(matches(isChecked()))
यह स्टेटमेंट आईडी task_detail_complete_checkbox
के साथ चेकबॉक्स व्यू को खोजता है, उस पर क्लिक करता है, फिर यह पक्का करता है कि उसे चुना गया है.
ज़्यादातर एस्प्रेसो स्टेटमेंट के चार हिस्से होते हैं:
onView
onView
एक स्टैटिक एस्प्रेसो मेथड का उदाहरण है. यह एस्प्रेसो स्टेटमेंट शुरू करता है. onView
सबसे ज़्यादा इस्तेमाल होने वाले विकल्पों में से एक है, लेकिन इसके दूसरे विकल्प हैं, जैसे कि onData
.
2. व्यू मैचर
withId(R.id.task_detail_title_text)
withId
, ViewMatcher
का एक उदाहरण है, जिसे उसके आईडी के हिसाब से एक व्यू मिलता है. कुछ दूसरे व्यू मैचर हैं, जिन्हें आप दस्तावेज़ में देख सकते हैं.
3. ViewAction
perform(click())
perform
वाला तरीका, जो ViewAction
का इस्तेमाल करता है. ViewAction
एक ऐसा काम है जो व्यू के लिए किया जा सकता है, उदाहरण के लिए, यहां व्यू पर क्लिक करके #39.
check(matches(isChecked()))
check
जो ViewAssertion
लेता है. ViewAssertion
, व्यू के बारे में कुछ खोजता है या उस पर दावा करता है. आप ViewAssertion
का सबसे ज़्यादा इस्तेमाल करने के लिए matches
दावा करते हैं. दावा पूरा करने के लिए, दूसरे ViewMatcher
का इस्तेमाल करें, ऐसे में isChecked
.
ध्यान दें कि आप एस्प्रेसो स्टेटमेंट में हमेशा perform
और check
, दोनों को कॉल नहीं करते. आपके पास ऐसे स्टेटमेंट हो सकते हैं जो check
का इस्तेमाल करके दावा करते हैं या perform
का इस्तेमाल करके, ViewAction
करते हैं.
TaskDetailFragmentTest.kt
खोलें.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
// THEN
टिप्पणी के बाद की हर चीज़ Espresso का इस्तेमाल करती है. टेस्ट स्ट्रक्चर औरwithId
के इस्तेमाल की जांच करें. साथ ही, यह पुष्टि करें कि जानकारी वाला पेज कैसा दिखना चाहिए.- टेस्ट चलाएं और पुष्टि करें कि यह पास हो गया है.
चरण 4. वैकल्पिक रूप से, अपना खुद का Espresso टेस्ट लिखें
अब खुद एक टेस्ट लिखें.
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
}
- पिछले टेस्ट को देखते हुए, इस टेस्ट को पूरा करें.
- चलाएं और टेस्ट पास की पुष्टि करें.
खत्म किया गया 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 डिपेंडेंसी जोड़ना
- 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 बनाएं
TasksFragment
खोलें.TasksFragment
कक्षा के नाम पर दायां क्लिक करें. इसके बाद, जनरेट करें और फिर जांच करें चुनें. androidTest सोर्स सेट में टेस्ट बनाएं.- इस कोड को
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
पर ले जाता है.
- टेस्ट
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)
}
- मॉक बनाने के लिए Mockito's
mock
फ़ंक्शन का इस्तेमाल करें.
TasksFragmentTest.kt
val navController = mock(NavController::class.java)
मॉकिटो में मॉक करने के लिए, उस क्लास को पास करें जिसे आप मॉक करना चाहते हैं.
इसके बाद, आपको फ़्रैगमेंट के साथ NavController
को जोड़ना होगा. onFragment
फ़्रैगमेंट पर आप कॉल करने के तरीके खुद कर सकते हैं.
- नए फ़्रैगमेंट को
NavController
का नया मॉक बनाएं.
scenario.onFragment {
Navigation.setViewNavController(it.view!!, navController)
}
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 कार्रवाइयां कर सकते हैं.
- पुष्टि करें कि सही आर्ग्युमेंट के साथ
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")
)
}
- अपना टेस्ट चलाएं!
खास जानकारी में, नेविगेशन की जांच करने के लिए आप यह कर सकते हैं:
NavController
मॉक बनाने के लिए मॉकिटो का इस्तेमाल करें.- फ़्रैगमेंट में मॉक
NavController
को अटैच करें. - पुष्टि करें कि नेविगेशन को सही कार्रवाई और पैरामीटर के साथ कॉल किया गया था.
तीसरा चरण. ज़रूरी नहीं है, ClickAddTaskButton_navigateToAddFragment को लिखें
नेविगेशन टेस्ट खुद लिखें या नहीं, यह देखने के लिए यह टास्क आज़माएं.
- टेस्ट
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 डेवलपर दस्तावेज़:
- ऐप्लिकेशन के आर्किटेक्चर की गाइड
runBlocking
औरrunBlockingTest
FragmentScenario
- एस्प्रेसो
- मॉकीटो
- जेयूनिट4
- AndroidX टेस्ट लाइब्रेरी
- AndroidX आर्किटेक्चर कॉम्पोनेंट कोर टेस्ट लाइब्रेरी
- स्रोत सेट
- कमांड लाइन से जांच करना
वीडियो:
अन्य:
इस कोर्स में दिए गए दूसरे कोडलैब के लिंक के लिए, Kotlin कोडलैब के लैंडिंग पेज में ऐडवांस्ड Android देखें.