2009-07-29 11 views
7

मैं एक पर निर्भर है कि मैं खुद को एक विधि के साथ मिल गया है ताकि कोड परीक्षण योग्य (परीक्षण और विलगन चौखटे के रूप में NUnit और RhinoMocks का प्रयोग करके) और पाया है है एक वर्ग पुनर्रचना कर रहा हूँ (यानी यह कुछ है कि द्वारा बनाई गई है पर निर्भर करता है अन्य विधि)। निम्नलिखित की तरह कुछ:क्या यह एक विधि के लिए एक कोड गंध है जो दूसरे पर निर्भर करता है?

public class Impersonator 
{ 
    private ImpersonationContext _context; 

    public void Impersonate() 
    { 
     ... 
     _context = GetContext(); 
     ... 
    } 

    public void UndoImpersonation() 
    { 
     if (_context != null) 
      _someDepend.Undo(); 
    } 
} 

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

public void ExerciseClassToTest(Impersonator c) 
{ 
    try 
    { 
     if (NeedImpersonation()) 
     { 
      c.Impersonate(); 
     } 
     ... 
    } 
    finally 
    { 
     c.UndoImpersonation(); 
    } 
} 

मैं इस पर काम किया नहीं होता अगर मैं एक लिखने की कोशिश नहीं की थी UndoImpersonation के लिए इकाई परीक्षण और मुझे अन्य सार्वजनिक विधि को कॉल करके परीक्षण स्थापित करने के लिए मिला। तो, क्या यह एक बुरी गंध है और यदि ऐसा है तो मैं इसके आसपास कैसे काम कर सकता हूं?

+0

संयोग से, इस एक महत्वपूर्ण पक्ष से पता चलता - इकाई परीक्षण के लाभ: आप एक कारखाने के रूप में Impersonator वर्ग रख सकते (?): – sleske

+0

हाँ यह आपको अपने डिजाइन के बारे में सोचते हैं ... :-), सहमति:) – jpoh

उत्तर

9

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

वैसे भी, कि मेरे शेख़ी है, धन्यवाद :-) सुन

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

जब आप इसे बिना के बिना पूर्व-स्थितियों को सेट करते हैं तो परीक्षणों में से एक होना चाहिए - इसे या तो गलती से विफल होना चाहिए या कॉलर को ऐसा करने के लिए परेशान नहीं होने पर इसकी अपनी पूर्व शर्त स्थापित करनी चाहिए ।

+0

एचएम, मैं * करता हूं * कोड गंध बहुत उपयोगी लगता है। वे केवल पाठ्यक्रम के अंगूठे के नियम हैं, लेकिन अच्छे वाले (कम से कम)। लेकिन हाँ, अगर आप स्वीकार करते हैं कि पूर्व शर्त की आवश्यकता है तो ठीक है (एक डिज़ाइन प्रश्न), परीक्षण ठीक है। सेटअप के बिना व्यवहार का परीक्षण करने के लिए सलाह के लिए +1। – sleske

+0

मुझे लगता है कि "कोड गंध" चीज सामान्य प्रोग्रामर दोष से पीड़ित होती है जो सबकुछ भी कड़वाहट से लेती है। –

+0

+1 अपनी पूर्व शर्त – Nelson

7

ठीक है, यह बताने के लिए एक सा बहुत कम संदर्भ है, ऐसा लगता है _someDepend की तरह निर्माता में initalized किया जाना चाहिए।

उदाहरण विधि में फ़ील्ड प्रारंभ करना मेरे लिए एक बड़ा नहीं है। एक वर्ग पूरी तरह प्रयोग योग्य होना चाहिए (यानी सभी विधियों का काम) जैसे ही इसे बनाया गया हो; इसलिए निर्माता (ओं) को सभी आवृत्ति चर प्रारंभ करना चाहिए। उदाहरण देखें वार्ड कनिंघम की विकी में single step construction पर पृष्ठ।

