क्या परीक्षण योग्य कोड बेहतर कोड है?


103

मैं अपने कोड के साथ नियमित रूप से यूनिट टेस्ट लिखने की आदत में आने का प्रयास कर रहा हूं, लेकिन मैंने पढ़ा है कि पहले टेस्टेबल कोड लिखना महत्वपूर्ण है । यह प्रश्न परीक्षण योग्य कोड लिखने के एसओएलआईडी सिद्धांतों पर छूता है, लेकिन मैं यह जानना चाहता हूं कि क्या उन डिजाइन सिद्धांतों को लाभकारी है (या कम से कम हानिकारक नहीं है) बिल्कुल परीक्षण लिखने की योजना के बिना। स्पष्ट करने के लिए - मैं परीक्षण लिखने के महत्व को समझता हूं; यह उनकी उपयोगिता पर सवाल नहीं है।

मेरे भ्रम को स्पष्ट करने के लिए, इस प्रश्न को प्रेरित करने वाले अंश में , लेखक एक ऐसे फ़ंक्शन का उदाहरण देता है जो वर्तमान समय की जाँच करता है, और समय के आधार पर कुछ मान लौटाता है। लेखक इसे बुरे कोड के रूप में इंगित करता है क्योंकि यह आंतरिक रूप से उपयोग किए जाने वाले डेटा (समय) का उत्पादन करता है, जिससे परीक्षण करना मुश्किल हो जाता है। मेरे लिए, हालांकि, यह तर्क के रूप में समय में पारित करने के लिए ओवरकिल जैसा लगता है। कुछ बिंदु पर मूल्य को शुरू करने की आवश्यकता है, और खपत के सबसे करीब क्यों नहीं? इसके अलावा, मेरे दिमाग में विधि का उद्देश्य वर्तमान समय के आधार पर कुछ मूल्य वापस करना है , इसे एक पैरामीटर बनाकर आप इसका मतलब है कि यह उद्देश्य बदल सकता है / होना चाहिए। यह और अन्य प्रश्न मुझे आश्चर्यचकित करते हैं कि क्या परीक्षण योग्य कोड "बेहतर" कोड का पर्याय था।

क्या परीक्षण योग्य कोड परीक्षण के अभाव में अभी भी अच्छा अभ्यास है ?


क्या परीक्षण योग्य कोड वास्तव में अधिक स्थिर है? डुप्लिकेट के रूप में सुझाया गया है। हालाँकि, यह प्रश्न कोड की "स्थिरता" के बारे में है, लेकिन मैं इस बारे में अधिक विस्तृत रूप से पूछ रहा हूं कि क्या कोड अन्य कारणों से बेहतर है, जैसे कि पठनीयता, प्रदर्शन, युग्मन, और इसके आगे।


24
समारोह की एक विशेष संपत्ति है जिसे आपको समय के साथ निष्क्रियता कहा जाता है ऐसा फ़ंक्शन हर बार एक ही परिणाम का उत्पादन करेगा , जिसे एक दिए गए तर्क मूल्य के साथ कहा जाता है, जो न केवल इसे और अधिक परीक्षण करने योग्य बनाता है, बल्कि इसके बारे में तर्क करने के लिए अधिक व्यावहारिक और आसान है।
रॉबर्ट हार्वे

4
क्या आप "बेहतर कोड" को परिभाषित कर सकते हैं? क्या आपका मतलब "रख-रखाव" है ?, "आसान-से-बिना-आईओसी-कंटेनर-मैजिक"?
k3b

7
मुझे लगता है कि आपने कभी टेस्ट फेल नहीं किया है क्योंकि यह वास्तविक सिस्टम समय का उपयोग करता है और फिर समय क्षेत्र बदल गया है।
एंडी

5
यह अनस्टेबल कोड से बेहतर है।
ट्यूलेंस कोर्डोवा

14
@RobertHarvey मुझे लगता है कि idempotency फोन नहीं होगा, मैं कहूंगा कि यह निर्देशात्मक पारदर्शिता : अगर func(X)रिटर्न "Morning", तो के सभी आवृत्तियां की जगह func(X)के साथ "Morning"कार्यक्रम में परिवर्तन नहीं होगा (यानी बुला। funcअन्य किसी भी चीज से मूल्य वापस नहीं करता है)। बेरोजगारी का तात्पर्य या तो यह है कि func(func(X)) == X(जो टाइप-सही नहीं है), या जो func(X); func(X);उसी तरह के साइड-इफ़ेक्ट करता है func(X)(लेकिन यहाँ कोई साइड-इफ़ेक्ट नहीं हैं)
वॉर्बो

जवाबों:


116

इकाई परीक्षणों की सामान्य परिभाषा के संबंध में, मैं कहूंगा कि नहीं। मैं सरल कोड परीक्षण ढांचे के अनुरूप करने के लिए यह मोड़ करने की आवश्यकता (जैसे। इंटरफेस और की वजह से जटिल बना देखा है आईओसी हर जगह बातें इंटरफ़ेस कॉल और डेटा है कि जादू से में पारित स्पष्ट होना चाहिए की परतों के माध्यम से पालन करने के लिए मुश्किल हो जाता है)। कोड के बीच की पसंद को समझना आसान है या कोड को समझना आसान है जो यूनिट टेस्ट के लिए है, मैं हर बार मेंटेन करने योग्य कोड के साथ जाता हूं।

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

