क्या परीक्षण किए गए वर्ग बुरे अभ्यास पर जासूसी कर रहे हैं?


14

मैं एक ऐसे प्रोजेक्ट पर काम कर रहा हूं, जहां क्लास इंटरनल कॉल्स सामान्य हैं, लेकिन परिणाम कई बार सरल होते हैं। उदाहरण ( वास्तविक कोड नहीं ):

public boolean findError(Set<Thing1> set1, Set<Thing2> set2) {
  if (!checkFirstCondition(set1, set2)) {
    return false;
  }
  if (!checkSecondCondition(set1, set2)) {
    return false;
  }
  return true;
}

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

एक काम कर समाधान; हालाँकि, परीक्षण की गई वस्तु को एक जासूस बनाने के लिए और आंतरिक कार्यों के लिए कॉल को मॉक करने के लिए है।

systemUnderTest = Mockito.spy(systemUnderTest);
doReturn(true).when(systemUnderTest).checkFirstCondition(....);

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

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

जवाबों:


15

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

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


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

@ बॉल्रोग जब आपको बहुत सारे मॉकिंग करने की आवश्यकता होती है, तो ऐसा लगता है जैसे आपकी कक्षाओं के बीच बहुत अधिक निर्भरता है। क्या आपने उनके बीच युग्मन को कम करने की कोशिश की?
फिलिप

@ बॉल अगर आप उस स्थिति में हैं, तो क्लास डिजाइन को दोष देना है।
13

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

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

4

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


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

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

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

4

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

आंतरिक कॉल एक कार्यान्वयन विवरण हैं और केवल प्रदर्शन को मापने में रुचि होनी चाहिए । जो आमतौर पर यूनिट परीक्षणों का काम नहीं है।


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

1
यदि bसार्वजनिक इंटरफ़ेस का हिस्सा है, तो इसे वैसे भी परीक्षण किया जाना चाहिए। यदि ऐसा नहीं है, तो इसका परीक्षण करने की आवश्यकता नहीं है। यदि आपने इसे केवल इसलिए सार्वजनिक किया कि आप इसका परीक्षण करना चाहते हैं, तो आपने गलत किया।
इसकी

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

2

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

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

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


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

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

"मैं जटिल फ़ंक्शन का परीक्षण करना चाहता हूं .. उप कार्यों के लिए समझदार पैरामीटर प्रदान किए बिना" - मुझे वहां नहीं मिलता है जो आपके लिए मतलब है। कौन से उप कार्य? क्या आप 'जटिल फ़ंक्शन' द्वारा उपयोग किए जा रहे आंतरिक कार्यों के बारे में बात कर रहे हैं?
बीटी

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

@ बॉल "तार्किक रूप से जटिल" - यदि इसका जटिल, आपको जटिल परीक्षणों की आवश्यकता है। उसके आसपास कोई रास्ता नहीं है। जासूस सिर्फ आपके लिए इसे और अधिक जटिल बनाने जा रहे हैं। आपको समझने योग्य उप-कार्यों का निर्माण करना चाहिए जिन्हें आप किसी अन्य फ़ंक्शन के अंदर अपने विशेष व्यवहार का परीक्षण करने के लिए जासूसों का उपयोग करने के बजाय अपने दम पर परीक्षण कर सकते हैं।
बीटी
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.