2010-09-02 14 views
20

मैं अपने कोड का परीक्षण करने में मेरी सहायता करने के लिए एक कक्षा लिख ​​रहा हूं। ऐसा लगता है:क्या यूनिट परीक्षण के लिए कोड जोड़ना बुरा है?

/// <summary> 
/// Wrapper for the LogManager class to allow us to stub the logger 
/// </summary> 
public class Logger 
{ 
    private static ILogger _logger = null; 

    /// <summary> 
    /// This should be called to get a valid logger. 
    /// </summary> 
    /// <returns>A valid logger to log issues to file.</returns> 
    public static ILogger GetLogger() 
    { 
     if (_logger == null) 
      _logger = LogManager.GetLogger("logger"); 

     return _logger; 
    } 

    /// <summary> 
    /// This is used by unit tests to allow a stub to be used as a logger. 
    /// </summary> 
    /// <param name="logger"></param> 
    /// <returns></returns> 
    public static ILogger GetLogger(ILogger logger) 
    { 
     _logger = logger; 
     return _logger; 
    } 
} 

दूसरी विधि केवल यूनिट परीक्षण के लिए है। मैं कभी भी अपने उत्पादन कोड में इसे कॉल करने का इरादा नहीं रखता।

क्या यह बुरा अभ्यास है? क्या मुझे ऐसा कोई और तरीका मिलना चाहिए जो ऐसा नहीं करता?

+4

क्या कोई कारण है कि आप मोजे का उपयोग नहीं कर रहे हैं? – arootbeer

+1

मेरे एक प्रोफेसर ने मुझे एक बार कहा कि यदि आप वास्तव में मजबूत और लचीला सॉफ्टवेयर विकसित करना चाहते हैं, तो पहले अपने परीक्षण के मामलों को लिखें और फिर अपनी कार्यक्षमता को लागू करें। – Chris

+0

@arootbeer - ऐसा इसलिए है कि मैं mocks का उपयोग कर सकते हैं। मैं अपने कोड के माध्यम से इस लॉगर का उपयोग करता हूं। जब मैं इकाई परीक्षण करता हूं तो मैं लॉगर के लिए एक स्टब डालने में सक्षम होना चाहता हूं। जब मैं इकाई परीक्षण करता हूं तो मैं दूसरी विधि का उपयोग करके लॉगर का एक स्टब डाल दूंगा। उत्पादन के दौरान सामान्य लॉगर का उपयोग किया जाएगा। – Vaccano

उत्तर

3

आप इस पर विचार कर सकते:

/// <summary> 
/// Wrapper for the LogManager class to allow us to stub the logger 
/// </summary> 
public class Logger 
{ 
    private static ILogger _logger = null; 

    /// <summary> 
    /// This should be called to get a valid logger. 
    /// </summary> 
    /// <returns>A valid logger to log issues to file.</returns> 
    public static ILogger Logger 
    { 
     get 
     { 
      if (_logger == null) 
      { 
       _logger = LogManager.GetLogger("logger"); 
      } 
      return _logger 
     } 
     set 
     { 
      _logger = value; 
     } 
    } 
} 

यह लकड़हारा पहली बार यदि वह पहले से सेटर का उपयोग कर स्थापित नहीं किया गया गेटर कहा जाता है निर्धारित करता है।

+0

सिंगलटन पैटर्न पर एक नज़र डालें। यहां कुछ थ्रेडिंग मुद्दे हैं। लॉक करने के लिए आपको लॉक और ऑब्जेक्ट जोड़ना होगा। –

+0

"यहां कुछ थ्रेडिंग समस्याएं हैं। आपको लॉक करने के लिए लॉक और ऑब्जेक्ट जोड़ने की आवश्यकता होगी।" - जरूरी नहीं, यह कार्यान्वयन पर निर्भर करता है। रेस हालत के परिणामस्वरूप LogManager.GetLogger को दो बार बुलाया जा सकता है, लेकिन कार्यान्वयन के आधार पर यह समस्या हो सकती है या नहीं भी हो सकती है। – Joe

+0

@ जो - आप सही हैं। इस मामले में, 'LogManager.GetLogger()' "सिंगलटन" गतिविधि को स्वयं प्रबंधित करता है। GetLogger एक से अधिक बार बुलाया वास्तव में कोई फर्क नहीं पड़ता। – Vaccano

12

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

+2