आप अपने कोड को बनाए रखने के लिए आसान बना सकते हैं और फिर भी यूनिट परीक्षणों के लिए उनके बीच अच्छे इंटरफेस को परिभाषित करके और फिर ऐसे परीक्षण लिख सकते हैं जो घटक के सार्वजनिक इंटरफ़ेस का अभ्यास करते हैं; या आप एक बेहतर परीक्षण ढाँचा प्राप्त कर सकते हैं (एक ऐसा है जो रनटाइम के कार्यों को बदलने के लिए उन्हें जगह देने के लिए कोड की आवश्यकता होती है) एक बेहतर इकाई परीक्षण ढाँचा आपको रनटाइम के दौरान सिस्टम गेटक्रीट टाइम () कार्यक्षमता को अपने साथ बदलने की सुविधा देता है, इसलिए आपको परीक्षण उपकरण के अनुकूल होने के लिए कृत्रिम आवरणों को पेश करने की आवश्यकता नहीं है।


3
टिप्पणियाँ विस्तारित चर्चा के लिए नहीं हैं; इस वार्तालाप को बातचीत में स्थानांतरित कर दिया गया है ।
वर्ल्ड इंजीनियर

2
मुझे लगता है कि यह ध्यान देने योग्य है कि मुझे कम से कम एक भाषा का पता है जो आपको वह करने की अनुमति देता है जो आपके अंतिम पैराग्राफ का वर्णन करता है: मॉक के साथ पायथन। मॉड्यूल आयात के काम के कारण, कीवर्ड से हटकर बहुत कुछ भी एक नकली, यहां तक ​​कि मानक एपीआई विधियों / कक्षाओं / आदि के साथ बदला जा सकता है। तो यह संभव है, लेकिन यह आवश्यक हो सकता है कि भाषा एक तरह से इस तरह के लचीलेपन का समर्थन करती है।
jpmc26

5
मुझे लगता है कि "टेस्टेबल कोड" और "कोड [ट्विस्टेड] टेस्टिंग फ्रेमवर्क के अनुरूप" में अंतर है। मुझे यकीन नहीं है कि मैं इस टिप्पणी के साथ कहां जा रहा हूं, कहने के अलावा मैं सहमत हूं कि "मुड़" कोड खराब है, और अच्छे इंटरफेस के साथ "परीक्षण योग्य" कोड अच्छा है।
ब्रायन ओकले

2
मैंने अपने कुछ विचार लेख की टिप्पणियों में व्यक्त किए हैं (क्योंकि विस्तारित टिप्पणियों की यहां अनुमति नहीं है), इसे देखें! स्पष्ट होने के लिए: मैं उल्लिखित लेख का लेखक हूं :)
सेर्गेई कोलोडी

मैं @BryanOakley से सहमत हूँ। "परीक्षण योग्य कोड" का सुझाव है कि आपकी चिंताओं को अलग कर दिया गया है: अन्य पहलुओं के हस्तक्षेप के बिना एक पहलू (मॉड्यूल) का परीक्षण करना संभव है। मैं कहूंगा कि यह "आपकी परियोजना समर्थन विशिष्ट परीक्षण सम्मेलनों को समायोजित करने" से अलग है। यह डिजाइन पैटर्न के समान है: उन्हें मजबूर नहीं किया जाना चाहिए। कोड है कि ठीक से डिजाइन पैटर्न का इस्तेमाल मजबूत कोड पर विचार किया जाएगा। एक ही परीक्षण सिद्धांतों के लिए लागू होता है। यदि आपके कोड को "परीक्षण योग्य" बनाने के परिणामस्वरूप आपकी परियोजना के कोड को अत्यधिक घुमाया जा रहा है, तो आप कुछ गलत कर रहे हैं।
विंस एमिग

68

क्या परीक्षण योग्य कोड परीक्षण के अभाव में अभी भी अच्छा अभ्यास है?

पहली चीजें पहले, परीक्षणों की अनुपस्थिति आपके कोड के परीक्षण योग्य होने या न होने की तुलना में एक बड़ा मुद्दा है। इकाई परीक्षण नहीं होने का मतलब है कि आप अपने कोड / सुविधा के साथ नहीं हैं।

इस तरह से, मैं यह नहीं कहूंगा कि परीक्षण योग्य कोड लिखना महत्वपूर्ण है - लचीला कोड लिखना महत्वपूर्ण है । अनम्य कोड का परीक्षण करना कठिन है, इसलिए दृष्टिकोण में बहुत अधिक ओवरलैप है और लोग इसे कहते हैं।

इसलिए मेरे लिए, कोड लिखने में हमेशा प्राथमिकताएँ होती हैं:

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

