परीक्षण बनाम अपने आप को मत दोहराओ (DRY)


11

परीक्षण को इतना प्रोत्साहित करके अपने आप को क्यों दोहरा रहा है?

ऐसा लगता है कि परीक्षण मूल रूप से कोड के समान चीज़ को व्यक्त करते हैं, और इसलिए कोड का एक डुप्लिकेट (अवधारणा में, कार्यान्वयन नहीं) है। DRY के अंतिम लक्ष्य में सभी परीक्षण कोड का उन्मूलन शामिल नहीं होगा?

जवाबों:


25

मेरा मानना ​​है कि यह एक गलत धारणा है जिस तरह से मैं सोच सकता हूं।

उत्पादन-परीक्षण का परीक्षण करने वाला परीक्षण-कोड बिल्कुल समान नहीं है। मैं अजगर में प्रदर्शित करूँगा:

def multiply(a, b):
    """Multiply ``a`` by ``b``"""
    return a*b

फिर एक साधारण परीक्षा होगी:

def test_multiply():
    assert multiply(4, 5) == 20

दोनों कार्यों की एक समान परिभाषा है लेकिन दोनों बहुत अलग चीजें करते हैं। यहां कोई डुप्लिकेट कोड नहीं है। ;-)

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

def test_multiply_1_and_3():
    """Assert that a multiplication of 1 and 3 is 3."""
    assert multiply(1, 3) == 3

def test_multiply_1_and_7():
    """Assert that a multiplication of 1 and 7 is 7."""
    assert multiply(1, 7) == 7

def test_multiply_3_and_4():
    """Assert that a multiplication of 3 and 4 is 12."""
    assert multiply(3, 4) == 12

कोड की 1000+ प्रभावी लाइनों के लिए ऐसा करने की कल्पना करें। इसके बजाय आप प्रति 'फीचर' आधार पर परीक्षण करते हैं:

def test_multiply_positive():
    """Assert that positive numbers can be multiplied."""
    assert multiply(1, 3) == 3
    assert multiply(1, 7) == 7
    assert multiply(3, 4) == 12

def test_multiply_negative():
    """Assert that negative numbers can be multiplied."""
    assert multiply(1, -3) == -3
    assert multiply(-1, -7) == 7
    assert multiply(-3, 4) == -12

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

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


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

@ गुलाबी-हीरा-वर्ग मैं देखता हूं कि एक जोर के विफल होने के बाद नुनाइट परीक्षण बंद नहीं करता है (जो मुझे लगता है कि अजीब है)। उस विशिष्ट मामले में वास्तव में प्रति परीक्षण एक जोर देना बेहतर है। यदि एक इकाई-परीक्षण रूपरेखा विफल परीक्षण के बाद परीक्षण बंद कर देती है तो कई दावे बेहतर होते हैं।
siebz0r

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

मैंने सोचा था कि मैं इसे बढ़ाऊंगा क्योंकि प्रति परीक्षण में एक ही जोर है, जो मैंने .net दुनिया में पढ़ा है, "अच्छा अभ्यास" और कई दावे "व्यावहारिक उपयोग" है। यह पायथन डॉक्यूमेंटेशन में थोड़ा अलग है, जहां उदाहरण def test_shuffleदो दावे करता है।
रॉब चर्च

मैं सहमत हूं और असहमत हूं: डी यहां स्पष्ट रूप से दोहराव है: assert multiply(*, *) == *इसलिए आप एक assert_multiplyफ़ंक्शन को परिभाषित कर सकते हैं । वर्तमान परिदृश्य में यह पंक्ति गणना और पठनीयता से कोई फर्क नहीं पड़ता है, लेकिन अब परीक्षण से आप जटिल दावे, जुड़नार, स्थिरता उत्पन्न करने वाले कोड आदि का पुन: उपयोग कर सकते हैं ... मुझे नहीं पता कि यह सबसे अच्छा अभ्यास है, लेकिन मैं आमतौर पर करता हूं इस।
inf3rno

10

ऐसा लगता है कि परीक्षण मूल रूप से कोड के समान चीज़ को व्यक्त करते हैं, और इसलिए एक डुप्लिकेट है

नहीं, यह सच नहीं है।

आपके कार्यान्वयन से टेस्ट का एक अलग उद्देश्य है:

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

4

नहीं DRY किसी विशेष कार्य को करने के लिए सिर्फ एक बार कोड लिखने के बारे में है, परीक्षण इस बात की पुष्टि करते हैं कि कार्य सही ढंग से किया जा रहा है। यह कुछ हद तक एक वोटिंग एल्गोरिथ्म के समान है, जहां स्पष्ट रूप से एक ही कोड का उपयोग करना बेकार होगा।


