मैं नकली और नकली वस्तुओं की एक बुनियादी समझ है, लेकिन मैं नहीं कर रहा हूँ यकीन है कि मैं जब / जहां मजाक का उपयोग करने के बारे में लग रहा - विशेष रूप से के रूप में यह इस परिदृश्य पर लागू होगा यहाँ ।
मैं नकली और नकली वस्तुओं की एक बुनियादी समझ है, लेकिन मैं नहीं कर रहा हूँ यकीन है कि मैं जब / जहां मजाक का उपयोग करने के बारे में लग रहा - विशेष रूप से के रूप में यह इस परिदृश्य पर लागू होगा यहाँ ।
जवाबों:
एक यूनिट टेस्ट को एक एकल विधि के माध्यम से एक एकल कोडपैथ का परीक्षण करना चाहिए। जब किसी विधि का निष्पादन उस विधि के बाहर, किसी अन्य ऑब्जेक्ट में, और फिर से वापस होता है, तो आपके पास एक निर्भरता होती है।
जब आप वास्तविक निर्भरता के साथ उस कोड पथ का परीक्षण करते हैं, तो आप इकाई परीक्षण नहीं होते हैं; आप एकीकरण परीक्षण कर रहे हैं। हालांकि यह अच्छा और आवश्यक है, यह इकाई परीक्षण नहीं है।
यदि आपकी निर्भरता छोटी है, तो आपका परीक्षण इस तरह से प्रभावित हो सकता है कि वह झूठी सकारात्मक वापसी कर सके। उदाहरण के लिए, आप निर्भरता को एक अप्रत्याशित अशक्तता मान सकते हैं, और निर्भरता शून्य पर नहीं फेंक सकती है क्योंकि यह करने के लिए प्रलेखित है। आपका परीक्षण एक अशक्त तर्क अपवाद का सामना नहीं करता है जैसा कि उसके पास होना चाहिए, और परीक्षण पास हो जाता है।
इसके अलावा, आप इसकी कड़ी मेहनत कर सकते हैं, अगर असंभव नहीं है, तो भरोसेमंद रूप से परीक्षण के दौरान जो आप चाहते हैं उसे वापस करने के लिए निर्भर वस्तु प्राप्त करें। इसमें परीक्षणों के भीतर अपेक्षित अपवादों को फेंकना भी शामिल है।
एक नकली कि निर्भरता बदल देता है। आप आश्रित वस्तु के लिए कॉल पर अपेक्षाएं निर्धारित करते हैं, सटीक रिटर्न मान सेट करें जो आपको आपके द्वारा वांछित परीक्षण करने के लिए देना चाहिए, और / या क्या अपवादों को फेंकना है ताकि आप अपने अपवाद हैंडलिंग कोड का परीक्षण कर सकें। इस तरह आप आसानी से प्रश्न में इकाई का परीक्षण कर सकते हैं।
टीएल; डीआर: आपकी यूनिट परीक्षण को छूने वाली हर निर्भरता का मजाक उड़ाती है।
मॉक ऑब्जेक्ट तब उपयोगी होते हैं जब आप परीक्षण के तहत एक वर्ग और एक विशेष इंटरफ़ेस के बीच बातचीत का परीक्षण करना चाहते हैं ।
उदाहरण के लिए, हम उस विधि 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
वह बनाएं जो कुछ नहीं करता। इस तरह से आप इसे सभी परीक्षणों के लिए एक बार बदल सकते हैं और आपके परीक्षणों को लंबे समय तक सेटअप के साथ बंद नहीं किया जाता है जहां आप अपने स्टब्स को प्रशिक्षित करते हैं।सभी में, नकली वस्तुओं का उपयोग होता है, लेकिन जब सावधानी से उपयोग नहीं किया जाता है, तो वे अक्सर खराब प्रथाओं को प्रोत्साहित करते हैं, कार्यान्वयन विवरणों का परीक्षण करते हैं, रिफैक्टिंग में बाधा उत्पन्न करते हैं और पढ़ने के लिए कठिन और परीक्षण बनाए रखने के लिए मुश्किल पैदा करते हैं ।
मॉक की कमियों के बारे में कुछ और जानकारी के लिए मॉक ऑब्जेक्ट भी देखें : शॉर्टकॉमिंग और यूसेज केस ।
अंगूठे का नियम:
यदि आप जिस फ़ंक्शन का परीक्षण कर रहे हैं, एक पैरामीटर के रूप में एक जटिल ऑब्जेक्ट की आवश्यकता होती है, और यह इस ऑब्जेक्ट को तुरंत हटाने के लिए एक दर्द होगा (यदि, उदाहरण के लिए यह एक टीसीपी कनेक्शन स्थापित करने की कोशिश करता है), एक मॉक का उपयोग करें।
जब आप कोड की एक इकाई में निर्भरता रखते हैं, तो आपको एक वस्तु का मज़ाक उड़ाना चाहिए जिसे आप परखने की कोशिश कर रहे हैं कि "बस इतना" होना चाहिए।
उदाहरण के लिए, जब आप कोड की अपनी इकाई में कुछ तर्क का परीक्षण करने की कोशिश कर रहे हैं, लेकिन आपको किसी अन्य वस्तु से कुछ प्राप्त करने की आवश्यकता है और इस निर्भरता से जो लौटा है, वह उस चीज को प्रभावित कर सकता है जिसे आप उस वस्तु का परीक्षण करने की कोशिश कर रहे हैं।