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




unit-testing tdd (24)

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

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


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

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


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

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

इस प्रकार 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();

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


एमएस टेस्ट में एक अच्छी सुविधा है जो VSCodeGenAccessors नामक फ़ाइल बनाने के द्वारा प्रोजेक्ट में उपलब्ध निजी सदस्यों और विधियों को बनाता है।

[System.Diagnostics.DebuggerStepThrough()]
    [System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.TestTools.UnitTestGeneration", "1.0.0.0")]
    internal class BaseAccessor
    {

        protected Microsoft.VisualStudio.TestTools.UnitTesting.PrivateObject m_privateObject;

        protected BaseAccessor(object target, Microsoft.VisualStudio.TestTools.UnitTesting.PrivateType type)
        {
            m_privateObject = new Microsoft.VisualStudio.TestTools.UnitTesting.PrivateObject(target, type);
        }

        protected BaseAccessor(Microsoft.VisualStudio.TestTools.UnitTesting.PrivateType type)
            :
                this(null, type)
        {
        }

        internal virtual object Target
        {
            get
            {
                return m_privateObject.Target;
            }
        }

        public override string ToString()
        {
            return this.Target.ToString();
        }

        public override bool Equals(object obj)
        {
            if (typeof(BaseAccessor).IsInstanceOfType(obj))
            {
                obj = ((BaseAccessor)(obj)).Target;
            }
            return this.Target.Equals(obj);
        }

        public override int GetHashCode()
        {
            return this.Target.GetHashCode();
        }
    }

बेसएसेसर से प्राप्त कक्षाओं के साथ

जैसे कि

[System.Diagnostics.DebuggerStepThrough()]
[System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.TestTools.UnitTestGeneration", "1.0.0.0")]
internal class SomeClassAccessor : BaseAccessor
{

    protected static Microsoft.VisualStudio.TestTools.UnitTesting.PrivateType m_privateType = new Microsoft.VisualStudio.TestTools.UnitTesting.PrivateType(typeof(global::Namespace.SomeClass));

    internal SomeClassAccessor(global::Namespace.Someclass target)
        : base(target, m_privateType)
    {
    }

    internal static string STATIC_STRING
    {
        get
        {
            string ret = ((string)(m_privateType.GetStaticField("STATIC_STRING")));
            return ret;
        }
        set
        {
            m_privateType.SetStaticField("STATIC_STRING", value);
        }
    }

    internal int memberVar    {
        get
        {
            int ret = ((int)(m_privateObject.GetField("memberVar")));
            return ret;
        }
        set
        {
            m_privateObject.SetField("memberVar", value);
        }
    }

    internal int PrivateMethodName(int paramName)
    {
        object[] args = new object[] {
            paramName};
        int ret = (int)(m_privateObject.Invoke("PrivateMethodName", new System.Type[] {
                typeof(int)}, args)));
        return ret;
    }

मुझे आश्चर्य है कि किसी ने अभी तक यह नहीं कहा है, लेकिन एक समाधान जिसे मैंने नियोजित किया है, कक्षा के अंदर एक स्थिर विधि स्वयं परीक्षण करने के लिए है। यह आपको परीक्षण करने के लिए सार्वजनिक और निजी सब कुछ तक पहुंच प्रदान करता है।

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


यदि आप .NET का उपयोग कर रहे हैं, तो आपको InternalsVisibleToAttribute उपयोग करना चाहिए।


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);

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);
}

मैं कंपाइलर निर्देशों का उपयोग नहीं करता क्योंकि वे जल्दी से चीजों को अव्यवस्थित करते हैं। यदि आपको वास्तव में उनकी आवश्यकता है तो इसे कम करने का एक तरीका यह है कि उन्हें आंशिक वर्ग में रखा जाए और अपना निर्माण उत्पादन संस्करण बनाते समय .cs फ़ाइल को अनदेखा कर दें।


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.


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

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

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

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


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

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

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

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


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

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


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

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

CC -Dprivate=public

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

  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 });
    

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

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

  /**
   *
   * @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));

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


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

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

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


Here is good article about unit testing of private methods. But I'm not sure what's better, to make you application designed specially for testing(it's like creating tests for testing only) or use reflexion for testing. Pretty sure most of us will choose second way.


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


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

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

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.


1) If you have a legacy code then the only way to test private methods is by reflection.

2) If it is new code then you have the following options:

  • Use reflection (to complicated)
  • Write unit test in the same class (makes the production code ugly by having test code also in it)
  • Refactor and make the method public in some kind of util class
  • Use @VisibleForTesting annotation and remove private

I prefer the annotation method, simplest and least complicated. The only issue is that we have increased the visibility which I think is not a big concern. We should always be coding to interface, so if we have an interface MyService and an implementation MyServiceImpl then we can have the corresponding test classes that is MyServiceTest (test interface methods) and MyServiceImplTest (test private methods). All clients should anyway be using the interface so in a way even though the visibility of the private method has been increased it should not really matter.


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

public class ReflectionTools
{
    // If the class is non-static
    public static Object InvokePrivate(Object objectUnderTest, string method, params object[] args)
    {
        Type t = objectUnderTest.GetType();
        return t.InvokeMember(method,
            BindingFlags.InvokeMethod |
            BindingFlags.NonPublic |
            BindingFlags.Instance |
            BindingFlags.Static,
            null,
            objectUnderTest,
            args);
    }
    // if the class is static
    public static Object InvokePrivate(Type typeOfObjectUnderTest, string method, params object[] args)
    {
        MemberInfo[] members = typeOfObjectUnderTest.GetMembers(BindingFlags.NonPublic | BindingFlags.Static);
        foreach(var member in members)
        {
            if (member.Name == method)
            {
                return typeOfObjectUnderTest.InvokeMember(method, BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.InvokeMethod, null, typeOfObjectUnderTest, args);
            }
        }
        return null;
    }
}

Then in your actual tests, you can do something like this:

Assert.AreEqual( 
  ReflectionTools.InvokePrivate(
    typeof(StaticClassOfMethod), 
    "PrivateMethod"), 
  "Expected Result");

Assert.AreEqual( 
  ReflectionTools.InvokePrivate(
    new ClassOfMethod(), 
    "PrivateMethod"), 
  "Expected Result");




private