मुझे कब मजाक करना चाहिए?


138

मैं नकली और नकली वस्तुओं की एक बुनियादी समझ है, लेकिन मैं नहीं कर रहा हूँ यकीन है कि मैं जब / जहां मजाक का उपयोग करने के बारे में लग रहा - विशेष रूप से के रूप में यह इस परिदृश्य पर लागू होगा यहाँ


मैं केवल आउट-ऑफ-प्रोसेस निर्भरता और उनमें से केवल उन लोगों का मजाक उड़ाने की सलाह देता हूं, जिनके साथ बातचीत बाहरी रूप से (एसएमटीपी सर्वर, संदेश बस, आदि) होती है। डेटाबेस का मजाक न करें, यह एक कार्यान्वयन विवरण है। इसके बारे में यहाँ और अधिक: enterprisecraftsmanship.com/posts/when-to-mock
व्लादिमीर

जवाबों:


122

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

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

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

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

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

टीएल; डीआर: आपकी यूनिट परीक्षण को छूने वाली हर निर्भरता का मजाक उड़ाती है।


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

10
यह उत्तर भी आशावादी है। यह बेहतर होगा यदि इसमें @ जनवरी की नकली वस्तुओं की कमियों को शामिल किया जाए।
जेफ एक्सलरोड

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

2
आपकी इकाई परीक्षण को छूने वाली हर निर्भरता का मजाक उड़ाती है। यह सब कुछ समझाता है।
तेमन शिपही

2
टीएल; डीआर: आपकी यूनिट परीक्षण को छूने वाली हर निर्भरता का मजाक उड़ाती है। - यह वास्तव में एक महान दृष्टिकोण नहीं है, मॉकिटो खुद कहता है - सब कुछ नकली मत करो। (
डाउनवोटेड

167

मॉक ऑब्जेक्ट तब उपयोगी होते हैं जब आप परीक्षण के तहत एक वर्ग और एक विशेष इंटरफ़ेस के बीच बातचीत का परीक्षण करना चाहते हैं ।

उदाहरण के लिए, हम उस विधि sendInvitations(MailServer mailServer)को MailServer.createMessage()एक बार कॉल करना चाहते हैं , और ठीक एक बार कॉल करना चाहते हैं MailServer.sendMessage(m), और MailServerइंटरफ़ेस पर कोई अन्य विधि नहीं कहते हैं । यह तब है जब हम नकली वस्तुओं का उपयोग कर सकते हैं।

नकली वस्तुओं के साथ, एक वास्तविक MailServerImpl, या एक परीक्षा पास करने के बजाय TestMailServer, हम MailServerइंटरफ़ेस का एक नकली कार्यान्वयन पारित कर सकते हैं । इससे पहले कि हम एक मॉक पास करें MailServer, हम इसे "ट्रेन" करते हैं, ताकि यह पता चले कि किस पद्धति से उम्मीद की जाती है और वापसी के लिए क्या मान वापस आते हैं। अंत में, नकली वस्तु का दावा है कि सभी अपेक्षित तरीकों को उम्मीद के मुताबिक कहा गया था।

यह सिद्धांत में अच्छा लगता है, लेकिन कुछ डाउनसाइड भी हैं।

मॉक कमियां

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

यहाँ छद्मकोश में एक उदाहरण दिया गया है। मान लीजिए कि हमने एक MySorterवर्ग बनाया है और हम उसका परीक्षण करना चाहते हैं:

// the correct way of testing
testSort() {
    testList = [1, 7, 3, 8, 2] 
    MySorter.sort(testList)

    assert testList equals [1, 2, 3, 7, 8]
}


// incorrect, testing implementation
testSort() {
    testList = [1, 7, 3, 8, 2] 
    MySorter.sort(testList)

    assert that compare(1, 2) was called once 
    assert that compare(1, 3) was not called 
    assert that compare(2, 3) was called once 
    ....
}

(इस उदाहरण में हम मानते हैं कि यह एक विशेष छँटाई एल्गोरिथ्म नहीं है, जैसे कि त्वरित छाँटना, जिसे हम परीक्षण करना चाहते हैं; उस स्थिति में, बाद वाला परीक्षण वास्तव में मान्य होगा।)

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

स्टब्स के रूप में मोक्स

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

मान लीजिए कि हमारे पास एक तरीका है sendInvitations(PdfFormatter pdfFormatter, MailServer mailServer)जिसे हम परीक्षण करना चाहते हैं। PdfFormatterवस्तु निमंत्रण बनाने के लिए इस्तेमाल किया जा सकता है। यहाँ परीक्षण है:

testInvitations() {
   // train as stub
   pdfFormatter = create mock of PdfFormatter
   let pdfFormatter.getCanvasWidth() returns 100
   let pdfFormatter.getCanvasHeight() returns 300
   let pdfFormatter.addText(x, y, text) returns true 
   let pdfFormatter.drawLine(line) does nothing

   // train as mock
   mailServer = create mock of MailServer
   expect mailServer.sendMail() called exactly once

   // do the test
   sendInvitations(pdfFormatter, mailServer)

   assert that all pdfFormatter expectations are met
   assert that all mailServer expectations are met
}

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

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

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

उसे कैसे ठीक करें? सरलता:

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

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

मॉक की कमियों के बारे में कुछ और जानकारी के लिए मॉक ऑब्जेक्ट भी देखें : शॉर्टकॉमिंग और यूसेज केस


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

1
एक नकली की पूरी बात यह नहीं है कि आपके परीक्षण सुसंगत हैं, कि आपको उन वस्तुओं पर मॉकिंग के बारे में चिंता करने की ज़रूरत नहीं है जिनके क्रियान्वयन में लगातार अन्य प्रोग्रामर द्वारा हर बार जब आप अपना परीक्षण चलाते हैं और आपको लगातार परीक्षण के परिणाम मिलते हैं?
पॉजिटिव

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

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

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

55

अंगूठे का नियम:

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


4

जब आप कोड की एक इकाई में निर्भरता रखते हैं, तो आपको एक वस्तु का मज़ाक उड़ाना चाहिए जिसे आप परखने की कोशिश कर रहे हैं कि "बस इतना" होना चाहिए।

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

विषय पर एक शानदार पॉडकास्ट यहां पाया जा सकता है


लिंक अब वर्तमान प्रकरण का मार्ग दिखाता है, न कि इच्छित प्रकरण का। क्या यह पॉडकास्ट इस hanselminutes.com/32/mock-objects है ?
सी पर्किन्स
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.