आप कुशलतापूर्वक अपने परीक्षणों को कैसे नया स्वरूप देते हैं?


15

एक अच्छी तरह से परीक्षण किए गए कोडबेस के कई लाभ हैं, लेकिन सिस्टम के कुछ पहलुओं के परीक्षण से कोडबेस में परिणाम होता है जो कुछ प्रकार के परिवर्तन के लिए प्रतिरोधी है।

एक उदाहरण विशिष्ट आउटपुट के लिए परीक्षण कर रहा है - जैसे, टेक्स्ट या HTML। परीक्षण अक्सर (भोलेपन से) कुछ इनपुट मापदंडों के लिए आउटपुट के रूप में पाठ के एक विशेष ब्लॉक की उम्मीद करने के लिए लिखे जाते हैं, या एक ब्लॉक में विशिष्ट खंडों की खोज करने के लिए।

कोड के व्यवहार को बदलना, नई आवश्यकताओं को पूरा करने के लिए या क्योंकि प्रयोज्य परीक्षण के परिणामस्वरूप इंटरफ़ेस में परिवर्तन हुआ है, परीक्षणों को भी बदलने की आवश्यकता है - शायद यह भी परीक्षण कि कोड के लिए विशेष रूप से यूनिट परीक्षण नहीं किए जा रहे हैं।

  • आप इन परीक्षणों को खोजने और पुनर्लेखन के कार्य का प्रबंधन कैसे करते हैं? क्या होगा यदि आप उन्हें केवल "रन 'नहीं कर सकते हैं और फ्रेमवर्क उन्हें छाँटने दें"?

  • आदतन नाजुक परीक्षणों में अन्य प्रकार के कोड-अंडर-टेस्ट परिणाम क्या हैं?



4
उस सवाल को गलती से रिफैक्टिंग के बारे में पूछा गया था - यूनिट टेस्ट को रिफलेक्टिंग के तहत अपरिवर्तित किया जाना चाहिए।
एलेक्स फीमैन

जवाबों:


10

मुझे पता है कि TDD लोग इस जवाब से नफरत करेंगे, लेकिन मेरे लिए इसका एक बड़ा हिस्सा ध्यान से चुनना है कि किसी चीज़ का परीक्षण कहाँ करना है।

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

इसके विपरीत यदि आप जो परिवर्तन करने का प्रयास कर रहे हैं, वह उजागर हो या उन परीक्षणों में से हर एक का फिर से उपयोग किया जाए जिन्हें आप बदलने जा रहे हैं, तो इसका प्रमाण है कि आप कहीं और तोड़ रहे हैं।

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

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


मुझे लगता है कि आपको "मॉड्यूल के बाहर" लिखना है, न कि "ऐप के बाहर"।
सैमब सिप

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

मैंने इस दृष्टिकोण को TDD के साथ बहुत संगत पाया है। मैं अंत उपयोगकर्ता के पास एप्लिकेशन की ऊपरी परतों में शुरू करना पसंद करता हूं ताकि मैं निचली परतों को यह जानकर डिजाइन कर सकूं कि ऊपरी परतों को निचली परतों का उपयोग करने की आवश्यकता कैसे है। अनिवार्य रूप से ऊपर नीचे निर्माण आपको एक परत और दूसरी के बीच इंटरफ़ेस को अधिक सटीक रूप से डिज़ाइन करने की अनुमति देता है।
ग्रेग बरगार्ड

4

मैंने अभी-अभी अपने SIP स्टैक का एक बड़ा ओवरहाल पूरा किया, पूरे टीसीपी ट्रांसपोर्ट को फिर से लिखा। (यह ज्यादातर रिफ्लेक्टरिंग के सापेक्ष एक भव्य रिफ्लेक्टर था, बल्कि भव्य पैमाने पर।)

संक्षेप में, वहाँ एक TIdSipTcpTransport, TIdSipTransport का उपवर्ग है। सभी TIdSipTransports एक साझा परीक्षण सूट साझा करते हैं। TIdSipTcpTransport में आंतरिक कई कक्षाएं थीं - एक नक्शा जिसमें कनेक्शन / आरंभ करने-संदेश जोड़े, थ्रेडेड टीसीपी क्लाइंट, थ्रेडेड टीसीपी सर्वर और इतने पर थ्रेडेड थे।

यहाँ मैंने क्या किया है:

  • जिन वर्गों को मैं बदलने जा रहा था, उन्हें हटा दिया गया।
  • उन वर्गों के लिए परीक्षण सूट को नष्ट कर दिया।
  • TIdSipTcpTransport के लिए विशिष्ट परीक्षण सूट छोड़ दिया (और अभी भी सभी TIdSipTransports के लिए सामान्य परीक्षण सूट था)।
  • TIDSipTransport / TIdSipTcpTransport परीक्षण चला, यह सुनिश्चित करने के लिए कि वे सभी विफल रहे हैं।
  • सभी पर टिप्पणी की गई लेकिन एक TIdSipTransport / TIdSipTcpTransport परीक्षण।
  • अगर मुझे एक कक्षा जोड़ने की आवश्यकता है, तो मैं इसे पर्याप्त कार्यक्षमता बनाने के लिए परीक्षण लिखूंगा, जो कि एकमात्र अपूर्ण परीक्षा उत्तीर्ण हो।
  • बल्कि, कुल्ला, दोहराएं।