यूनिट परीक्षण कोड बनाए रखने में मदद करते हैं, लेकिन केवल एक बिंदु तक। यदि आप इकाई परीक्षणों को काम करने के लिए कोड को कम पठनीय, या अधिक नाजुक बनाते हैं, तो काउंटर उत्पादक हो जाता है। "टेस्टेबल कोड" आम तौर पर लचीला कोड होता है, इसलिए यह अच्छा है, लेकिन कार्यक्षमता या रखरखाव के लिए उतना महत्वपूर्ण नहीं है। वर्तमान समय जैसी किसी चीज़ के लिए, उस लचीले को बनाना अच्छा है लेकिन यह सही और अधिक जटिल का उपयोग करने के लिए कोड को कठिन बनाकर स्थिरता बनाए रखता है। चूँकि स्थिरता अधिक महत्वपूर्ण है, इसलिए मैं आमतौर पर सरल दृष्टिकोण की ओर भूल जाता हूँ, भले ही यह कम परीक्षण योग्य हो।


4
मुझे वह रिश्ता पसंद है जो आप परीक्षण योग्य और लचीले के बीच इंगित करते हैं - जो पूरी समस्या को मेरे लिए अधिक समझने योग्य बनाता है। लचीलापन आपके कोड को अनुकूलित करने की अनुमति देता है, लेकिन जरूरी यह समझने के लिए थोड़ा अधिक सार और कम सहज बनाता है, लेकिन यह लाभ के लिए एक सार्थक बलिदान है।
WannabeCoder

3
उस ने कहा, अक्सर मैं ऐसे तरीकों को देखता हूं जो निजी होना चाहिए था सार्वजनिक या पैकेज स्तर के क्रम में यूनिट परीक्षण ढांचे के लिए उन्हें सीधे उपयोग करने में सक्षम होना चाहिए। एक आदर्श दृष्टिकोण से दूर।
जुंटिंग

4
@WannabeCoder बेशक, यह केवल लचीलेपन को जोड़ने के लायक है जब यह आपको अंत में समय बचाता है। इसलिए हम इंटरफ़ेस के विरुद्ध हर एक विधि नहीं लिखते हैं - अधिकांश समय कोड को फिर से लिखना शुरू करने के बजाय बहुत अधिक लचीलेपन को शामिल करना आसान होता है। YAGNI अभी भी एक अत्यंत शक्तिशाली सिद्धांत है - बस यह सुनिश्चित कर लें कि जो चीज "आप की आवश्यकता नहीं है" वह है, इसे रेट्रो रूप से जोड़ना आपको समय से पहले इसे लागू करने की तुलना में औसतन अधिक काम नहीं देगा । यह YAGNI के बाद का कोड नहीं है जिसमें मेरे अनुभव में लचीलेपन के साथ सबसे अधिक मुद्दे हैं।
लुआं

3
"इकाई परीक्षण नहीं होने का मतलब है कि आप अपने कोड / सुविधा के साथ नहीं हैं" - असत्य। "की गई परिभाषा" कुछ ऐसा है जो टीम तय करती है। इसमें कुछ हद तक परीक्षण कवरेज शामिल हो सकता है या नहीं। लेकिन कहीं भी एक सख्त आवश्यकता नहीं है जो कहती है कि एक सुविधा "नहीं" हो सकती है अगर इसके लिए कोई परीक्षण नहीं हैं। टीम को परीक्षण की आवश्यकता हो सकती है, या वे नहीं कर सकते हैं।
एरोह

3
@Telastyn विकास के 10 वर्षों में, मेरे पास कभी भी एक टीम नहीं थी जिसने एक इकाई परीक्षण रूपरेखा को अनिवार्य किया था, और केवल दो में से एक (दोनों का खराब कवरेज था)। आपके द्वारा लिखी जा रही सुविधा का परीक्षण करने के लिए एक जगह एक शब्द दस्तावेज़ की आवश्यकता है। बस। शायद मैं बदकिस्मत हूं? मैं एंटी-यूनिट टेस्ट नहीं कर रहा हूं (गंभीरता से, मैं SQA.SE साइट को मॉड करता हूं, मैं बहुत प्रो यूनिट टेस्ट हूं!) लेकिन मैंने उन्हें आपके बयान के दावों के अनुसार व्यापक नहीं पाया है।
corsiKa

50

हां, यह अच्छा अभ्यास है। कारण यह है कि परीक्षण के लिए परीक्षणशीलता नहीं है। यह स्पष्टता और समझ के लिए है कि यह अपने साथ लाता है।

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

परीक्षण होने से हमारे परीक्षण के स्वतंत्र रूप से हमारे व्यवसाय कोड में सुधार कैसे होता है? हमें अपनी कार्यक्षमता को उन इकाइयों में विभाजित करने के लिए मजबूर करके जो आसानी से सही होने के लिए प्रदर्शित होती हैं। ये इकाइयाँ और भी आसान हैं जिन्हें हम अन्यथा लिखना पसंद करेंगे।

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


एक अन्य उदाहरण यह है कि यदि आप किसी एक परियोजना के कोड का कुछ हिस्सा किसी अन्य स्थान पर खींचना चाहते हैं (जो भी कारण हो)। कार्यक्षमता के अलग-अलग हिस्से एक-दूसरे से स्वतंत्र होते हैं, यह उतना ही आसान है जितना कि आपकी ज़रूरत की कार्यक्षमता को निकालना और अधिक कुछ नहीं।
वैलेंट्री जूल