2

DRY के अंतिम लक्ष्य में सभी परीक्षण कोड का उन्मूलन शामिल नहीं होगा?

नहीं, DRY का अंतिम लक्ष्य वास्तव में सभी उत्पादन कोड को खत्म करना होगा ।

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

यह वास्तव में क्या है जो मॉडल-संचालित वास्तुकला की तरह दृष्टिकोण को प्राप्त करने का दावा करता है - सत्य का एक मानव-डिजाइन स्रोत जिसमें से सब कुछ गणना द्वारा प्राप्त होता है।

मुझे नहीं लगता कि रिवर्स (सभी परीक्षणों से छुटकारा पाना) वांछनीय है क्योंकि:

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

1

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


1

चूंकि यूनिट-परीक्षण अनजाने में कठिन बदलाव करने के बारे में है, इसलिए यह कभी-कभी जानबूझकर बदलाव को भी कठिन बना सकता है। यह तथ्य वास्तव में DRY सिद्धांत से संबंधित है।

उदाहरण के लिए, यदि आपके पास एक फ़ंक्शन MyFunctionहै जिसे केवल एक स्थान पर उत्पादन कोड में कहा जाता है, और आप इसके लिए 20 यूनिट परीक्षण लिखते हैं, तो आप आसानी से अपने कोड में 21 स्थानों को समाप्त कर सकते हैं जहां उस फ़ंक्शन को कहा जाता है। अब, जब आपको MyFunction, या शब्दार्थ, या दोनों के हस्ताक्षर को बदलना होगा (क्योंकि कुछ आवश्यकताएं बदलती हैं), तो आपके पास केवल एक के बजाय बदलने के लिए 21 स्थान हैं। और कारण वास्तव में DRY सिद्धांत का उल्लंघन है: आपने MyFunction21 बार एक ही फ़ंक्शन कॉल को (कम से कम) दोहराया ।

इस तरह के एक मामले के लिए सही दृष्टिकोण DRY सिद्धांत को आपके परीक्षण कोड पर लागू कर रहा है: 20 इकाई परीक्षण लिखते समय, MyFunctionकुछ इकाई कार्यों (आदर्श रूप से सिर्फ एक) में आपके इकाई परीक्षणों में कॉलों को इनकैप्सुलेट करें , जो कि उपयोग किया जाता है 20 यूनिट परीक्षण। आदर्श रूप से, आप अपने कोड कॉलिंग में केवल दो स्थानों के साथ समाप्त होते हैं MyFunction: एक आपके उत्पादन कोड से, और एक आप यूनिट परीक्षणों से। इसलिए जब आपको MyFunctionबाद में हस्ताक्षर बदलने होंगे, तो आपके पास अपने परीक्षणों में बदलने के लिए कुछ ही स्थान होंगे।

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


0

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

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

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

यह सब कहा, आप शायद गलत परीक्षण कर रहे हैं यदि आपके परीक्षण वास्तविक कार्यक्रमों से अलग हैं। सुश्री का सुझाव होगा कि परीक्षणों में किस पर अधिक ध्यान दिया जाए और किस तरह कम किया जाए।

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


0

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


0

यूनिट परीक्षणों में परीक्षण के तहत कोड का दोहराव शामिल नहीं होना चाहिए, जैसा कि पहले ही नोट किया गया है।

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

तो व्यावहारिक हो। यदि आप रखरखाव से समझौता किए बिना सेटअप कोड को समेकित कर सकते हैं, तो हर तरह से। लेकिन अगर विकल्प सेटअप विधियों का एक जटिल और भंगुर सेट है, तो आपके परीक्षण के तरीकों में थोड़ा सा दोहराव ठीक है।

एक स्थानीय TDD / BDD प्रचारक इसे इस तरह कहते हैं:
"आपका उत्पादन कोड DRY होना चाहिए। लेकिन आपके परीक्षणों के लिए 'नम' होना ठीक है।"


0

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

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

परीक्षणों द्वारा पुनरावृत्ति भी हो सकती है। उदाहरण के लिए, आप जुड़नार, स्थिरता उत्पन्न करने वाले कोड, जटिल अभिक्रियाओं आदि का पुन: उपयोग कर सकते हैं ... मैं आमतौर पर ऐसा करता हूं, परीक्षणों में कीड़े को रोकने के लिए, लेकिन मैं आमतौर पर पहले परीक्षण करना भूल जाता हूं कि क्या एक परीक्षण वास्तव में विफल रहता है, और यह वास्तव में दिन को बर्बाद कर सकता है। , जब आप आधे घंटे के लिए कोड में बग की तलाश कर रहे हैं और परीक्षण गलत है ... xD

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.