मुझे कुछ कारणों से निजी कार्यक्षमता का परीक्षण करना पसंद नहीं है। वे इस प्रकार हैं (ये TLDR लोगों के लिए मुख्य बिंदु हैं):
- आमतौर पर जब आप कक्षा की निजी पद्धति का परीक्षण करने के लिए ललचाते हैं, तो यह एक डिजाइन गंध होती है।
- आप उन्हें सार्वजनिक इंटरफ़ेस के माध्यम से परीक्षण कर सकते हैं (जो कि आप उन्हें कैसे परीक्षण करना चाहते हैं, क्योंकि यही क्लाइंट उन्हें कॉल / उपयोग करेगा)। आप अपने निजी तरीकों के लिए सभी पासिंग टेस्ट पर हरी बत्ती देखकर सुरक्षा का गलत अर्थ निकाल सकते हैं। अपने सार्वजनिक इंटरफ़ेस के माध्यम से अपने निजी कार्यों पर किनारे के मामलों का परीक्षण करना बेहतर / सुरक्षित है।
- निजी तरीकों का परीक्षण करके आप गंभीर परीक्षण दोहराव (बहुत समान दिखने वाले परीक्षण) का जोखिम उठाते हैं। जब आवश्यकताएं बदलती हैं तो इसके बड़े परिणाम होते हैं, क्योंकि आवश्यकता से अधिक परीक्षण टूटेंगे। यह आपको उस स्थिति में भी डाल सकता है जहां आपके टेस्ट सूट के कारण रिफ्लेक्टर करना मुश्किल है ... जो कि अंतिम विडंबना है, क्योंकि टेस्ट सूट आपको सुरक्षित रूप से रीडिजाइन और रिफलेक्टर करने में मदद करने के लिए है!
मैं इनमें से प्रत्येक को एक ठोस उदाहरण के साथ समझाता हूँ। यह पता चला है कि 2) और 3) कुछ हद तक जटिल रूप से जुड़े हुए हैं, इसलिए उनका उदाहरण समान है, हालांकि मैं उन्हें अलग-अलग कारणों से मानता हूं कि आपको निजी तरीकों का परीक्षण क्यों नहीं करना चाहिए।
कई बार निजी तरीकों का परीक्षण करना उचित है, ऊपर सूचीबद्ध डाउनसाइड्स के बारे में पता होना बहुत जरूरी है। मैं बाद में इसे और अधिक विस्तार से बताने जा रहा हूं।
मैं यह भी बताता हूं कि बहुत अंत में टीडीडी निजी तरीकों के परीक्षण के लिए एक वैध बहाना क्यों नहीं है।
ख़राब डिज़ाइन से अपना रास्ता निकाल कर
सबसे आम (विरोधी) पितरों में से एक जो मैं देखता हूं कि माइकल फेदर क्या कहते हैं एक "हिमखंड" वर्ग (यदि आप जो माइकल पंख है पता नहीं है, खरीदने जाने / अपनी पुस्तक "विरासत कोड के साथ प्रभावी ढंग से काम कर रहे" पढ़ें। वह है यदि आप एक पेशेवर सॉफ़्टवेयर इंजीनियर / डेवलपर हैं) के बारे में जानने लायक व्यक्ति। अन्य (एंटी) पैटर्न हैं जो इस मुद्दे को क्रॉप करने का कारण बनते हैं, लेकिन यह अब तक सबसे आम है जिसे मैंने ठोकर खाई है। "आइसबर्ग" कक्षाओं में एक सार्वजनिक विधि है, और बाकी निजी हैं (यही कारण है कि यह निजी तरीकों का परीक्षण करने के लिए आकर्षक है)। इसे "आइसबर्ग" वर्ग कहा जाता है क्योंकि आमतौर पर एक अकेला सार्वजनिक तरीका होता है, लेकिन बाकी कार्यक्षमता निजी तरीकों के रूप में पानी के नीचे छिपी होती है।
उदाहरण के लिए, आप परीक्षण करना चाह सकते हैं GetNextToken()
क्रमिक एक स्ट्रिंग पर बुला और देख कि यह उम्मीद परिणाम देता है। इस तरह का एक कार्य एक परीक्षण को वारंट करता है: यह व्यवहार तुच्छ नहीं है, खासकर यदि आपके टोकन नियम जटिल हैं। चलो दिखावा करते हैं कि यह सब जटिल नहीं है, और हम केवल अंतरिक्ष द्वारा सीमांकित टोकन रस्सी करना चाहते हैं। तो आप एक परीक्षण लिखते हैं, शायद यह कुछ इस तरह दिखता है (कुछ भाषा अज्ञेय-पीडो-कोड, उम्मीद है कि विचार स्पष्ट है):
TEST_THAT(RuleEvaluator, canParseSpaceDelimtedTokens)
{
input_string = "1 2 test bar"
re = RuleEvaluator(input_string);
ASSERT re.GetNextToken() IS "1";
ASSERT re.GetNextToken() IS "2";
ASSERT re.GetNextToken() IS "test";
ASSERT re.GetNextToken() IS "bar";
ASSERT re.HasMoreTokens() IS FALSE;
}
खैर, यह वास्तव में बहुत अच्छा लग रहा है। हम यह सुनिश्चित करना चाहते हैं कि जैसे-जैसे हम परिवर्तन करते हैं, हम इस व्यवहार को बनाए रखें। लेकिन GetNextToken()
एक निजी समारोह है! इसलिए हम इसे इस तरह से नहीं परख सकते हैं, क्योंकि यह भी संकलन नहीं करता है (यह मानते हुए कि हम कुछ भाषा का उपयोग कर रहे हैं जो वास्तव में सार्वजनिक / निजी को लागू करता है, कुछ स्क्रिप्टिंग भाषाओं जैसे पाइथन के विपरीत)। लेकिन RuleEvaluator
एकल जिम्मेदारी सिद्धांत (एकल जिम्मेदारी सिद्धांत) का पालन करने के लिए कक्षा को बदलने के बारे में क्या ? उदाहरण के लिए, हमें एक पार्सर, टोकनधारक और मूल्यांकनकर्ता एक वर्ग में जाम लगता है। क्या उन जिम्मेदारियों को अलग करना बेहतर नहीं होगा? उसके ऊपर, यदि आप बनाते हैंTokenizer
वर्ग , तो यह सार्वजनिक तरीके हैं HasMoreTokens()
और GetNextTokens()
। RuleEvaluator
वर्ग एक हो सकता थाTokenizer
एक सदस्य के रूप में वस्तु। अब, हम ऊपर के समान परीक्षण रख सकते हैं, सिवाय इसके कि हम Tokenizer
कक्षा के बजाय कक्षा का परीक्षण कर रहे हैं RuleEvaluator
।
यहाँ यह UML में कैसा दिख सकता है:
ध्यान दें कि यह नया डिज़ाइन प्रतिरूपकता बढ़ाता है, इसलिए आप संभवतः अपने सिस्टम के अन्य भागों में इन वर्गों का फिर से उपयोग कर सकते हैं (इससे पहले कि आप निजी विधियों को परिभाषा द्वारा पुन: प्रयोज्य नहीं कर सकते हैं)। यह RuleEvaluator को तोड़ने का मुख्य लाभ है, साथ ही बढ़ी हुई समझ / स्थानीयता के साथ।
परीक्षा बेहद समान दिखाई देगी, सिवाय इसके कि वास्तव में इस बार संकलन होगा क्योंकि GetNextToken()
विधि अब Tokenizer
कक्षा में सार्वजनिक है :
TEST_THAT(Tokenizer, canParseSpaceDelimtedTokens)
{
input_string = "1 2 test bar"
tokenizer = Tokenizer(input_string);
ASSERT tokenizer.GetNextToken() IS "1";
ASSERT tokenizer.GetNextToken() IS "2";
ASSERT tokenizer.GetNextToken() IS "test";
ASSERT tokenizer.GetNextToken() IS "bar";
ASSERT tokenizer.HasMoreTokens() IS FALSE;
}
सार्वजनिक इंटरफ़ेस के माध्यम से निजी घटकों का परीक्षण करना और परीक्षण दोहराव से बचना
यहां तक कि अगर आपको नहीं लगता है कि आप अपनी समस्या को कम मॉड्यूलर घटकों (जो आप 95% समय पर कर सकते हैं यदि आप इसे करने की कोशिश करते हैं) में तोड़ सकते हैं , तो आप बस एक सार्वजनिक इंटरफ़ेस के माध्यम से निजी कार्यों का परीक्षण कर सकते हैं। कई बार निजी सदस्य परीक्षण के लायक नहीं होते क्योंकि उनका परीक्षण सार्वजनिक इंटरफ़ेस के माध्यम से किया जाएगा। बहुत बार जो मैं देखता हूं, वह परीक्षण है जो बहुत लगते हैं समान , लेकिन दो अलग-अलग कार्यों / विधियों का परीक्षण करते हैं। क्या हो रहा समाप्त होता है कि जब आवश्यकताओं परिवर्तन (और वे हमेशा करते हैं), तो आप अब 2 टूट परीक्षण 1. के बजाय और अगर आप वास्तव में आपकी सभी निजी तरीकों का परीक्षण किया है, तो आप 1. के बजाय और अधिक की तरह 10 टूट परीक्षण हो सकता है है संक्षेप में , निजी कार्यों का परीक्षण (उपयोग करके)FRIEND_TEST
या उन्हें सार्वजनिक या परावर्तन का उपयोग करते हुए) जिसे अन्यथा एक सार्वजनिक इंटरफ़ेस के माध्यम से परीक्षण किया जा सकता है, परीक्षण दोहराव का कारण बन सकता है । आप वास्तव में यह नहीं चाहते हैं, क्योंकि आपके टेस्ट सूट से अधिक कुछ भी आपको धीमा नहीं करता है। यह विकास के समय को कम करने और रखरखाव की लागत को कम करने वाला है! यदि आप निजी तरीकों का परीक्षण करते हैं जो अन्यथा एक सार्वजनिक इंटरफ़ेस के माध्यम से परीक्षण किया जाता है, तो परीक्षण सूट बहुत अच्छी तरह से विपरीत कर सकता है, और सक्रिय रूप से रखरखाव की लागत में वृद्धि और विकास के समय को बढ़ा सकता है। जब आप किसी निजी फ़ंक्शन को सार्वजनिक करते हैं, या यदि आप कुछ FRIEND_TEST
और / या प्रतिबिंब का उपयोग करते हैं , तो आप आमतौर पर इसे लंबे समय में पछतावा करेंगे।
Tokenizer
कक्षा के निम्नलिखित संभावित कार्यान्वयन पर विचार करें :
मान लीजिए कि SplitUpByDelimiter()
एक सरणी को वापस करने के लिए ज़िम्मेदार है जैसे कि सरणी में प्रत्येक तत्व एक टोकन है। इसके अलावा, चलो बस कहना है कि GetNextToken()
इस वेक्टर पर बस एक पुनरावृत्ति है। तो आपका सार्वजनिक परीक्षण यह देख सकता है:
TEST_THAT(Tokenizer, canParseSpaceDelimtedTokens)
{
input_string = "1 2 test bar"
tokenizer = Tokenizer(input_string);
ASSERT tokenizer.GetNextToken() IS "1";
ASSERT tokenizer.GetNextToken() IS "2";
ASSERT tokenizer.GetNextToken() IS "test";
ASSERT tokenizer.GetNextToken() IS "bar";
ASSERT tokenizer.HasMoreTokens() IS false;
}
आइए दिखाते हैं कि हमारे पास माइकल फेदर का ग्रोपिंग टूल क्या है । यह एक उपकरण है जो आपको अन्य लोगों के निजी भागों को छूने देता है। एक उदाहरण FRIEND_TEST
गूगलेट से है, या प्रतिबिंब यदि भाषा इसका समर्थन करती है।
TEST_THAT(TokenizerTest, canGenerateSpaceDelimtedTokens)
{
input_string = "1 2 test bar"
tokenizer = Tokenizer(input_string);
result_array = tokenizer.SplitUpByDelimiter(" ");
ASSERT result.size() IS 4;
ASSERT result[0] IS "1";
ASSERT result[1] IS "2";
ASSERT result[2] IS "test";
ASSERT result[3] IS "bar";
}
खैर, अब कहते हैं कि आवश्यकताएं बदल जाती हैं, और टोकन बहुत अधिक जटिल हो जाता है। आप तय करते हैं कि एक साधारण स्ट्रिंग परिसीमन पर्याप्त नहीं होगा, और आपको Delimiter
काम संभालने के लिए एक वर्ग की आवश्यकता होगी । स्वाभाविक रूप से, आप एक परीक्षण को तोड़ने की उम्मीद करने जा रहे हैं, लेकिन जब आप निजी कार्यों का परीक्षण करते हैं तो यह दर्द बढ़ जाता है।
निजी तरीकों का परीक्षण कब उचित हो सकता है?
सॉफ्टवेयर में कोई "एक आकार सभी फिट बैठता है" नहीं है। कभी-कभी "नियमों को तोड़ने" के लिए यह ठीक है (और वास्तव में आदर्श)। जब आप कर सकते हैं तो मैं निजी कार्यक्षमता का परीक्षण नहीं करने की दृढ़ता से वकालत करता हूं। जब मुझे लगता है कि यह ठीक है तो दो मुख्य परिस्थितियां हैं:
मैंने विरासत प्रणालियों के साथ बड़े पैमाने पर काम किया है (यही वजह है कि मैं इतना बड़ा माइकल फेदर प्रशंसक हूं), और मैं सुरक्षित रूप से कह सकता हूं कि कभी-कभी यह सिर्फ निजी कार्यक्षमता का परीक्षण करने के लिए सबसे सुरक्षित है। यह बेसलाइन में "चरित्रांकन परीक्षण" प्राप्त करने के लिए विशेष रूप से सहायक हो सकता है।
आप जल्दबाज़ी में हैं, और यहाँ और अभी के लिए सबसे तेज़ काम करना है। लंबे समय में, आप निजी तरीकों का परीक्षण नहीं करना चाहते हैं। लेकिन मैं कहूंगा कि आमतौर पर डिज़ाइन के मुद्दों को हल करने में कुछ समय लगता है। और कभी-कभी आपको एक सप्ताह में जहाज करना पड़ता है। यह ठीक है: त्वरित और गंदे काम करें और एक ग्रूपिंग टूल का उपयोग करके निजी तरीकों का परीक्षण करें यदि आपको लगता है कि यह काम पाने का सबसे तेज़ और सबसे विश्वसनीय तरीका है। लेकिन यह समझें कि आपने जो कुछ किया था, वह लंबे समय में उप-विषयी था, और कृपया इसे वापस आने पर विचार करें (या, अगर यह भूल गया था, लेकिन आप इसे बाद में देखते हैं, तो इसे ठीक करें)।
वहाँ शायद अन्य स्थितियों जहाँ यह ठीक है। अगर आपको लगता है कि यह ठीक है, और आपके पास एक अच्छा औचित्य है, तो इसे करें। कोई आपको रोक नहीं रहा है। बस संभावित लागतों के बारे में पता होना चाहिए।
TDD बहाना
एक तरफ के रूप में, मैं वास्तव में निजी तरीकों के परीक्षण के लिए एक बहाने के रूप में TDD का उपयोग करने वाले लोगों को पसंद नहीं करता। मैं टीडीडी का अभ्यास करता हूं, और मुझे नहीं लगता कि टीडीडी आपको ऐसा करने के लिए मजबूर करता है। आप अपना परीक्षण (अपने सार्वजनिक इंटरफ़ेस के लिए) पहले लिख सकते हैं, और फिर उस इंटरफ़ेस को संतुष्ट करने के लिए कोड लिख सकते हैं। कभी-कभी मैं एक सार्वजनिक इंटरफ़ेस के लिए एक परीक्षण लिखता हूं, और मैं इसे एक या दो छोटे निजी तरीकों को लिखकर भी संतुष्ट करूंगा (लेकिन मैं सीधे निजी तरीकों का परीक्षण नहीं करता हूं, लेकिन मुझे पता है कि वे काम करते हैं या मेरा सार्वजनिक परीक्षण विफल हो जाएगा )। अगर मुझे उस निजी पद्धति के किनारे के मामलों का परीक्षण करने की आवश्यकता है, तो मैं परीक्षणों का एक पूरा गुच्छा लिखूंगा जो उन्हें मेरे सार्वजनिक इंटरफ़ेस के माध्यम से हिट करेगा।यदि आप यह पता नहीं लगा सकते हैं कि किनारे के मामलों को कैसे मारा जाए, तो यह एक मजबूत संकेत है जिसे आपको अपने स्वयं के सार्वजनिक तरीकों से छोटे घटकों में परावर्तित करने की आवश्यकता है। यह एक संकेत है कि आप निजी कार्य बहुत अधिक कर रहे हैं, और कक्षा के दायरे के बाहर ।
इसके अलावा, कभी-कभी मुझे लगता है कि मैं एक ऐसा परीक्षण लिखता हूं जो इस समय चबाने के लिए बहुत बड़ा है, और इसलिए मुझे लगता है कि "एह मैं बाद में उस परीक्षा में वापस आऊंगा जब मेरे पास काम करने के लिए एक एपीआई अधिक होगा" (मैं 'इसे टिप्पणी करूँगा और अपने दिमाग के पीछे रखूँगा)। यह वह जगह है जहाँ मैंने जितने भी देवों से मुलाकात की है, वे तेदेपा के रूप में TDD का उपयोग करते हुए, अपनी निजी कार्यक्षमता के लिए परीक्षण लिखना शुरू करेंगे। वे कहते हैं, "ओह, ठीक है, मुझे कुछ अन्य परीक्षण की आवश्यकता है, लेकिन उस परीक्षण को लिखने के लिए, मुझे इन निजी तरीकों की आवश्यकता होगी। इसलिए, जब से मैं एक परीक्षण लिखने के बिना कोई उत्पादन कोड नहीं लिख सकता, मुझे परीक्षण लिखने की आवश्यकता है एक निजी विधि के लिए। " लेकिन वास्तव में उन्हें जो करने की आवश्यकता है, वह अपने वर्तमान वर्ग में निजी तरीकों का एक गुच्छा जोड़ने / परीक्षण करने के बजाय छोटे और पुन: प्रयोज्य घटकों में पुन: निर्माण कर रहा है।
ध्यान दें:
मैंने थोड़ी देर पहले GoogleTest का उपयोग करके निजी तरीकों के परीक्षण के बारे में इसी तरह के सवाल का जवाब दिया । मैंने ज्यादातर संशोधित किया है कि यहाँ अधिक भाषा अज्ञेय होने का जवाब है।
PS यहाँ माइकल पंखों द्वारा आइसबर्ग कक्षाओं और ग्रोपिंग टूल के बारे में प्रासंगिक व्याख्यान दिया गया है: https://www.youtube.com/watch?v=4cVZvoFGJTU