डिजाइन सिद्धांत क्या हैं जो परीक्षण योग्य कोड को बढ़ावा देते हैं? (परीक्षण के माध्यम से ड्राइविंग कोड बनाम परीक्षण डिजाइन तैयार करना)


54

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

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

मैंने पढ़ा है कि कुछ ठोस के रूप में जाना जाता है। मैं यह समझना चाहता हूं कि क्या SOLID सिद्धांतों का पालन करने से कोड में अप्रत्यक्ष रूप से परिणाम आता है जो आसानी से परीक्षण योग्य है? यदि नहीं, तो क्या कोई अच्छी तरह से परिभाषित डिज़ाइन सिद्धांत हैं जो परीक्षण योग्य कोड को बढ़ावा देते हैं?

मुझे पता है कि टेस्ट ड्रिवेन डेवलपमेंट के नाम से कुछ जाना जाता है। हालाँकि, मुझे डिज़ाइन के चरण के दौरान परीक्षण के साथ डिजाइन के बजाय कोड को डिजाइन करने में अधिक रुचि है। मुझे लगता है कि इसका मतलब बनता है।

इस विषय से संबंधित एक और सवाल यह है कि क्या किसी मौजूदा उत्पाद / परियोजना को फिर से फ़ैक्टर करना और प्रत्येक मॉड्यूल के लिए यूनिट टेस्ट केस लिखने में सक्षम होने के लिए कोड और डिज़ाइन में बदलाव करना ठीक है?


3
इसे देखें: googletesting.blogspot.in/2008/08/…
VS1

धन्यवाद। मैंने केवल लेख पढ़ना शुरू किया है और यह पहले से ही समझ में आता है।

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

जवाबों:


56

हां, SOLID कोड डिजाइन करने का एक बहुत अच्छा तरीका है जिसे आसानी से जांचा जा सकता है। एक छोटे प्राइमर के रूप में:

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

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

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

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

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

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

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

आईएसपी का पालन परीक्षण के तहत प्रणालियों की जटिलता और उन SUT की निर्भरता को कम करके परीक्षण क्षमता में सुधार करता है। यदि आप जिस ऑब्जेक्ट का परीक्षण कर रहे हैं, वह एक इंटरफ़ेस IDoThreeThings पर निर्भर करता है जो DoOne (), DoTwo () और DoThree () को उजागर करता है, तो आपको एक ऐसी वस्तु का मज़ाक उड़ाना होगा जो सभी तीन विधियों को लागू करती है, भले ही ऑब्जेक्ट केवल DoTwo विधि का उपयोग करता हो। लेकिन, यदि ऑब्जेक्ट केवल IDoTwo पर निर्भर करता है (जो केवल DoTwo को उजागर करता है), तो आप अधिक आसानी से एक ऑब्जेक्ट को मॉक कर सकते हैं जिसमें वह एक विधि है।

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

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


16

मैंने पढ़ा है कि कुछ ठोस के रूप में जाना जाता है। मैं यह समझना चाहता हूं कि क्या SOLID सिद्धांतों का पालन करने से कोड में अप्रत्यक्ष रूप से परिणाम आता है जो आसानी से परीक्षण योग्य है?

यदि सही तरीके से लागू किया जाता है, तो हाँ। नहीं है ब्लॉग पोस्ट जेफ द्वारा एक में ठोस सिद्धांत हैं उनकी व्याख्या वास्तव में कम रास्ता (उल्लेख पॉडकास्ट भी सुनने लायक है), मैं एक नज़र वहाँ देने का सुझाव देते हैं, तो लंबे समय तक विवरण आप बंद फेंक रहे हैं।

मेरे अनुभव से, SOLID के 2 सिद्धांत परीक्षण योग्य कोड डिजाइन करने में प्रमुख भूमिका निभाते हैं:

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

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

(...) क्या किसी मौजूदा उत्पाद / परियोजना को फिर से फ़ैक्टर करना और प्रत्येक मॉड्यूल के लिए यूनिट टेस्ट केस लिखने में सक्षम होने के उद्देश्य से कोड और डिज़ाइन में बदलाव करना ठीक है?

मौजूदा इकाई परीक्षणों के बिना, इसे सरल रूप से कहा जाता है - परेशानियों के लिए पूछना। यूनिट टेस्ट आपकी गारंटी है कि आपका कोड काम करता है । यदि आपके पास उचित परीक्षण कवरेज है, तो ब्रेकिंग परिवर्तन का परिचय तुरंत दिया जाता है।

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

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


iaw उच्च सामंजस्य और कम युग्मन
jk।

8

आपका पहला प्रश्न:

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

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

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

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


4

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

मुझे एक बार एक वर्ग का परीक्षण करना पड़ा था, जिसका तर्क बहुत जटिल था, और बहुत सी स्थितियों के साथ, और जहाँ खेतों की भूमिका को समझना कठिन था।

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

तथ्य यह है कि राज्यों को स्पष्ट किया गया था कि कोड के सभी संभावित रास्तों (राज्य संक्रमण) की गणना करना आसान हो गया है, और इस प्रकार प्रत्येक के लिए एक इकाई-परीक्षण लिखना है।

बेशक, प्रत्येक जटिल तर्क को एक राज्य मशीन के रूप में मॉडल नहीं किया जा सकता है।


3

SOLID एक उत्कृष्ट शुरुआत है, मेरे अनुभव में, SOLID के चार पहलू यूनिट परीक्षण के साथ वास्तव में अच्छी तरह से काम करते हैं।

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

मैं अलग-अलग पैटर्न, विशेषकर फ़ैक्टरी पैटर्न पर भी ध्यान देता हूँ। मान लीजिए कि आपके पास एक ठोस वर्ग है जो एक इंटरफ़ेस लागू करता है। आप कंक्रीट वर्ग को तुरंत बनाने के लिए एक कारखाना बनाएंगे, लेकिन इसके बजाय इंटरफ़ेस वापस कर देंगे।

public interface ISomeInterface
{
    int GetValue();
}  

public class SomeClass : ISomeInterface
{
    public int GetValue()
    {
         return 1;
    }
}

public interface ISomeOtherInterface
{
    bool IsSuccess();
}

public class SomeOtherClass : ISomeOtherInterface
{
     private ISomeInterface m_SomeInterface;

     public SomeOtherClass(ISomeInterface someInterface)
     {
          m_SomeInterface = someInterface;
     }

     public bool IsSuccess()
     {
          return m_SomeInterface.GetValue() == 1;
     }
}

public class SomeFactory
{
     public virtual ISomeInterface GetSomeInterface()
     {
          return new SomeClass();
     }

     public virtual ISomeOtherInterface GetSomeOtherInterface()
     {
          ISomeInterface someInterface = GetSomeInterface();

          return new SomeOtherClass(someInterface);
     }
}

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

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


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