इस प्रकार मुझे पता था कि मुझे अभी भी क्या करने की ज़रूरत थी, टिप्पणी-किए गए परीक्षणों (*) के रूप में, और यह जानता था कि नया कोड अपेक्षित रूप से काम कर रहा था, नए परीक्षणों के लिए धन्यवाद।

(*) वास्तव में, आपको उन्हें टिप्पणी करने की आवश्यकता नहीं है। बस उन्हें मत चलाना; 100 असफल परीक्षण बहुत उत्साहजनक नहीं है। इसके अलावा, मेरे विशेष सेटअप में कम परीक्षणों का संकलन करने का मतलब है तेज परीक्षण-लेखन-रिफ्लेक्टर लूप।


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

3

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

आप ऐसा कर सकते हैं:

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

इसी तरह की बातें SQL के साथ होती हैं। यदि आप वास्तविक एसक्यूएल पर जोर देते हैं तो आपकी कक्षाएं आपको परेशानी में डालने का प्रयास करती हैं। आप वास्तव में परिणामों को मुखर करना चाहते हैं। इसलिए मैं अपने यूनिट परीक्षणों के दौरान SQLITE मेमोरी डेटाबेस का उपयोग यह सुनिश्चित करने के लिए करता हूं कि मेरा एसक्यूएल वास्तव में वही करता है जो इसका माना जाता है।


यह संरचनात्मक HTML का उपयोग करने में भी मदद कर सकता है।
सैमब सिप

@SamB निश्चित रूप से मदद करेगा, लेकिन मुझे नहीं लगता कि यह पूरी तरह से समस्या को हल करेगा
विंस्टन एवर्ट

बिल्कुल नहीं, कुछ भी नहीं कर सकते हैं :-)
सैमबी

-1

पहले एक नया एपीआई बनाएं, वही करें जो आप चाहते हैं कि आपका नया एपीआई व्यवहार हो। यदि ऐसा होता है कि इस नए एपीआई का एक नाम OLDER API है, तो मैं नए API नाम के लिए _NEW नाम रखता हूं।

int DoSomethingInterestingAPI ();

हो जाता है:

int DoSomethingInterestingAPI_NEW (int take_more_arguments); int DoSomethingInterestingAPI_OLD (); int DoSomethingInterestingAPI () {DoSomethingInterestingAPI_NEW (जो भी_बदलता है_मीमिक्स_the_old_API); ठीक है - इस स्तर पर - आपके सभी प्रतिगमन परीक्षण डिल पास - DoSomethingInterestingAPI () नाम का उपयोग करते हुए।

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

अगला, [[पदावनत ()]] के रूप में DoSomethingInterestingAPIOLD () को चिह्नित करें। जब तक आप चाहें तब तक अपग्रेड किए गए एपीआई के आसपास रहें (जब तक कि आपने सुरक्षित रूप से सभी कोड को अपडेट नहीं किया है जो उस पर निर्भर हो सकता है)।

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

व्यवहार में इस दृष्टिकोण का एक अच्छा (कठिन) उदाहरण है। मेरे पास BitSubstring () फ़ंक्शन था - जहां मैंने तीसरे पैरामीटर होने के दृष्टिकोण का उपयोग किया था, जहां विकल्प में बिट्स का COUNT होना चाहिए। सी ++ में अन्य एपीआई और पैटर्न के अनुरूप होने के लिए, मैं फ़ंक्शन के तर्क के रूप में शुरू / समाप्त करना चाहता था।

https://github.com/SophistSolutions/Stroika/commit/003dd8707405c43e735ca71116c773b108c217c0

मैंने नए API के साथ एक फ़ंक्शन BitSubstring_NEW बनाया, और इसका उपयोग करने के लिए अपने सभी कोड को अपडेट कर दिया (BitSubString के लिए कोई और कॉल नहीं छोड़ रहा)। लेकिन मैंने कई रिलीज़ (महीनों) के लिए कार्यान्वयन में छोड़ दिया - और इसे पदावनत किया - ताकि हर कोई BitSubString_NEW (और उस समय के लिए एक गिनती से तर्क को शुरू / समाप्ति शैली में बदल सके)।

तब - जब वह संक्रमण पूरा हो गया था, मैंने BitSubString () और BitSubString_NEW-> BitSubString () (और नाम BitSubString_NEW नाम हटा दिया) को हटाते हुए एक और कमिट किया।


कभी भी उन प्रत्ययों को शामिल न करें जो कोई अर्थ नहीं रखते हैं, या नामों के लिए आत्म-वंचित हैं। हमेशा सार्थक नाम देने का प्रयास करते हैं।
बसिलेव्स

तुम पूरी तरह से चूक गए। पहला - ये प्रत्यय नहीं हैं कि "कोई अर्थ न रखें"। वे इस अर्थ को ले जाते हैं कि एपीआई पुराने से नए के रूप में परिवर्तित हो रहा है। वास्तव में, यह प्रश्न का पूरा बिंदु है, जिसका मैं उत्तर दे रहा था, और उत्तर का पूरा बिंदु। नाम जो पूरी तरह से संवाद करते हैं जो OLD API है, जो कि NEW API है, और जो संक्रमण पूर्ण होने के बाद अंततः API का लक्ष्य नाम है। और - _OLD / _NEW प्रत्यय अस्थायी हैं - केवल एपीआई परिवर्तन के दौरान परिवर्तन।
लुईस प्रिंगल

तीन साल बाद एपीआई के NEW_NEW_3 संस्करण के साथ शुभकामनाएँ।
बसिलेव्स
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.