आप जानते हैं कि खराब टेस्ट किए गए कोड के साथ काम करना कितना दर्दनाक है। हर बार जब आप एक बग ठीक करते हैं, तो आप पांच और बना देते हैं। और जब चीजें काम करती हैं, तो आप कभी नहीं जानते कि यह डिज़ाइन द्वारा था, या बस संयोग से काम कर गया।
दूसरी ओर, आपने अभी एक छोटी सी सुविधा शिप करने के लिए 200 टेस्ट लिखे जैसे लगते हैं। आपको पहले से काम कर रहे कोड को 100% टेस्ट कवरेज हिट करने के लिए लगातार रीडिज़ाइन करना पड़ता है। आप इस भावना को हिला नहीं सकते कि आपका सबसे अच्छा टेस्ट किया गया कोड किसी तरह कम पठनीय हो रहा है। और सबसे बुरी बात, आप अपने ऐप पर बर्नआउट महसूस करना शुरू कर रहे हैं।
एक मध्य मैदान होना चाहिए। तो कितना टेस्टिंग सही मात्रा है?
यह बहुत अच्छा होता अगर कोई अच्छा गोल संख्या होती जिसका आप नियम के रूप में उपयोग कर सकते थे: ऐप कोड की तुलना में टेस्ट कोड की दोगुनी लाइनें, शायद, या 95% टेस्ट कवरेज। लेकिन यहां तक कि "95% टेस्ट कवरेज" कहना भी अस्पष्ट है।
कवरेज अच्छी तरह से टेस्ट किए गए कोड का एक संकेतक हो सकता है, लेकिन यह अच्छी तरह से टेस्ट किए गए कोड की गारंटी नहीं है। मेरे पास 100%-कवर ऐप्स थे जिनमें 85% कवरेज वाले ऐप्स से अधिक बग थे।
तो, सही मात्रा में टेस्टिंग एक संख्या के बारे में नहीं हो सकती। इसके बजाय, यह किसी धुंधली चीज़ के बारे में है, और परिभाषित करना कठिन है। यह कुशलतापूर्वक टेस्टिंग के बारे में है।
कुशल टेस्टिंग
कुशलता से टेस्टिंग करना कम से कम काम के लिए सबसे अधिक लाभ प्राप्त करने के बारे में है। बहुत अच्छा लगता है, है ना?
लेकिन कुशलतापूर्वक टेस्ट करने में बहुत कुछ जाता है। इसलिए विशेष रूप से तीन चीजों के बारे में सोचने में मदद मिलती है: आकार, अलगाव, और फोकस।
आकार
इंटीग्रेशन टेस्ट कमाल के हैं। वे एक वास्तविक व्यक्ति द्वारा आपके ऐप के माध्यम से लिए गए पथ को दर्पण करते हैं। वे आपके सभी कोड को एक साथ काम करते हुए टेस्ट करते हैं, उसी तरह जैसे वास्तविक दुनिया में इसका उपयोग किया जाता है।
लेकिन इंटीग्रेशन टेस्ट धीमे हैं। वे लंबे और गन्दे हो सकते हैं। और यदि आप अपने सिस्टम के एक छोटे से हिस्से को पूरी तरह से टेस्ट करना चाहते हैं, तो वे बहुत अधिक ओवरहेड जोड़ते हैं।
यूनिट टेस्ट छोटे हैं। वे तेजी से चलते हैं। उनके बारे में सोचना आसान है, क्योंकि आपको उन्हें लिखते समय केवल अपने सिस्टम के एक छोटे से हिस्से को अपने दिमाग में रखने की आवश्यकता होती है।
लेकिन वे नकली भी हो सकते हैं। सिर्फ इसलिए कि कुछ यूनिट टेस्ट के अंदर काम करता है इसका मतलब यह नहीं है कि यह वास्तविक दुनिया में भी काम करेगा। (खासकर यदि आप बहुत मॉकिंग कर रहे हैं)।
तो आप उनको कैसे संतुलित करते हैं?
चूंकि यूनिट टेस्ट तेज और लिखने में आसान हैं, इसलिए उनमें से बहुत सारे रखने में ज्यादा खर्च नहीं होता। इसलिए वे एज केस और जटिल तर्क जैसी चीजों को टेस्ट करने के लिए एक शानदार जगह हैं।
एक बार जब आपके पास आपके सिस्टम के अच्छी तरह से टेस्ट किए गए टुकड़ों का एक गुच्छा होता है, तो आपको अभी भी अंतराल भरने होंगे। आपको टेस्ट करना होगा कि वे हिस्से कैसे इंटरैक्ट करते हैं, और कोई आपके ऐप के माध्यम से पूरी यात्रा कैसे कर सकता है। लेकिन क्योंकि आपके अधिकांश एज केस और तर्क आपके यूनिट टेस्ट द्वारा टेस्ट किए गए हैं, आपको इन अधिक जटिल, धीमे इंटीग्रेशन टेस्ट में से केवल कुछ की आवश्यकता है।
आप इस विचार को "टेस्ट पिरामिड" कहेंगे। यह कई यूनिट टेस्ट के आधार पर बैठे कुछ इंटीग्रेशन टेस्ट हैं। और यदि आप इसके बारे में अधिक जानना चाहते हैं, तो मेरी किताब, Practicing Rails के तीसरे अध्याय को देखें।
अलगाव
फिर भी, यदि आपका सिस्टम जटिल है, तो ऐसा लग सकता है कि आपके द्वारा चलने वाली हर स्थिति को कवर करने के लिए अनंत संख्या में टेस्ट लगेंगे। यह एक संकेत हो सकता है कि आपको अपने ऐप के डिज़ाइन पर पुनर्विचार करने की आवश्यकता है। इसका मतलब है कि आपके सिस्टम के हिस्से एक-दूसरे पर बहुत बारीकी से निर्भर करते हैं।
मान लें कि आपके पास एक ऑब्जेक्ट है जो कुछ अलग-अलग स्थितियों में से एक में हो सकता है:
case user.type when :admin message = admin_message when :user message = user_message when :author message = author_message else message = anonymous_message end if user.preferred_notification_method = :email send_email(message) elsif user.preferred_notification_method = :text send_text_message(message) else queue_notification(message) endpre>यदि आप यहां हर संभावित पथ का टेस्ट करना चाहते थे, तो आपके पास टेस्ट करने के लिए 12 अलग-अलग स्थितियां होंगी:
- यूजर एडमिन है,
preferred_notification_methodईमेल है- यूजर एडमिन है,
preferred_notification_methodटेक्स्ट है- यूजर एडमिन है,
preferred_notification_methodकोई नहीं है- यूजर एक यूजर है,
preferred_notification_methodईमेल है- यूजर एक यूजर है,
preferred_notification_methodटेक्स्ट है- यूजर एक यूजर है,
preferred_notification_methodकोई नहीं है- यूजर एक लेखक है,
preferred_notification_methodईमेल है- यूजर एक लेखक है,
preferred_notification_methodटेक्स्ट है- यूजर एक लेखक है,
preferred_notification_methodकोई नहीं है- यूजर अनाम है,
preferred_notification_methodईमेल है- यूजर अनाम है,
preferred_notification_methodटेक्स्ट है- यूजर अनाम है,
preferred_notification_methodकोई नहीं हैइतने सारे केस टेस्ट करने हैं क्योंकि "अधिसूचना विधि के आधार पर एक संदेश भेजना" और "उपयोगकर्ता के प्रकार के आधार पर एक संदेश उत्पन्न करना" एक साथ बंधे हुए हैं। आप शायद कम के साथ निपट सकते हैं, लेकिन यह स्पष्ट नहीं है - और यह सिर्फ बग मांग रहा है।
लेकिन क्या होगा यदि आप उन्हें अलग कर दें?
message = get_message_based_on_user_type(user.type) send_notification(message, user.preferred_notification_method)pre>अब आप प्रत्येक भाग को अलग से टेस्ट कर सकते हैं।
पहले भाग के लिए, आप टेस्ट कर सकते हैं कि प्रत्येक प्रकार के उपयोगकर्ता के लिए सही संदेश वापस किया जाता है।
दूसरे भाग के लिए, आप टेस्ट कर सकते हैं कि
preferred_notification_methodके मूल्य के आधार पर एक दिया गया संदेश सही ढंग से भेजा जाता है।और अंत में, आप टेस्ट कर सकते हैं कि पैरेंट मेथड
do_stuff_based_on_user_typeसे वापस किए गए संदेश कोsend_email_or_textतक पास करेगा। तो अब, आपके पास टेस्ट करने के लिए 8 स्थितियां हैं:
- यूजर एडमिन है
- यूजर एक यूजर है
- यूजर एक लेखक है
- यूजर अनाम है
preferred_notification_methodईमेल हैpreferred_notification_methodटेक्स्ट हैpreferred_notification_methodकोई नहीं है- और पैरेंट मेथड के लिए एक टेस्ट
यहां, आप कोड को अलग करके अलग से टेस्ट करने के लिए चार टेस्ट बचाते हैं। दूसरे उदाहरण में, यह बहुत अधिक स्पष्ट है कि आप कम टेस्ट के साथ निपट सकते हैं। और आप कल्पना कर सकते हैं कि जैसे-जैसे आप अधिक स्थितियां जोड़ते हैं, अपना कोड विभाजित करना एक और भी बेहतर विचार बन जाता है।
इससे पहले कि आप अलगाव और पठनीयता के बीच सबसे अच्छा संतुलन ढूंढें, इसमें समय और अभ्यास लगता है। लेकिन अगर आप अपने निर्भरताओं को सही जगह पर तोड़ते हैं, तो आप बहुत कम टेस्ट के साथ निपट सकते हैं।
फोकस
आपका ऐप अच्छी तरह से टेस्ट किया जाना चाहिए। लेकिन इसका मतलब यह नहीं है कि आपके ऐप का हर हिस्सा अपने टेस्ट पर समान मात्रा में ध्यान देने का हकदार है।
यहां तक कि अगर आप 100% टेस्ट कवरेज का लक्ष्य रखते हैं, तो भी आप सब कुछ टेस्ट नहीं करेंगे। आप शायद अपने व्यू में टेक्स्ट की हर लाइन को टेस्ट नहीं करेंगे, उदाहरण के लिए, या कि आप हर पांच सेकंड के बजाय हर दस सेकंड में अपडेट के लिए पोलिंग कर रहे हैं।
यहीं फोकस आता है। कम, अधिक उपयोगी टेस्ट लिखना। और एक सचेत निर्णय लेना कि आप अपना समय सबसे अच्छी तरह से कहां खर्च कर सकते हैं।
फोकस एक और चीज है जिसे सही करना कठिन है। ये कुछ प्रश्न हैं जो मैं खुद से पूछता हूं जो मुझे सबसे महत्वपूर्ण टेस्ट पर ध्यान केंद्रित करने में मदद करते हैं:
यह मेरे ऐप के बाकी हिस्सों से कितना परस्पर जुड़ा हुआ है? अगर यह टूटता है, तो कितने अन्य टुकड़े इसके साथ गिरेंगे?
इसके स्वाभाविक रूप से बदलने की कितनी संभावना है? अगर मेरे टेस्ट विफल होते हैं, तो क्या यह बग के कारण होगा, या क्योंकि किसी ने UI में कुछ टेक्स्ट अपडेट किया?
इसके टूटने का प्रभाव क्या है? क्या मैं किसी के क्रेडिट कार्ड को दो बार चार्ज करने जा रहा हूं, या यह सिर्फ कुछ लापता टेक्स्ट के साथ समाप्त होगा?
इस भाग का कितनी बार उपयोग किया जाता है? क्या यह ऐप के व्यवहार के लिए महत्वपूर्ण है, या यह फूटर में कहीं दबा एक अबाउट पेज है?
आपको केवल महत्वपूर्ण भागों का टेस्ट नहीं करना चाहिए। लेकिन अगर आप अपना टेस्टिंग समय अच्छी तरह से खर्च करते हैं तो आपके पास एक ऐप होगा जो महसूस करता है उच्च गुणवत्ता वाला।
यदि आप अपने ऐप के माध्यम से कोई व्यक्ति ले जा सकने वाले हर एक संभावित पथ का टेस्ट करने की कोशिश करता है, तो आप कभी शिप नहीं कर पाएंगे। TDD मदद करता है, लेकिन यह आपकी सभी टेस्टिंग समस्याओं को हल नहीं करेगा।
बेशक, इसका मतलब यह नहीं है कि आपको बिल्कुल भी टेस्ट नहीं करना चाहिए।
आप अपने टेस्ट को छोटा रखने के लिए टेस्ट पिरामिड का उपयोग कर सकते हैं। आप m * n टेस्ट केस को m + n में बदलने के लिए निर्भरताओं को अलग और तोड़ सकते हैं। और आप प्राथमिकता दे सकते हैं, ताकि आप अपने ऐप के सबसे महत्वपूर्ण भागों का टेस्ट करने में अधिक समय बिता सकें।
तो, आप कितना टेस्ट करते हैं? क्या आप अपने ऐप को बनाते समय इनमें से किसी भी विचार पर विचार करते हैं? और आप कैसे जानते हैं कि अपने ऐप के किन भागों पर ध्यान केंद्रित करना है? एक टिप्पणी छोड़ें और मुझे इसके बारे में सब बताएं!