10
Nobody cares about the tests themselves-- मैं करता हूँ। मुझे लगता है कि किसी भी टिप्पणी या readme फ़ाइलों की तुलना में कोड क्या करता है, इसका एक बेहतर दस्तावेज होना चाहिए।
१२:०१ पर जूलम

मैं धीरे-धीरे कुछ समय के लिए परीक्षण प्रथाओं के बारे में पढ़ रहा हूं (जैसा कि किसी तरह जो अभी तक कोई इकाई परीक्षण नहीं करता है) और मुझे कहना है, नियंत्रित परिस्थितियों में कॉल को सत्यापित करने के बारे में अंतिम भाग, और अधिक लचीला कोड जो इसके साथ आता है यह, सभी तरह की चीजों ने जगह बना ली। धन्यवाद।
plast1k

12

कुछ बिंदु पर मूल्य को शुरू करने की आवश्यकता है, और खपत के सबसे करीब क्यों नहीं?

क्योंकि आपको उस कोड को फिर से उपयोग करने की आवश्यकता हो सकती है, आंतरिक रूप से उत्पन्न की तुलना में एक अलग मूल्य के साथ। एक पैरामीटर के रूप में आपके द्वारा उपयोग किए जा रहे मूल्य को सम्मिलित करने की क्षमता, यह सुनिश्चित करती है कि आप उन मानों को उत्पन्न कर सकते हैं जिन्हें आप किसी भी समय पसंद करते हैं, न कि केवल "अब" (कोड को कॉल करते समय "अब" के साथ "अर्थ")।

कोड को वास्तव में परीक्षण योग्य बनाने का मतलब है कि कोड बनाना (शुरू से) दो अलग-अलग परिदृश्यों (उत्पादन और परीक्षण) में उपयोग किया जा सकता है।

मूल रूप से, जब आप तर्क कर सकते हैं कि परीक्षणों की अनुपस्थिति में कोड को परीक्षण योग्य बनाने के लिए कोई प्रोत्साहन नहीं है, पुन: प्रयोज्य कोड लिखने में बहुत फायदा है, और दो समानार्थी हैं।

इसके अलावा, मेरे दिमाग में विधि का उद्देश्य वर्तमान समय के आधार पर कुछ मूल्य वापस करना है, इसे एक पैरामीटर बनाकर आप इसका मतलब है कि यह उद्देश्य बदल सकता है / होना चाहिए।

आप यह भी तर्क दे सकते हैं कि इस पद्धति का उद्देश्य समय मूल्य के आधार पर कुछ मूल्य वापस करना है, और आपको "अब" के आधार पर इसे उत्पन्न करने की आवश्यकता है। उनमें से एक अधिक लचीला है, और यदि आपको उस संस्करण को चुनने की आदत है, तो समय के साथ, आपके कोड पुन: उपयोग की दर बढ़ जाएगी।


10

यह इस तरह कहने के लिए मूर्खतापूर्ण लग सकता है, लेकिन अगर आप अपने कोड का परीक्षण करने में सक्षम होना चाहते हैं, तो हाँ, परीक्षण योग्य कोड लिखना बेहतर है। तुम पूछो:

कुछ बिंदु पर मूल्य को शुरू करने की आवश्यकता है, और खपत के सबसे करीब क्यों नहीं?

ठीक है क्योंकि, उदाहरण में आप जिसका उल्लेख कर रहे हैं, यह उस कोड को अप्राप्य बनाता है। जब तक आप केवल दिन के अलग-अलग समय पर अपने परीक्षणों का एक सबसेट नहीं चलाते। या आप सिस्टम क्लॉक को रीसेट कर देते हैं। या कुछ और वर्कअराउंड। जिनमें से सभी बस अपने कोड को लचीला बनाने से भी बदतर हैं।

अनम्य होने के अलावा, प्रश्न में उस छोटे तरीके की दो जिम्मेदारियां होती हैं: (1) सिस्टम का समय प्राप्त करना और फिर (2) इसके आधार पर कुछ मूल्य वापस करना।

public static string GetTimeOfDay()
{
    DateTime time = DateTime.Now;
    if (time.Hour >= 0 && time.Hour < 6)
    {
        return "Night";
    }
    if (time.Hour >= 6 && time.Hour < 12)
    {
        return "Morning";
    }
    if (time.Hour >= 12 && time.Hour < 18)
    {
        return "Afternoon";
    }
    return "Evening";
}

यह आगे की जिम्मेदारियों को तोड़ने के लिए समझ में आता है, ताकि आपके नियंत्रण से बाहर का हिस्सा ( DateTime.Now) आपके कोड के बाकी हिस्सों पर कम से कम प्रभाव डाले। ऐसा करने से कोड सरल हो जाएगा, जिसका साइड इफेक्ट व्यवस्थित रूप से परीक्षण करने योग्य होगा।


