आपको प्रश्न के तहत कक्षा को तोड़ देना चाहिए।
प्रत्येक वर्ग को कुछ सरल कार्य करने चाहिए। यदि आप परीक्षण करने के लिए बहुत जटिल हैं, तो कक्षा जो कार्य करता है वह बहुत बड़ा है।
इस डिजाइन की नासमझी को अनदेखा करना:
class NewYork
{
decimal GetRevenue();
decimal GetExpenses();
decimal GetProfit();
}
class Miami
{
decimal GetRevenue();
decimal GetExpenses();
decimal GetProfit();
}
class MyProfit
{
MyProfit(NewYork new_york, Miami miami);
boolean bothProfitable();
}
अद्यतन करें
एक कक्षा में स्टबिंग विधियों के साथ समस्या यह है कि आप इनकैप्सुलेशन का उल्लंघन कर रहे हैं। आपका परीक्षण यह देखने के लिए जाँचना चाहिए कि वस्तु का बाहरी व्यवहार विनिर्देशों से मेल खाता है या नहीं। वस्तु के अंदर जो कुछ भी होता है, उसका कोई भी व्यवसाय नहीं है।
तथ्य यह है कि FullName FirstName और LastName का उपयोग करता है एक कार्यान्वयन विवरण है। वर्ग के बाहर कुछ भी परवाह नहीं करनी चाहिए कि यह सच है। ऑब्जेक्ट का परीक्षण करने के लिए सार्वजनिक तरीकों का मज़ाक उड़ाते हुए, आप उस चीज़ के बारे में एक धारणा बना रहे हैं।
भविष्य में कुछ बिंदु पर, यह धारणा सही हो सकती है। शायद नाम तर्क के सभी एक नाम वस्तु के लिए स्थानांतरित कर दिया जाएगा जिसे व्यक्ति बस कहता है। शायद FullName सीधे सदस्य चरों को पहले_नाम और last_name तक पहुँचाएगा, फिर FirstName और LastName को कॉल करेगा।
दूसरा सवाल यह है कि आपको ऐसा करने की आवश्यकता क्यों महसूस होती है। आखिर आपके व्यक्ति वर्ग का परीक्षण कुछ इस तरह किया जा सकता है:
Person person = new Person("John", "Doe");
Test.AssertEquals(person.FullName(), "John Doe");
आपको इस उदाहरण के लिए किसी चीज की आवश्यकता महसूस नहीं करनी चाहिए। यदि आप करते हैं तो आप हठी-खुश और अच्छी तरह से हैं ... इसे रोकें! वहाँ तरीकों का मज़ाक उड़ाने से कोई फ़ायदा नहीं है क्योंकि आप उन पर नियंत्रण कर चुके हैं, वैसे भी उनमें क्या है।
केवल ऐसा मामला जहां यह प्रतीत होता है कि FullName द्वारा उपयोग की जाने वाली विधियों के लिए यह गलत है कि क्या FirstName () और LastName () गैर-तुच्छ कार्य थे। हो सकता है कि आप उन यादृच्छिक नाम जनरेटर, या FirstName और LastName में से किसी एक को उत्तर के लिए डेटाबेस या कुछ लिख रहे हों। लेकिन अगर ऐसा हो रहा है तो यह सुझाव देता है कि वस्तु कुछ ऐसा कर रही है जो व्यक्ति वर्ग में नहीं है।
इसे दूसरे तरीके से डालते हुए, तरीकों का मज़ाक उड़ाते हुए वस्तु को ले जा रहे हैं और दो टुकड़ों में तोड़ रहे हैं। एक टुकड़े का मज़ाक उड़ाया जा रहा है जबकि दूसरे टुकड़े का परीक्षण किया जा रहा है। आप जो कर रहे हैं वह अनिवार्य रूप से वस्तु का एक तदर्थ है। अगर ऐसा है, तो पहले से ही वस्तु को तोड़ दें।
यदि आपकी कक्षा सरल है, तो आपको परीक्षण के दौरान इसके टुकड़ों का मजाक उड़ाने की आवश्यकता महसूस नहीं होनी चाहिए। यदि आपकी कक्षा इतनी जटिल है कि आपको नकली की आवश्यकता महसूस होती है, तो आपको कक्षा को सरल टुकड़ों में तोड़ देना चाहिए।
अद्यतन रखें
जिस तरह से मैं इसे देखता हूं, एक वस्तु में बाहरी और आंतरिक व्यवहार होता है। बाहरी व्यवहार में अन्य वस्तुओं आदि में रिटर्न वैल्यूज शामिल हैं। जाहिर है, उस श्रेणी की किसी भी चीज का परीक्षण किया जाना चाहिए। (अन्यथा आप क्या परीक्षण करेंगे?) लेकिन आंतरिक व्यवहार का वास्तव में परीक्षण नहीं किया जाना चाहिए।
अब आंतरिक व्यवहार का परीक्षण किया जाता है, क्योंकि यह बाहरी व्यवहार का परिणाम है। लेकिन मैं सीधे आंतरिक व्यवहार पर परीक्षण नहीं लिखता, केवल बाहरी व्यवहार के माध्यम से अप्रत्यक्ष रूप से।
अगर मैं कुछ परीक्षण करना चाहता हूं, तो मुझे लगता है कि इसे स्थानांतरित करना चाहिए ताकि यह बाहरी व्यवहार बन जाए। इसीलिए मुझे लगता है कि अगर आप किसी चीज का मजाक बनाना चाहते हैं, तो आपको ऑब्जेक्ट को विभाजित करना चाहिए ताकि आप जिस चीज का मजाक बनाना चाहते हैं, वह अब वस्तुओं के बाहरी व्यवहार में हो।
लेकिन, क्या फर्क पड़ता है? यदि FirstName () और LastName () किसी अन्य ऑब्जेक्ट के सदस्य हैं तो क्या यह वास्तव में FullName () का मुद्दा बदल देता है? अगर हम यह तय करते हैं कि फर्स्टनाम और मेज़नाम का मजाक उड़ाना वास्तव में उनके लिए एक और वस्तु पर होना है?
मुझे लगता है कि यदि आप अपने मजाकिया दृष्टिकोण का उपयोग करते हैं, तो आप ऑब्जेक्ट में सीम बनाते हैं। आपके पास FirstName () और LastName () जैसे फ़ंक्शंस हैं जो सीधे बाहरी डेटा स्रोत के साथ संवाद करते हैं। आपके पास FullName () भी नहीं है। लेकिन चूंकि वे सभी एक ही वर्ग में हैं जो स्पष्ट नहीं है। कुछ टुकड़ों को सीधे डेटा स्रोत तक पहुंचने की अनुमति नहीं है और अन्य हैं। यदि आपका कोड उन दो समूहों को तोड़ देगा तो आपका कोड स्पष्ट हो जाएगा।
संपादित करें
चलो एक कदम पीछे लेते हैं और पूछते हैं: जब हम परीक्षण करते हैं तो हम वस्तुओं का मजाक क्यों उड़ाते हैं?
- परीक्षण लगातार चलाएं (चीजों को एक्सेस करने से बचें जो रन से रन में बदलते हैं)
- महंगे संसाधनों तक पहुँचने से बचें (तृतीय पक्ष सेवाओं को हिट न करें, आदि)
- परीक्षण के तहत प्रणाली को सरल बनाएं
- सभी संभावित परिदृश्यों का परीक्षण करना आसान बनाएं (जैसे कि असफलता जैसी चीजें, आदि)
- कोड के अन्य टुकड़ों के विवरण के आधार पर बचें ताकि कोड के उन अन्य टुकड़ों में परिवर्तन इस परीक्षा को नहीं तोड़ेंगे।
अब, मुझे लगता है कि 1-4 कारण इस परिदृश्य पर लागू नहीं होते हैं। फुलनाम का परीक्षण करते समय बाहरी स्रोत का मजाक उड़ाना उन सभी कारणों का ध्यान रखता है। एकमात्र ऐसा टुकड़ा नहीं है जो सादगी भरा हो, लेकिन ऐसा लगता है कि वस्तु काफी सरल है और यह चिंता का विषय नहीं है।
मुझे लगता है कि आपकी चिंता कारण संख्या 5 है। चिंता का विषय यह है कि भविष्य में फर्स्टनाम और लास्टनाम के कार्यान्वयन में बदलाव से परीक्षण टूट जाएगा। भविष्य में FirstName और LastName एक अलग स्थान या स्रोत से नाम प्राप्त कर सकते हैं। लेकिन FullName शायद हमेशा रहेगा FirstName() + " " + LastName()
। इसलिए आप FirstName और LastName का मजाक उड़ाते हुए FullName का परीक्षण करना चाहते हैं।
आपके पास तब क्या है, व्यक्ति वस्तु का कुछ सबसेट है जो कि तब दूसरों को बदलने की अधिक संभावना है। बाकी ऑब्जेक्ट इस सबसेट का उपयोग करता है। वह सबसेट वर्तमान में एक स्रोत का उपयोग करके अपना डेटा प्राप्त करता है, लेकिन बाद की तारीख में उस डेटा को पूरी तरह से अलग कर सकता है। लेकिन मेरे लिए ऐसा लगता है कि सबसेट एक अलग वस्तु है जो बाहर निकलने की कोशिश कर रही है।
यह मुझे प्रतीत होता है कि यदि आप ऑब्जेक्ट की विधि का मजाक उड़ाते हैं तो आप ऑब्जेक्ट को विभाजित कर रहे हैं। लेकिन आप ऐसा तदर्थ तरीके से कर रहे हैं। आपका कोड यह स्पष्ट नहीं करता है कि आपकी व्यक्तिगत वस्तु के अंदर दो अलग-अलग टुकड़े हैं। तो बस उस ऑब्जेक्ट को अपने वास्तविक कोड में विभाजित करें, ताकि यह आपके कोड को पढ़ने से स्पष्ट हो कि क्या चल रहा है। ऑब्जेक्ट का वास्तविक विभाजन चुनें जो समझ में आता है और प्रत्येक परीक्षण के लिए ऑब्जेक्ट को अलग-अलग विभाजित करने का प्रयास न करें।
मुझे संदेह है कि आपको अपनी वस्तु को विभाजित करने में आपत्ति हो सकती है, लेकिन क्यों?
संपादित करें
मैं गलत था।
आपको अलग-अलग तरीकों का इस्तेमाल करके वस्तुओं को विभाजित करना चाहिए, फिर अलग-अलग तरीकों का इस्तेमाल करके तदर्थ विभाजन करना चाहिए। हालाँकि, मैं वस्तुओं के विभाजन की एक विधि पर अत्यधिक ध्यान केंद्रित कर रहा था। हालाँकि, OO किसी ऑब्जेक्ट को विभाजित करने के कई तरीके प्रदान करता है।
मैं क्या प्रस्ताव दूंगा:
class PersonBase
{
abstract sring FirstName();
abstract string LastName();
string FullName()
{
return FirstName() + " " + LastName();
}
}
class Person extends PersonBase
{
string FirstName();
string LastName();
}
class FakePerson extends PersonBase
{
void setFirstName(string);
void setLastName(string);
string getFirstName();
string getLastName();
}
हो सकता है कि आप सभी के साथ ऐसा कर रहे थे। लेकिन मुझे नहीं लगता कि इस पद्धति में मेरे द्वारा मॉकिंग विधियों के साथ देखी गई समस्याएं होंगी क्योंकि हमने स्पष्ट रूप से समझा है कि प्रत्येक विधि किस तरफ है। और इनहेरिटेंस का उपयोग करके, हम उस अजीबता से बचते हैं जो एक अतिरिक्त आवरण वस्तु का उपयोग करने पर उत्पन्न होती है।
यह कुछ जटिलता का परिचय देता है, और केवल कुछ उपयोगिता कार्यों के लिए जो कि मैं अंतर्निहित 3 पार्टी स्रोत का मज़ाक उड़ाकर शायद उनका परीक्षण करूँगा। निश्चित रूप से, उन्हें टूटने का खतरा बढ़ गया है, लेकिन इसके पुन: संचालन के लायक नहीं है। यदि आपके पास एक जटिल पर्याप्त वस्तु है जिसे आपको इसे विभाजित करने की आवश्यकता है, तो मुझे लगता है कि ऐसा कुछ एक अच्छा विचार है।