2010-08-12 11 views
8

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

क्या कोई इस उदाहरण का वर्णन करने में आसान, आसान सुझाव दे सकता है?

मेरी योजना इस कार्यक्षमता के आस-पास यूनिट परीक्षण लिखना है ताकि हम यह जान सकें कि हम तुरंत परीक्षण चलाकर कुछ तोड़ चुके हैं।

+0

क्या यह प्रश्न भाषा अज्ञेयवादी है? – kbrimington

+0

@ कब्रिमिंगटन सी #। –

+0

सी # को प्राथमिकता दी जाती है, क्योंकि यह मेरे दर्शक है; लेकिन मैं इसे एक अच्छा, सरल उदाहरण दिया गया है, इसे सी # में फिर से लिख सकता हूं। मेरे द्वारा – dgiard

उत्तर

12

एक थोड़ा सरल, और इस तरह शायद स्पष्ट, उदाहरण है:

public string GetServerAddress() 
{ 
    return "172.0.0.1"; 
} 

public void DoSomethingWithServer() 
{ 
    Console.WriteLine("Server address is: " + GetServerAddress()); 
} 

हैं GetServerAddress परिवर्तन एक सरणी वापस जाने के लिए है:

public string[] GetServerAddress() 
{ 
    return new string[] { "127.0.0.1", "localhost" }; 
} 

DoSomethingWithServer से उत्पादन कुछ अलग हो जाएगा, लेकिन यह सब अभी भी एक संकुचित बग बनाने के लिए संकलित करेंगे।

पहला (गैर-सरणी) संस्करण Server address is: 127.0.0.1 प्रिंट करेगा और दूसरा Server address is: System.String[] प्रिंट करेगा, यह कुछ ऐसा है जिसे मैंने उत्पादन कोड में भी देखा है। कहने की जरूरत नहीं है कि यह अब नहीं है!

abstract class ProviderBase<T> 
{ 
    public IEnumerable<T> Results 
    { 
    get 
    { 
     List<T> list = new List<T>(); 
     using(IDataReader rdr = GetReader()) 
     while(rdr.Read()) 
      list.Add(Build(rdr)); 
     return list; 
    } 
    } 
    protected abstract IDataReader GetReader(); 
    protected T Build(IDataReader rdr); 
} 

विभिन्न कार्यान्वयन के साथ इस्तेमाल किया जा रहा:

+0

हालांकि आप इसके लिए पृथ्वी पर कैसे परीक्षण करेंगे? रिटर्न वैल्यू को बदलना संकलन समय पर पकड़ा जा सकता है (जैसे 'स्ट्रिंग एड्रेस = GetServerAddreess(); ') नहीं कर सकता है, लेकिन स्ट्रिंग्स में पकड़ना लगभग असंभव है – TheLQ

+0

@TheLQ, यदि आप कोड हैं तो:' string serverAddress = GetServerAddress(); Console.WriteLine ("सर्वर पता है:" + serverAddress); 'आपको इस उदाहरण में संकलन त्रुटि मिल जाएगी =) और, "वर्बोज़ कोड कम कुशल होने" के बारे में चिंता करने की कोई बात नहीं है जैसे कि जेआईटी इसे दूर करने में विफल रहता है , मैं बहुत आश्चर्यचकित होगा * और * चिंतित! :-) – Rob

8

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

class DataProvider { 
    public static IEnumerable<Something> GetData() { 
     return new Something[] { ... }; 
    } 
} 

class Consumer { 
    void DoSomething() { 
     Something[] data = (Something[])DataProvider.GetData(); 
    } 
} 

बदलें GetData() एक List<Something> वापस जाने के लिए, और Consumer टूट जाएगा।

यह कुछ हद तक विकसित हो सकता है, लेकिन मैंने वास्तविक कोड में समान समस्याएं देखी हैं।

4

आप एक विधि है कि करता है कहो। उनमें से एक का उपयोग किया जाता है:

public bool CheckNames(NameProvider source) 
{ 
    IEnumerable<string> names = source.Results; 
    switch(names.Count()) 
    { 
     case 0: 
     return true;//obviously none invalid. 
     case 1: 
     //having one name to check is a common case and for some reason 
     //allows us some optimal approach compared to checking many. 
     return FastCheck(names.Single()); 
     default: 
     return NormalCheck(names) 
    } 
} 

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

फिर भी, जब हम किसी विशेष कार्यान्वयन के आधार पर नहीं हैं, हम विशेष रूप से रिवाइंडेबल होने के कारण विशेष रूप से (Count() या तो आईसीओलेक्शन को कॉल करेंगे। गणना या अन्यथा गणना के माध्यम से गणना करें, जिसके बाद नाम- ।। चेकिंग होगी

किसी ने हालांकि देखता संपत्ति परिणाम और सोचता है कि "हम्म, कि एक सा बेकार है" वे साथ बदलें:

public IEnumerable<T> Results 
{ 
    get 
    { 
    using(IDataReader rdr = GetReader()) 
     while(rdr.Read()) 
     yield return Build(rdr); 
    } 
} 

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

किसी भी इकाई परीक्षण जो शून्य से अधिक परिणामों के साथ चेक नाम पर हिट करता है, हालांकि इसे पकड़ने जा रहा है।


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

संबंधित मुद्दे