.net - आप व्यक्तिगत तरीकों का परीक्षण कैसे करते हैं?




unit-testing tdd (20)

आपको पहले कोड में अपने कोड के निजी तरीकों का परीक्षण नहीं करना चाहिए। आपको 'सार्वजनिक इंटरफेस' या एपीआई, अपनी कक्षाओं की सार्वजनिक चीजों का परीक्षण करना चाहिए। एपीआई बाहरी कॉलर्स के सामने आने वाली सभी सार्वजनिक विधियां हैं।

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

आपको इस कारण से InternalsVisibleToAtrribute का उपयोग करने से बचना चाहिए।

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

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

ऐसा करने का सही तरीका क्या है?


उन्हें internal घोषित करें, और फिर अपने यूनिट परीक्षण असेंबली को देखने की अनुमति देने के लिए InternalsVisibleToAttribute का उपयोग करें।


एमबीयूनीट को इस परावर्तक के लिए एक अच्छा रैपर मिला।

Reflector dogReflector = new Reflector(new Dog());
dogReflector.Invoke("DreamAbout", DogDream.Food);

आप गुणों से मूल्य भी सेट और प्राप्त कर सकते हैं

dogReflector.GetProperty("Age");

"परीक्षण निजी" के संबंध में मैं सहमत हूं कि .. पूरी दुनिया में। निजी इकाई परीक्षण करने में कोई बात नहीं है। लेकिन असली दुनिया में आप कोड को दोबारा बदलने के बजाय निजी परीक्षण लिखना चाहते हैं।


कभी-कभी, निजी घोषणाओं का परीक्षण करना अच्छा हो सकता है। मूल रूप से, एक कंपाइलर में केवल एक सार्वजनिक विधि होती है: संकलन (स्ट्रिंग आउटपुटफाइलनाम, पैराम स्ट्रिंग [] स्रोत SFileNames)। मुझे यकीन है कि आप समझते हैं कि प्रत्येक "छिपी हुई" घोषणाओं के परीक्षण के बिना ऐसी विधि का परीक्षण करना मुश्किल होगा!

यही कारण है कि हमने आसान परीक्षण करने के लिए विजुअल टी # बनाया है। यह एक मुफ्त .NET प्रोग्रामिंग भाषा है (सी # v2.0 संगत)।

हमने '.-' ऑपरेटर जोड़ा है। यह सिर्फ '।' जैसा व्यवहार करता है ऑपरेटर, सिवाय इसके कि आप अपने परीक्षण परियोजना से कुछ भी बदले बिना किसी भी छिपी घोषणा को अपने परीक्षणों से भी एक्सेस कर सकते हैं।

हमारी वेबसाइट पर एक नज़र डालें: इसे मुफ्त में download करें


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

आप यहां लेख पा सकते हैं:

http://www.codeproject.com/KB/cs/testnonpublicmembers.aspx


खैर आप दो तरीकों से निजी विधि का परीक्षण कर सकते हैं

  1. आप PrivateObject क्लास का उदाहरण बना सकते हैं सिंटैक्स निम्नानुसार है

    PrivateObject obj= new PrivateObject(PrivateClass);
    //now with this obj you can call the private method of PrivateCalss.
    obj.PrivateMethod("Parameters");
    
  2. आप प्रतिबिंब का उपयोग कर सकते हैं।

    PrivateClass obj = new PrivateClass(); // Class containing private obj
    Type t = typeof(PrivateClass); 
    var x = t.InvokeMember("PrivateFunc", 
        BindingFlags.InvokeMethod | BindingFlags.NonPublic | BindingFlags.Public |  
            BindingFlags.Instance, null, obj, new object[] { 5 });
    

निजी प्रकार के 2 प्रकार हैं। स्टेटिक निजी तरीके और गैर स्टेटिक निजी तरीके (इंस्टेंस तरीके)। निम्नलिखित 2 लेख बताते हैं कि इकाई के उदाहरणों के साथ निजी तरीकों का परीक्षण कैसे करें।

  1. यूनिट परीक्षण स्टेटिक निजी तरीके
  2. यूनिट परीक्षण गैर स्टेटिक निजी तरीके

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

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

इस प्रकार AccessPrivateWrapper - http://amazedsaint.blogspot.com/2010/05/accessprivatewrapper-c-40-dynamic.html पैदा हुआ - यह एक त्वरित रैपर वर्ग है जो सी # 4.0 गतिशील विशेषताओं और प्रतिबिंब का उपयोग करके नौकरी को आसान बना देगा।

आप आंतरिक / निजी प्रकार जैसे बना सकते हैं

    //Note that the wrapper is dynamic
    dynamic wrapper = AccessPrivateWrapper.FromType
        (typeof(SomeKnownClass).Assembly,"ClassWithPrivateConstructor");

    //Access the private members
    wrapper.PrivateMethodInPrivateClass();

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

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

रिफैक्टरिंग के उत्पाद का अर्थ है कि निजी विधि अब एक अलग वर्ग है जो मूल वर्ग के लिए सहयोगी बन गई है। इसका व्यवहार अपने स्वयं के यूनिट परीक्षणों के माध्यम से अच्छी तरह से समझा जाएगा।

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

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

बेशक निजी तरीकों का परीक्षण सीधे एक अंतिम उपाय हो सकता है यदि आपके पास विरासत आवेदन है लेकिन मैं बेहतर परीक्षण सक्षम करने के लिए विरासत कोड को दोबारा पसंद करता हूं। माइकल फेदर्स ने इस विषय पर एक महान पुस्तक लिखी है। http://www.amazon.co.uk/Working-Effectively-Legacy-Robert-Martin/dp/0131177052


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

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


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