कारण एक उदाहरण विधि में खेतों आरंभ बुरा है कि यह कैसे आप तरीकों कॉल कर सकते हैं पर एक अंतर्निहित आदेश लगाता है मुख्य रूप से है। आपके मामले में, TheMethodIWantToTest अलग-अलग चीजें करेगा, इस पर निर्भर करता है कि DoStuff को पहले कहा गया था या नहीं। यह आम तौर पर कुछ अपने वर्ग के एक उपयोगकर्ता उम्मीद होती है नहीं है, तो यह बुरा :-(है।

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

अपने मामले पर लागू होता है क्या और अधिक संदर्भ के बिना बताने के लिए कठिन है।

+1

उत्कृष्ट बिंदु! –

+0

_someDepend बस वर्ग को किसी विशेष स्थिति में डाल रहा है। और इसलिए इसे कक्षा निर्माता – jpoh

+0

में प्रारंभ नहीं किया जा सकता है, वैसे, अलग-अलग राज्यों के साथ एक वर्ग होने के कारण मुझे इसी कारण से संदिग्ध लगता है (वही विधि इस "राज्य" के आधार पर अलग-अलग चीजें करेगी)। W/o संदर्भ बताना मुश्किल है, लेकिन क्या आपकी कक्षा शायद बहुत अधिक कर रही है? हो सकता है कि प्रति "राज्य" प्रति वर्ग अधिक उचित (कुछ अमूर्त वर्ग के संभावित उप-वर्ग) है? – sleske

2

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

2

यह जब दूरस्थ कनेक्शन के साथ काम अक्सर अपरिहार्य है, उदाहरण के लिए - आप Open() कॉल करने के लिए इससे पहले कि आप Close() कॉल कर सकते हैं, और आप Open() स्वचालित रूप से निर्माता में हो नहीं करना चाहती।

हालांकि यह करने के दौरान आप बहुत सावधान रहना चाहते हैं कि पैटर्न कुछ आसानी से समझा जाता है - उदाहरण के लिए मुझे लगता है कि ज्यादातर उपयोगकर्ता लेन-देन के लिए इस तरह के व्यवहार को स्वीकार करते हैं, लेकिन जब वे DoStuff() और TheMethodIWantToTest() (जो कुछ भी वास्तव में कहा जाता है)।

वर्तमान स्थिति का प्रतिनिधित्व करने वाली संपत्ति रखने के लिए यह आमतौर पर सबसे अच्छा अभ्यास है - फिर से लगातार समझने वाले डिज़ाइन के उदाहरण के लिए दूरस्थ या डीबी कनेक्शन देखें।

संपत्तियों के लिए कभी भी ऐसा करने के लिए बड़ा नंबर नहीं है। गुण कभी परवाह नहीं करते कि उन्हें किस क्रम में बुलाया जाता है। यदि आपके पास एक साधारण मूल्य है जो विधियों के क्रम पर निर्भर करता है तो यह संपत्ति-प्राप्त करने के बजाय पैरामीटर रहित विधि होना चाहिए।

1

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

श्रृंखलन के स्तर अपने वस्तु के डिजाइन पर निर्भर करता है - आप कर सकते हैं या तो कांटा या झरना

  • forking: classToTest1.SomeDependency.DoSomething()
  • व्यापक: classToTest1.DoSomething() (जो आंतरिक रूप से SomeDependency.DoSomething कहेंगे)

लेकिन के रूप में दूसरों का उल्लेख किया है, निश्चित रूप से निर्माता में अपने राज्य initialisation रखने जो मैं बता सकता हूं, शायद आपकी समस्या का समाधान करेगा।

2

हाँ, मुझे लगता है कि इस मामले में एक कोड गंध है। विधियों के बीच निर्भरताओं की वजह से नहीं, बल्कि वस्तु की अस्पष्ट पहचान के कारण। Impersonator होने के बजाय जो अलग-अलग व्यक्तित्व राज्यों में हो सकता है, क्यों एक अपरिवर्तनीय Persona नहीं है?

यदि आपको Persona की आवश्यकता है, तो मौजूदा ऑब्जेक्ट की स्थिति बदलने के बजाय बस एक नया बनाएं। यदि आपको बाद में कुछ सफाई करने की आवश्यकता है, तो Persona डिस्पोजेबल बनाएं।

using (var persona = impersonator.createPersona(...)) 
{ 
    // do something with the persona 
} 
+0

+1 अच्छा बिंदु पूरा किए बिना विधि को कॉल करने के लिए परीक्षण जोड़ने के लिए +1। नए ऑब्जेक्ट उदाहरण बनाने के लिए डरो मत, भले ही वे डिस्पोजेबल हों। इलेक्ट्रॉन पुनर्नवीनीकरण योग्य हैं :-)। – sleske

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