1
इसलिए आपको यह जांचने के लिए सुबह जल्दी परीक्षा देनी होगी कि आपको "रात" का परिणाम कब चाहिए। यह मुश्किल है। अब मान लें कि आप तिथि की जांच करना चाहते हैं कि दिनांक 29 फरवरी 2016 को सही है ... और कुछ iOS प्रोग्रामर (और शायद अन्य) एक शुरुआत की गलती से ग्रस्त हैं जो साल की शुरुआत से पहले या बाद में किसी चीज़ को गड़बड़ कर देता है, तो आप कैसे करते हैं उसके लिए परीक्षण करें। और अनुभव से, मैं 2 फरवरी, 2020 को तारीख से निपटने की जाँच
करूँगा।

1
@ gnasher729 बिल्कुल मेरी बात। "इस कोड को परीक्षण योग्य बनाना" एक साधारण परिवर्तन है जो बहुत सारी (परीक्षण) समस्याओं को हल कर सकता है। यदि आप परीक्षण को स्वचालित नहीं करना चाहते हैं, तो मुझे लगता है कि कोड जैसा है वैसा ही है। लेकिन यह बेहतर होगा कि एक बार यह "परीक्षण योग्य" हो।
एरिक किंग

9

यह निश्चित रूप से एक लागत है, लेकिन कुछ डेवलपर्स इसे भुगतान करने के आदी हैं कि वे भूल गए हैं कि लागत वहां है। आपके उदाहरण के लिए, अब आपके पास एक के बजाय दो इकाइयाँ हैं, आपको अतिरिक्त निर्भरता को शुरू करने और प्रबंधित करने के लिए कॉलिंग कोड की आवश्यकता है, और GetTimeOfDayअधिक परीक्षण योग्य होने के बावजूद, आप उसी नाव में वापस अपने नए परीक्षण कर रहे हैं IDateTimeProvider। यह सिर्फ इतना है कि यदि आपके पास अच्छे परीक्षण हैं, तो लाभ आमतौर पर लागत को कम कर देते हैं।

इसके अलावा, एक निश्चित डिग्री के लिए, परीक्षण योग्य कोड लिखने से आपको अपने कोड को अधिक रखरखाव योग्य तरीके से आर्किटेक्ट करने के लिए प्रोत्साहित किया जाता है। नया निर्भरता प्रबंधन कोड कष्टप्रद है, इसलिए यदि संभव हो तो आप अपने सभी समय-निर्भर कार्यों को एक साथ करना चाहते हैं। उदाहरण के लिए, जब आप किसी पृष्ठ को सही समय सीमा पर लोड करते हैं, तो कुछ तत्वों को पहले समय का उपयोग करके और कुछ का समय के बाद उपयोग करके ठीक करने में मदद कर सकते हैं। यह वर्तमान समय को प्राप्त करने के लिए बार-बार सिस्टम कॉल से बचकर आपके कार्यक्रम को गति दे सकता है।

बेशक, उन वास्तु सुधार अत्यधिक अवसरों पर ध्यान देने वाले और उन्हें लागू करने वाले व्यक्ति पर निर्भर हैं। इकाइयों पर इतनी बारीकी से ध्यान केंद्रित करने के सबसे बड़े खतरों में से एक बड़ी तस्वीर पर नजर गड़ाए हुए है।

कई यूनिट टेस्ट फ्रेमवर्क आपको रनटाइम के दौरान बंदर की नकली वस्तु बनाते हैं, जो आपको सभी गड़बड़ियों के बिना परीक्षण क्षमता का लाभ देता है। मैंने इसे C ++ में भी देखा है। उन स्थितियों में उस क्षमता को देखें जहां ऐसा लगता है कि परीक्षण क्षमता लागत इसके लायक नहीं है।


+1 - आपको लेखन इकाई परीक्षणों को आसान बनाने के लिए डिज़ाइन और आर्किटेक्चर में सुधार करने की आवश्यकता है।
B21овиЈ

3
+ - अपने कोड की वास्तुकला जो मायने रखती है। आसान परीक्षण सिर्फ एक खुश साइड-इफेक्ट है।
gbjbaanb

8

यह संभव है कि प्रत्येक विशेषता जो परीक्षण करने में योगदान करती है, परीक्षण के संदर्भ के बाहर वांछनीय है - मुझे उदाहरण के लिए, आपके द्वारा उद्धृत समय पैरामीटर के लिए गैर-परीक्षण-संबंधी औचित्य के साथ आने में परेशानी है, लेकिन मोटे तौर पर उन विशेषताओं को बोलना है जो परीक्षण करने में योगदान करते हैं परीक्षण क्षमता की परवाह किए बिना अच्छे कोड में भी योगदान करते हैं।

मोटे तौर पर, परीक्षण योग्य कोड निंदनीय कोड है। यह छोटे, असतत, सामंजस्यपूर्ण, विखंडू में है, इसलिए व्यक्तिगत बिट्स को पुन: उपयोग के लिए बुलाया जा सकता है। यह अच्छी तरह से संगठित और अच्छी तरह से नामांकित है (नामकरण पर ध्यान देने के लिए कुछ कार्यक्षमता का परीक्षण करने में सक्षम होने के लिए; यदि आप परीक्षण नहीं लिख रहे थे तो एकल-उपयोग फ़ंक्शन के लिए नाम कम मायने रखेगा)। यह अधिक पैरामीट्रिक हो जाता है (जैसे आपका समय उदाहरण), इसलिए मूल इच्छित उद्देश्य की तुलना में अन्य संदर्भों से उपयोग करने के लिए खुला है। यह DRY है, इसलिए कम बरबाद और समझने में आसान है।

