क्या यूनिट परीक्षणों के लिए कोड दोहराना ठीक है?


11

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

यहाँ एक उदाहरण है:

    [TestMethod]
    public void MergeSortAssertArrayIsSorted()
    {
        int[] a = new int[1000];
        Random rand = new Random(DateTime.Now.Millisecond);
        for(int i = 0; i < a.Length; i++)
        {
            a[i] = rand.Next(Int16.MaxValue);
        }
        int[] b = new int[1000];
        a.CopyTo(b, 0);
        List<int> temp = b.ToList();
        temp.Sort();
        b = temp.ToArray();

        MergeSort merge = new MergeSort();
        merge.mergeSort(a, 0, a.Length - 1);
        CollectionAssert.AreEqual(a, b);
    }
    [TestMethod]
    public void InsertionSortAssertArrayIsSorted()
    {
        int[] a = new int[1000];
        Random rand = new Random(DateTime.Now.Millisecond);
        for (int i = 0; i < a.Length; i++)
        {
            a[i] = rand.Next(Int16.MaxValue);
        }
        int[] b = new int[1000];
        a.CopyTo(b, 0);
        List<int> temp = b.ToList();
        temp.Sort();
        b = temp.ToArray();

        InsertionSort merge = new InsertionSort();
        merge.insertionSort(a);
        CollectionAssert.AreEqual(a, b); 
    }

जवाबों:


21

टेस्ट कोड अभी भी कोड है और इसे भी बनाए रखने की आवश्यकता है।

यदि आपको कॉपी किए गए लॉजिक को बदलने की आवश्यकता है, तो आपको उसे हर उस जगह पर करने की आवश्यकता है, जिसे आपने सामान्य रूप से कॉपी किया है।

DRY अभी भी लागू होता है।

क्या मुझे फिर से परीक्षण करने के लिए एक और परीक्षण लिखने की आवश्यकता नहीं होगी?

क्या तुम? और आप कैसे जानते हैं कि आपके पास वर्तमान में परीक्षण सही हैं?

आप परीक्षण चलाकर रीफैक्टरिंग का परीक्षण करते हैं। इन सबका परिणाम समान होना चाहिए।


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

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

2
मैंने पहले कभी DAMP नहीं सुना, लेकिन मुझे वह विवरण पसंद है।
जोकिम सॉयर

@ Jörg W Mittag: आप अभी भी परीक्षण के साथ DRY और DAMP हो सकते हैं। मैं आमतौर पर परीक्षण के अलग-अलग ARRANGE-ACT-ASSERT (या GIVEN-WHEN-THEN) भागों को परीक्षण फ़िक्चर में सहायक विधियों में बदल देता हूं यदि मुझे पता है कि परीक्षण का कुछ भाग दोहराता है। उनके पास आमतौर पर डीएएमपी नाम होते हैं, जैसे कि givenThereAreProductsSet(amount)और यहां तक ​​कि सरल भी actWith(param)। मैं इसे धाराप्रवाह एपीआई वार (जैसे givenThereAre(2).products()) एक बार करने में कामयाब रहा , लेकिन मैं जल्दी से रुक गया क्योंकि यह एक ओवरकिल की तरह लगा।
स्पोइक

11

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

आपके द्वारा पोस्ट किए गए दो कार्यों में, forलूप की शुरुआत में एक स्थान अंतर को छोड़कर निम्नलिखित पंक्तियां बिल्कुल समान हैं :

        int[] a = new int[1000];
        Random rand = new Random(DateTime.Now.Millisecond);
        for (int i = 0; i < a.Length; i++)
        {
            a[i] = rand.Next(Int16.MaxValue);
        }
        int[] b = new int[1000];
        a.CopyTo(b, 0);
        List<int> temp = b.ToList();
        temp.Sort();
        b = temp.ToArray();

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


4

नहीं, यह ठीक नहीं है। आपको इसके बजाय TestDataBuilder का उपयोग करना चाहिए । आपको अपने परीक्षणों की पठनीयता का भी ध्यान रखना चाहिए: a। 1000? बी? यदि कल को आपके द्वारा परीक्षण किए जा रहे कार्यान्वयन पर काम करना है, तो तर्क में प्रवेश करने के लिए परीक्षण एक शानदार तरीका है: अपने परीक्षण अपने साथी प्रोग्रामर के लिए लिखें, न कि कंपाइलर के लिए :)

यहाँ आपके परीक्षण के कार्यान्वयन हैं, "संशोधित":

/**
* Data your tests will exercice on
*/
public class MyTestData(){
    final int [] values;
    public MyTestData(int sampleSize){
        values = new int[sampleSize];
        //Out of scope of your question : Random IS a depencency you should manage
        Random rand = new Random(DateTime.Now.Millisecond);
        for (int i = 0; i < a.Length; i++)
        {
            a[i] = rand.Next(Int16.MaxValue);
        }
    }
    public int [] values();
        return values;
    }

}

/**
* Data builder, with default value. 
*/
public class MyTestDataBuilder {
    //1000 is actually your sample size : emphasis on the variable name
    private int sampleSize = 1000; //default value of the sample zie
    public MyTestDataBuilder(){
        //nope
    }
    //this is method if you need to test with another sample size
    public MyTestDataBuilder withSampleSizeOf(int size){
        sampleSize=size;
    }

    //call to get an actual MyTestData instance
    public MyTestData build(){
        return new MyTestData(sampleSize);
    }
}

public class MergeSortTest { 

    /**
    * Helper method build your expected data
    */
    private int [] getExpectedData(int [] source){
        int[] expectedData =  Arrays.copyOf(source,source.length);
        Arrays.sort(expectedData);
        return expectedData;
    }
}

//revamped tests method Merge
    public void MergeSortAssertArrayIsSorted(){
        int [] actualData = new MyTestDataBuilder().build();
        int [] expected = getExpectedData(actualData);
        //Don't know what 0 is for. An option, that should have a explicit name for sure :)
        MergeSort merge = new MergeSort();
        merge.mergeSort(actualData,0,actualData.length-1); 
        CollectionAssert.AreEqual(actualData, expected);
    }

 //revamped tests method Insertion
 public void InsertionSortAssertArrayIsSorted()
    {
        int [] actualData = new MyTestDataBuilder().build();
        int [] expected = getExpectedData(actualData);
        InsertionSort merge = new InsertionSort();
        merge.insertionSort(actualData);
        CollectionAssert.AreEqual(actualData, expectedData); 
    }
//another Test, for which very small sample size matter
public void doNotCrashesWithEmptyArray()
    {
        int [] actualData = new MyTestDataBuilder().withSampleSizeOf(0).build();
        int [] expected = getExpectedData(actualData);
        //continue ...
    }
}

2

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


2

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

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

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

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