आपके टेस्ट केस क्लास में इन विधियों को शामिल करें और फिर उन्हें संकेत के रूप में नियोजित करें।

  /**
   *
   * @var Class_name_of_class_you_want_to_test_private_methods_in
   * note: the actual class and the private variable to store the 
   * class instance in, should at least be different case so that
   * they do not get confused in the code.  Here the class name is
   * is upper case while the private instance variable is all lower
   * case
   */
  private $class_name_of_class_you_want_to_test_private_methods_in;

  /**
   * This uses reflection to be able to get private methods to test
   * @param $methodName
   * @return ReflectionMethod
   */
  protected static function getMethod($methodName) {
    $class = new ReflectionClass('Class_name_of_class_you_want_to_test_private_methods_in');
    $method = $class->getMethod($methodName);
    $method->setAccessible(true);
    return $method;
  }

  /**
   * Uses reflection class to call private methods and get return values.
   * @param $methodName
   * @param array $params
   * @return mixed
   *
   * usage:     $this->_callMethod('_someFunctionName', array(param1,param2,param3));
   *  {params are in
   *   order in which they appear in the function declaration}
   */
  protected function _callMethod($methodName, $params=array()) {
    $method = self::getMethod($methodName);
    return $method->invokeArgs($this->class_name_of_class_you_want_to_test_private_methods_in, $params);
  }

$ यह -> _ callMethod ('_ someFunctionName', सरणी (param1, param2, param3));

बस पैरामीटर को क्रम में जारी करें कि वे मूल निजी फ़ंक्शन में दिखाई देते हैं


मैंने InternalsVisibleToAttribute विधि का भी उपयोग किया है। यह भी उल्लेखनीय है कि, यदि आप इसे प्राप्त करने के लिए अपने पूर्व निजी तरीकों को आंतरिक बनाने में असहज महसूस करते हैं, तो हो सकता है कि वे वैसे भी प्रत्यक्ष इकाई परीक्षणों का विषय न हों।

आखिरकार, आप अपने विशिष्ट कार्यान्वयन के बजाए अपनी कक्षा के व्यवहार का परीक्षण कर रहे हैं - आप पूर्व को बदलने के बाद बाद में बदल सकते हैं और आपके परीक्षण अभी भी पास हो सकते हैं।


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

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

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

माइक्रोसॉफ्ट इसके लिए दो तंत्र प्रदान करता है:

पहुंचकर्ता

  • कक्षा परिभाषा के स्रोत कोड गोटो
  • कक्षा के नाम पर राइट-क्लिक करें
  • "निजी एक्सेसर बनाएं" चुनें
  • उस प्रोजेक्ट को चुनें जिसमें एक्सेसर बनाया जाना चाहिए => आप foo_accessor नाम के साथ एक नई कक्षा के साथ समाप्त हो जाएंगे। संकलन के दौरान यह वर्ग गतिशील रूप से उत्पन्न होगा और सभी सदस्यों को सार्वजनिक रूप से उपलब्ध कराएगा।

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

PrivateObject क्लास माइक्रोसॉफ्ट का उपयोग करने का दूसरा तरीका है। VisualStudio.TestTools.UnitTesting.PrivateObject

// Wrap an already existing instance
PrivateObject accessor = new PrivateObject( objectInstanceToBeWrapped );

// Retrieve a private field
MyReturnType accessiblePrivateField = (MyReturnType) accessor.GetField( "privateFieldName" );

// Call a private method
accessor.Invoke( "PrivateMethodName", new Object[] {/* ... */} );

A way to do this is to have your method protected and write a test fixture which inherits your class to be tested. This way, you are nor turning your method public , but you enable the testing.


Also note that the InternalsVisibleToAtrribute has a requirement that your assembly be strong named , which creates it's own set of problems if you're working in a solution that had not had that requirement before. I use the accessor to test private methods. See this question that for an example of this.


Here's an example, first the method signature:

private string[] SplitInternal()
{
    return Regex.Matches(Format, @"([^/\[\]]|\[[^]]*\])+")
                        .Cast<Match>()
                        .Select(m => m.Value)
                        .Where(s => !string.IsNullOrEmpty(s))
                        .ToArray();
}

Here's the test:

/// <summary>
///A test for SplitInternal
///</summary>
[TestMethod()]
[DeploymentItem("Git XmlLib vs2008.dll")]
public void SplitInternalTest()
{
    string path = "pair[path/to/@Key={0}]/Items/Item[Name={1}]/Date";
    object[] values = new object[] { 2, "Martin" };
    XPathString xp = new XPathString(path, values);

    PrivateObject param0 = new PrivateObject(xp);
    XPathString_Accessor target = new XPathString_Accessor(param0);
    string[] expected = new string[] {
        "pair[path/to/@Key={0}]",
        "Items",
        "Item[Name={1}]",
        "Date"
    };
    string[] actual;
    actual = target.SplitInternal();
    CollectionAssert.AreEqual(expected, actual);
}

I use PrivateObject class. But as mentioned previously better to avoid testing private methods.

Class target = new Class();
PrivateObject obj = new PrivateObject(target);
var retVal = obj.Invoke("PrivateMethod");
Assert.AreEqual(retVal);

You could also declare it as public or internal (with InternalsVisibleToAttribute) while building in debug-Mode:

    /// <summary>
    /// This Method is private.
    /// </summary>
#if DEBUG
    public
#else
    private
#endif
    static string MyPrivateMethod()
    {
        return "false";
    }

It bloats the code, but it will be private in a release build.


You could generate the test method for the private method from Visual studio 2008. When you create a unit test for a private method, a Test References folder is added to your test project and an accessor is added to that folder. The accessor is also referred to in the logic of the unit test method. This accessor allows your unit test to call private methods in the code that you are testing. For details have a look at

http://msdn.microsoft.com/en-us/library/bb385974.aspx





private