हाँ। परीक्षण योग्य कोड लिखना अच्छा है, यहां तक ​​कि परीक्षण के बावजूद।


इसके बारे में असहमत हैं DRY - एक विधि में GetCurrentTime लपेटकर MyGetCurrentTime परीक्षण टूलिंग की सहायता करने के अलावा कोई लाभ नहीं के साथ OS कॉल दोहरा रहा है। उदाहरणों में यह सबसे सरल है, वे वास्तविकता में बहुत खराब हो जाते हैं।
gbjbaanb

1
"बिना किसी बेनिफिट के ओएस कॉल को दोहराना" - जब तक आप एक सर्वर पर एक घड़ी के साथ चलना समाप्त नहीं करते हैं, एक अलग टाइमज़ोन में एवर्स सर्वर से बात करते हैं, और जो आपके कोड को तोड़ता है, और फिर आपको अपने सभी कोड से गुजरना होगा और इसे MyGetCurrentTime का उपयोग करने के लिए अपडेट करें, जो बदले में UTC देता है। ; घड़ी तिरछी, दिन के उजाले की बचत, और अन्य कारण हैं कि ओएस कॉल पर आँख बंद करके भरोसा करना एक अच्छा विचार नहीं हो सकता है, या कम से कम एक बिंदु है जहां आप एक और प्रतिस्थापन में छोड़ सकते हैं।
एंड्रयू हिल

8

परीक्षण योग्य कोड लिखना महत्वपूर्ण है यदि आप यह साबित करने में सक्षम होना चाहते हैं कि आपका कोड वास्तव में काम करता है।

मैं अपने कोड को केवल एक विशेष परीक्षण ढांचे में फिट करने के लिए जघन्य अंतर्विरोधों में कोड करने के बारे में नकारात्मक भावनाओं से सहमत हूं।

दूसरी ओर, हर किसी के यहाँ, किसी न किसी बिंदु पर, उस 1,000 लाइन लंबे जादू समारोह से निपटना पड़ता है, जिससे निपटने के लिए सिर्फ जघन्य है, वस्तुतः एक या एक से अधिक अस्पष्ट तोड़ने के बिना छुआ नहीं जा सकता, गैर- स्पष्ट रूप से निर्भरता कहीं और (या अपने भीतर कहीं, जहां निर्भरता लगभग कल्पना करना असंभव है) और परिभाषा से बहुत ज्यादा अप्रतिष्ठित है। यह धारणा (जो योग्यता के बिना नहीं है) कि टेस्टिंग फ्रेमवर्क ओवरब्लो हो गए हैं, मेरी राय में, खराब गुणवत्ता, अप्राप्य कोड लिखने के लिए नि: शुल्क लाइसेंस के रूप में नहीं लिया जाना चाहिए।