टेस्ट-संचालित डिज़ाइन का अर्थ है कि आपका कार्यान्वयन आपके परीक्षणों से काफी प्रभावित है। ध्यान दें, मैं यह नहीं कह रहा हूं कि यह टेस्ट-संचालित डिज़ाइन का एक विशेष रूप से अच्छा उदाहरण है। – flq

+0

मैं पूरी तरह से क्रिस के साथ सहमत हूं। [मोल्स] (http://research.microsoft.com/en-us/projects/moles/) एक लाइब्रेरी है जो आपको परीक्षण करते समय कोड "इंजेक्ट" करने की अनुमति देती है। यह "आवश्यकताओं को डिजाइन करने" के बजाय "परीक्षणों को डिजाइन करने" को रोकने में मदद करता है। –

1

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

0

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

हालांकि, जब भी संभव हो, ऐसी चीजों को अपनी इकाई परीक्षण पुस्तकालय में रखने का प्रयास करें। जैसा कि जोएल ईथरटन कहते हैं, यह किसी भी मामले में कोड ब्लोट है ...

10

आम तौर पर, कोड केवल इकाई परीक्षण hides a design flaw की सुविधा के लिए मौजूद है।

इस मामले में, आप जो छिपा रहे हैं वह global state का उपयोग है। आपके लॉगर का उपयोग करने वाले वर्ग गुप्त रूप से इस पर निर्भर हैं: आप यह नहीं बता सकते कि उन्हें लॉगर की आवश्यकता नहीं है जब तक कि आप उनका स्रोत कोड नहीं पढ़ते!

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

+0

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

+0

@jvenema - यदि कोई वर्ग लॉगर का उपयोग करने में सक्षम है, तो मुझे लगता है कि इसे विज्ञापन देना चाहिए। सब कुछ तो अगर यह * एक लॉगर की आवश्यकता है। (हालांकि अगर लॉगर वैकल्पिक है, तो मैं संपत्ति इंजेक्शन के माध्यम से इसे उजागर करने के लिए ऑब्जेक्ट नहीं करता हूं।) –

+1

@ जेवेनेमा - आप दो रचनाकार प्रदान कर सकते हैं: एक जो इलोगर लेता है, और वह जो पैरामीटर नहीं लेता है लेकिन सिर्फ एक कारखाने से डिफ़ॉल्ट लॉगर या जो कुछ भी पहले कन्स्ट्रक्टर को कॉल करता है। यह छिपाने की तरह हो सकता है (कोड में, Intellisense में नहीं) जेफ का उल्लेख निर्भरता, लेकिन कम से कम कोड थोड़ा क्लीनर है। –

2

आईएमओ, हाँ, यह खराब कोड है। दो कारण:

  • लॉगर LogManager के बारे में जानता है। यह कसकर जोड़ों LogManager के लिए लॉगजर, और आप सामना की समस्या प्रस्तुत करता है; आपको ILMger ऑब्जेक्ट प्राप्त करने के लिए LogManager का उपयोग करना होगा, और LogManager को स्टब या नकल नहीं कर सकता है।
  • आप इकाई परीक्षण करने के एकमात्र उद्देश्य के लिए एक उत्पादन वर्ग के लिए कोड जोड़ लिया है। इससे डेवलपर्स को सड़क पर भ्रमित कर दिया जाएगा, जो परीक्षण हुक देखते हैं और सोचते हैं कि वे इसे उत्पादन कोड में उपयोग कर सकते हैं।

मैं एक या निम्न में अधिक की सिफारिश करेंगे:

  • एक अंतरफलक ILogManager कि GetLogger (उजागर) को परिभाषित करें, और LogManager पर इसे लागू।फिर, लॉगर प्रकार के ILogManager पर एक संपत्ति बनाएं (या लॉगर को चालू करते समय एक को पारित करने की आवश्यकता है) और लॉगर को लॉगजर के दायरे से बाहर लॉगऑनगर दें (एक आईओसी फ्रेमवर्क के माध्यम से, या जो भी लॉगर को तुरंत चालू करता है)। यह ढीला युग्मन है; अब आप किसी भी वर्ग की आपूर्ति कर सकते हैं जो MockLogManager सहित ILogManager लागू करता है, और लॉगर अंतर को नहीं जानता है।
  • अपने यूनिट परीक्षण के प्रयोजनों के लिए, लॉगर से "प्रॉक्सी" प्राप्त करें और वहां किसी भी परीक्षण-केवल विधियों को लागू करें। मैं मुख्य रूप से कक्षा के यूनिट परीक्षण संरक्षित तरीकों के लिए इस विधि का उपयोग करता हूं, लेकिन यदि आप लॉगर से LogManager को रद्द करने में असमर्थ पाते हैं, तो आप कम से कम इस व्युत्पन्न प्रॉक्सी और अपने हुक को अपने परीक्षण असेंबली में छिपा सकते हैं, जहां उत्पादन कोड सक्षम नहीं होना चाहिए उन्हें संदर्भित करने के लिए।
1

हाँ, डिजाइन न सही :(GetLogger विधि वास्तव में लकड़हारा सेट है सेटर विधि का प्रयोग करें - इस अधिक उपयुक्त लग रहा है

BTW

, तुम क्या परीक्षण करते हैं आप नेट मानक वालों का उपयोग करते हैं।? - मजाक के लिए कॉन्फ़िगर apropriate लॉग श्रोताओं

और MOQ पुस्तकालय देखा है http://code.google.com/p/moq/ मुझे यह पसंद आया

+0

.NET लॉगर्स की कुछ सीमाएं हैं।उदाहरण के लिए, श्रोताओं का सेट डीबग और ट्रेस लॉगर्स के बीच साझा किया जाता है, इसलिए यदि आप ट्रेस लॉग से अधिक डीबग लॉग में अधिक जानकारी लिखना चाहते थे तो आप मूल लॉगिंग के लिए उपयोग कर रहे थे, या एक अलग लॉग फ़ाइल के लिए, आप चाहते थे ट्रेस का उपयोग न करने के लिए अपने मूल लॉगिंग को फिर से लागू करने के लिए। आपको कोड में लॉगर्स को स्पष्ट रूप से कॉल करना होगा; तीसरे पक्ष के लॉगर्स में कुछ पहलू-उन्मुख डिज़ाइन शामिल हैं जिनका उपयोग सजावटी विधियों और फ़ील्ड के माध्यम से किया जा सकता है जिन्हें आप लॉग करना चाहते हैं। – KeithS

1
- आप जोड़ सकते हैं या श्रोताओं को हटाना (जैसा कि मुझे याद है) और यदि आप कहीं कुछ लिखने के लिए परीक्षण की जरूरत नहीं है।?।

मेरे पास कोई अजीब रचनाकार नहीं है

private readonly ILog _log = LogManager.GetLogger(typeof(MyViewClassName)); 

कौन सा तो इस तरह के रूप में इस्तेमाल किया जा सकता है, इस मामले में यह कैच ब्लॉक के अंदर कहा जाता है::

_log.Error("SomePage.aspx.cs Page_Load failed", ex); 

वहाँ log4net के लिए कोई आवरण है EAD मैं दृश्य में एक निजी चर है। ILog log4net का हिस्सा है। मैं उत्सुक हूं कि आप मॉक्स का उपयोग क्यों नहीं कर रहे हैं। यह समझ में आता है और फिर आपको वह करने की ज़रूरत नहीं होगी जो आप कर रहे हैं।

1

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

इसे एक और तरीके से देखें; यदि आपके पास कुछ कोड बनाने के दो समान तरीके हैं, और मुख्य अंतर टेस्टेबिलिटी है, तो अधिक टेस्टेबल विकल्प के लिए जाएं।

लेकिन आपके कोड के विशिष्ट मामले के लिए, मैं यहां अन्य अधिकांश पोस्टों से सहमत हूं; उस कोड को लिखने का एक बेहतर तरीका है।

0

यहां क्या गलत है कि लॉगजर प्राप्त करने वाला कोड यह जानना है कि वह कौन सा लॉगर चाहता है। वास्तव में, इसकी देखभाल करने का कोई कारण नहीं है।

यही कारण है कि इस पर नियंत्रण की उलट के लिए एक पोस्टर बच्चा है। सबसे सरल पर, एक कॉन्फ़िगर फ़ाइल में लॉगर का नाम चिपकाएं और अपने परीक्षण कोड में एक अलग मान का उपयोग करें। अधिक परिष्कृत स्तर पर, लॉगर लोड करने का निर्णय लेने के लिए MEF जैसे कुछ का उपयोग करें।

0

नहीं, यह बिल्कुल एक बुरी चीज नहीं है।

अच्छा कोड सभी आवश्यकताओं को पूरा करना चाहिए और कुछ और नहीं करना चाहिए।

हालांकि, आवश्यकताएं कई रूपों में आती हैं, और सभी कोडों की एक अस्थिर गैर-कार्यात्मक आवश्यकता यह है कि आप दूसरों को साबित कर सकते हैं कि यह काम करता है।

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

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

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