टेस्ट चालित विकास आदर्शों एकल जिम्मेदारी प्रक्रियाओं लेखन, उदाहरण के लिए ओर पुश करने के लिए करते हैं, और है कि निश्चित रूप से एक अच्छी बात है। व्यक्तिगत रूप से, मैं कहता हूं कि एकल-जिम्मेदारी, सत्य के एकल स्रोत, नियंत्रित दायरे (कोई फ्रीकिन 'वैश्विक चर) में न खरीदें और भंगुर निर्भरता को कम से कम रखें, और आपका कोड परीक्षण योग्य होगा । कुछ विशिष्ट परीक्षण ढांचे द्वारा परीक्षण योग्य? कौन जाने। लेकिन तब शायद यह टेस्टिंग फ्रेमवर्क है जिसे खुद को अच्छे कोड में समायोजित करने की आवश्यकता है, न कि दूसरे तरीके से।

लेकिन सिर्फ स्पष्ट होने के लिए, कोड जो इतना चालाक है, या इतना लंबा और / या अन्योन्याश्रित है कि यह आसानी से किसी अन्य इंसान द्वारा समझ में नहीं आता है अच्छा कोड नहीं है। और यह भी, संयोग से, कोड नहीं है जिसे आसानी से परीक्षण किया जा सकता है।

तो मेरे सारांश के पास, परीक्षण योग्य कोड बेहतर कोड है?

मुझे नहीं पता, शायद नहीं। यहां के लोगों के पास कुछ वैध बिंदु हैं।

लेकिन मेरा मानना ​​है कि बेहतर कोड भी टेस्टेबल कोड होता है।

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

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


1
"बेहतर कोड भी परीक्षण योग्य कोड हो जाता है" यह कुंजी है। इसे टेस्टेबल बनाना बेहतर नहीं है। इसे बेहतर बनाना अक्सर इसे परीक्षण योग्य बनाता है, और परीक्षण अक्सर आपको ऐसी जानकारी देते हैं जिसका उपयोग आप इसे बेहतर बनाने के लिए कर सकते हैं, लेकिन परीक्षणों की मात्र उपस्थिति गुणवत्ता को प्रभावित नहीं करती है, और (दुर्लभ) अपवाद हैं।
अनएक्समैंडर

1
ठीक ठीक। गर्भनिरोधक पर विचार करें। यदि यह अप्राप्य कोड है, तो इसका परीक्षण नहीं किया गया है। यदि इसका परीक्षण नहीं किया गया है, तो आपको कैसे पता चलेगा कि यह जीवित स्थिति में काम करता है या नहीं?
pjc50

1
सभी परीक्षण साबित करते हैं कि कोड परीक्षणों को पास करता है। अन्यथा इकाई परीक्षण कोड बग मुक्त होगा और हम जानते हैं कि ऐसा नहीं है।
wobbily_col

@anaximander बिल्कुल। कम से कम संभावना है कि परीक्षणों की मात्र उपस्थिति एक contraindication है जो खराब गुणवत्ता कोड के परिणामस्वरूप होती है यदि सभी ध्यान सिर्फ चेकबॉक्स की जांच पर है। "प्रत्येक फ़ंक्शन के लिए कम से कम सात यूनिट टेस्ट?" "चेक।" लेकिन मैं वास्तव में विश्वास करता हूं कि यदि कोड गुणवत्ता कोड है, तो यह परीक्षण करना आसान होगा।
क्रेग

1
... लेकिन नौकरशाही को परीक्षण से बाहर करना एक कुल बेकार हो सकता है और उपयोगी जानकारी या भरोसेमंद परिणाम नहीं दे सकता है। भले ही; मुझे यकीन है कि किसी ने एसएसएल हार्टबल बग, हां का परीक्षण किया था ? या Apple गोटो असफल बग?
क्रेग

5

मेरे लिए, हालांकि, यह तर्क के रूप में समय में पारित करने के लिए ओवरकिल जैसा लगता है।

आप सही हैं, और मॉकिंग के साथ आप कोड को परीक्षण योग्य बना सकते हैं और समय गुजरने से बच सकते हैं (वाक्य इरादा अपरिभाषित)। उदाहरण कोड:

def time_of_day():
    return datetime.datetime.utcnow().strftime('%H:%M:%S')

अब हम कहते हैं कि आप परीक्षण करना चाहते हैं कि एक लीप सेकंड के दौरान क्या होता है। जैसा कि आप कहते हैं, इस ओवरकिल तरीके का परीक्षण करने के लिए आपको (उत्पादन) कोड बदलना होगा:

def time_of_day(now=None):
    now = now if now is not None else datetime.datetime.utcnow()
    return now.strftime('%H:%M:%S')

यदि पायथन ने छलांग सेकंड का समर्थन किया तो परीक्षण कोड इस तरह दिखाई देगा:

def test_handle_leap_second(self):
    actual = time_of_day(
        now=datetime.datetime(year=2015, month=6, day=30, hour=23, minute=59, second=60)
    expected = '23:59:60'
    self.assertEquals(actual, expected)

आप इसका परीक्षण कर सकते हैं, लेकिन कोड आवश्यकता से अधिक जटिल है और परीक्षण अभी भी उस शाखा शाखा का मज़बूती से उपयोग नहीं कर सकते हैं जो कि अधिकांश उत्पादन कोड का उपयोग कर रहे होंगे (अर्थात, मान के लिए पास नहीं है now)। आप एक मॉक का उपयोग करके इसके चारों ओर काम करते हैं । मूल उत्पादन कोड से शुरू:

def time_of_day():
    return datetime.datetime.utcnow().strftime('%H:%M:%S')

टेस्ट कोड:

@unittest.patch('datetime.datetime.utcnow')
def test_handle_leap_second(self, utcnow_mock):
    utcnow_mock.return_value = datetime.datetime(
        year=2015, month=6, day=30, hour=23, minute=59, second=60)
    actual = time_of_day()
    expected = '23:59:60'
    self.assertEquals(actual, expected)

इससे कई लाभ मिलते हैं:

  • आप time_of_day स्वतंत्र रूप से इसकी निर्भरता का परीक्षण कर रहे हैं ।
  • आप उत्पादन कोड के समान कोड पथ का परीक्षण कर रहे हैं ।
  • उत्पादन कोड जितना संभव हो उतना सरल है।

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


FYI करें: आपका डिफ़ॉल्ट तर्क गलत है। यह केवल एक बार परिभाषित किया जाएगा, इसलिए आपका फ़ंक्शन हमेशा उस समय को लौटाएगा, जब इसका पहले मूल्यांकन किया गया था।
अरहस

4

अच्छी तरह से लिखित कोड की एक गुणवत्ता यह है कि यह बदलने के लिए मजबूत है । यही है, जब एक आवश्यकताओं में बदलाव आता है, तो कोड में परिवर्तन आनुपातिक होना चाहिए। यह एक आदर्श (और हमेशा हासिल नहीं किया गया) है, लेकिन परीक्षण योग्य कोड लिखने से हमें इस लक्ष्य के करीब पहुंचने में मदद मिलती है।

यह हमें करीब लाने में मदद क्यों करता है? उत्पादन में, हमारा कोड उत्पादन पर्यावरण के भीतर संचालित होता है, जिसमें हमारे सभी अन्य कोड के साथ एकीकरण और सहभागिता शामिल है। इकाई परीक्षणों में, हम इस वातावरण से बहुत दूर निकल जाते हैं। हमारा कोड अब बदलने के लिए मजबूत हो रहा है क्योंकि परीक्षण एक बदलाव है । हम विभिन्न तरीकों से इकाइयों का उपयोग कर रहे हैं, विभिन्न इनपुट्स के साथ (नकली, खराब इनपुट जो वास्तव में कभी भी पास नहीं हो सकते हैं, आदि) हम उत्पादन में उनका उपयोग करेंगे।

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


4

मेरे अनुभव से, एक प्रोग्राम बनाते समय आपके द्वारा किए गए सबसे महत्वपूर्ण और सबसे दूरगामी फैसलों में से एक यह है कि आप कोड को इकाइयों में कैसे तोड़ते हैं (जहां "इकाइयों" का उपयोग इसके व्यापक अर्थों में किया जाता है)। यदि आप क्लास-आधारित ओओ भाषा का उपयोग कर रहे हैं, तो आपको प्रोग्राम को कुछ वर्गों में लागू करने के लिए उपयोग किए जाने वाले सभी आंतरिक तंत्रों को तोड़ने की आवश्यकता है। फिर आपको प्रत्येक वर्ग के कोड को कुछ तरीकों से तोड़ने की आवश्यकता है। कुछ भाषाओं में, विकल्प यह है कि अपने कोड को फ़ंक्शन में कैसे तोड़ें। या यदि आप SOA बात करते हैं, तो आपको यह तय करने की आवश्यकता है कि आप कितनी सेवाओं का निर्माण करेंगे और प्रत्येक सेवा में क्या जाएंगे।

आपके द्वारा चुने जाने वाले ब्रेकडाउन का पूरी प्रक्रिया पर भारी प्रभाव पड़ता है। अच्छे विकल्प से कोड को लिखना आसान हो जाता है, और कम बग (आप परीक्षण और डिबगिंग शुरू करने से पहले) में परिणाम करते हैं। वे बदलने और बनाए रखने में आसान बनाते हैं। दिलचस्प है, यह पता चला है कि एक बार जब आप एक अच्छा ब्रेकडाउन पाते हैं, तो आमतौर पर एक गरीब की तुलना में परीक्षण करना आसान होता है ।

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

यह सिर्फ इतना नहीं है, हालांकि। एक अच्छे आंतरिक डिजाइन का मतलब है कि कार्यान्वयन की प्रत्येक इकाई के अपेक्षित व्यवहार (इनपुट / आउटपुट / आदि) को स्पष्ट और सटीक रूप से परिभाषित किया जा सकता है। यह परीक्षण के लिए महत्वपूर्ण है। एक अच्छे डिजाइन का अर्थ आमतौर पर यह होता है कि कार्यान्वयन की प्रत्येक इकाई में दूसरों पर निर्भरता की एक मध्यम संख्या होगी। यह आपके कोड को दूसरों के लिए पढ़ना और समझना आसान बनाता है, लेकिन यह परीक्षण करना भी आसान बनाता है। कारण चलते हैं; हो सकता है कि अन्य लोग अधिक कारण बता सकें जो मैं नहीं कर सकता।

आपके प्रश्न में उदाहरण के संबंध में, मुझे नहीं लगता कि "अच्छा कोड डिज़ाइन" यह कहने के बराबर है कि सभी बाहरी निर्भरताएं (जैसे सिस्टम घड़ी पर निर्भरता) हमेशा "इंजेक्ट" होनी चाहिए। यह एक अच्छा विचार हो सकता है, लेकिन यह एक अलग मुद्दा है कि मैं यहां क्या वर्णन कर रहा हूं और मैं इसके पेशेवरों और विपक्षों में नहीं दूंगा।

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

सारांश में, परीक्षण योग्य कोड आवश्यक रूप से "अच्छा" कोड नहीं है, लेकिन "अच्छा" कोड आमतौर पर परीक्षण योग्य है।


1

आप के साथ जा रहे हैं ठोस सिद्धांतों आप अच्छा तरफ हो जाएगा, खासकर अगर साथ इस विस्तार KISS , सूखी , और YAGNI

मेरे लिए एक लापता बिंदु एक विधि की जटिलता का बिंदु है। क्या यह एक सरल गटर / सेटर विधि है? फिर सिर्फ अपने परीक्षण ढांचे को संतुष्ट करने के लिए परीक्षण लिखना समय की बर्बादी होगी।

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

लेखन परीक्षण का एक अन्य पहलू अन्य डेवलपर्स को यह दिखाना है कि आपकी पद्धति का उपयोग कैसे करें। कई बार एक डेवलपर एक विधि का उपयोग करने के तरीके पर एक उदाहरण के लिए खोज करेगा और वापसी मूल्य क्या होगा।

बस मेरे दो सेंट

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.