यूनिट परीक्षण विधियों का सबसे अच्छा तरीका है जो एक ही कक्षा के अंदर अन्य तरीकों को कहते हैं


35

मैं हाल ही में कुछ दोस्तों के साथ चर्चा कर रहा था कि निम्न में से कौन सी 2 विधियां रिटर्न के परिणामों को स्टब करने के लिए सबसे अच्छा है या किसी कक्षा के अंदर के तरीकों से उसी वर्ग के तरीकों के लिए कॉल करें।

यह एक बहुत ही सरल उदाहरण है। वास्तव में कार्य बहुत अधिक जटिल हैं।

उदाहरण:

public class MyClass
{
     public bool FunctionA()
     {
         return FunctionB() % 2 == 0;
     }

     protected int FunctionB()
     {
         return new Random().Next();
     }
}

तो इसको टेस्ट करने के लिए हमारे पास 2 तरीके हैं।

विधि 1: विधियों की कार्यक्षमता को बदलने के लिए क्रियाओं और क्रियाओं का उपयोग करें। उदाहरण:

public class MyClass
{
     public Func<int> FunctionB { get; set; }

     public MyClass()
     {
         FunctionB = FunctionBImpl;
     }

     public bool FunctionA()
     {
         return FunctionB() % 2 == 0;
     }

     protected int FunctionBImpl()
     {
         return new Random().Next();
     }
}

[TestClass]
public class MyClassTests
{
    private MyClass _subject;

    [TestInitialize]
    public void Initialize()
    {
        _subject = new MyClass();
    }

    [TestMethod]
    public void FunctionA_WhenNumberIsOdd_ReturnsTrue()
    {
        _subject.FunctionB = () => 1;

        var result = _subject.FunctionA();

        Assert.IsFalse(result);
    }
}

विधि 2: सदस्यों को आभासी, व्युत्पन्न वर्ग और व्युत्पन्न वर्ग में कार्यक्षमता को बदलने के लिए कार्य और क्रिया का उपयोग करें उदाहरण:

public class MyClass
{     
     public bool FunctionA()
     {
         return FunctionB() % 2 == 0;
     }

     protected virtual int FunctionB()
     {
         return new Random().Next();
     }
}

public class TestableMyClass
{
     public Func<int> FunctionBFunc { get; set; }

     public MyClass()
     {
         FunctionBFunc = base.FunctionB;
     }

     protected override int FunctionB()
     {
         return FunctionBFunc();
     }
}

[TestClass]
public class MyClassTests
{
    private TestableMyClass _subject;

    [TestInitialize]
    public void Initialize()
    {
        _subject = new TestableMyClass();
    }

    [TestMethod]
    public void FunctionA_WhenNumberIsOdd_ReturnsTrue()
    {
        _subject.FunctionBFunc = () => 1;

        var result = _subject.FunctionA();

        Assert.IsFalse(result);
    }
}

मैं जानना चाहता हूं कि क्या बेहतर है और क्यों?

अपडेट: नोट: फ़ंक्शनबी भी सार्वजनिक हो सकता है


आपका उदाहरण सरल है, लेकिन बिल्कुल सही नहीं है। FunctionAएक बूल लौटाता है लेकिन केवल एक स्थानीय चर सेट xकरता है और कुछ भी वापस नहीं करता है।
एरिक पी।

1
इस विशेष उदाहरण में, फंक्शनबी public staticएक अलग वर्ग में हो सकता है ।

कोड समीक्षा के लिए, आपसे वास्तविक कोड पोस्ट करने की अपेक्षा की जाती है, इसका कोई सरलीकृत संस्करण नहीं। एफएक्यू देखें। इसके स्टैंड के रूप में, आप एक विशिष्ट प्रश्न पूछ रहे हैं जो कोड समीक्षा की तलाश में नहीं है।
विंस्टन एवरर्ट

1
FunctionBटूट-दर-डिजाइन है। new Random().Next()लगभग हमेशा गलत होता है। आपको इसका उदाहरण इंजेक्ट करना चाहिए Random। ( Randomयह भी एक बुरी तरह से डिज़ाइन किया गया वर्ग है, जो कुछ अतिरिक्त समस्याएं पैदा कर सकता है)
कोडइनचोस

प्रतिनिधियों के माध्यम से एक अधिक सामान्य नोट DI पर बिल्कुल ठीक imho
jk है।

जवाबों:


32

मूल पोस्टर अपडेट के बाद संपादित किया गया।

अस्वीकरण: एक सी # प्रोग्रामर (ज्यादातर जावा या रूबी) नहीं। मेरा जवाब होगा: मैं इसे बिल्कुल नहीं परखूंगा, और मुझे नहीं लगता कि आपको चाहिए।

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

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

संबंधित चर्चा वहां देखें: https://stackoverflow.com/questions/105007/should-i-test-private-methods-or-only-public-ones

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

यदि आप वास्तव में दो परीक्षणों को पूरी तरह से अलग करने में सक्षम होना चाहते हैं, तो मैं फंक्शनए का परीक्षण करते समय (आमतौर पर, एक निश्चित ज्ञात सही मान लौटाता हूं) फंक्शनबी को मॉक करने के लिए किसी प्रकार की मॉकिंग तकनीक का उपयोग करूंगा। मेरे पास विशिष्ट मॉकिंग लाइब्रेरी की सलाह के लिए C # इकोसिस्टम ज्ञान की कमी है, लेकिन आप इस प्रश्न को देख सकते हैं ।


2
पूरी तरह से @Martin जवाब से सहमत हैं। जब आप कक्षा के लिए इकाई परीक्षण लिखते हैं तो आपको विधियों का परीक्षण नहीं करना चाहिए । आप जो परीक्षण कर रहे हैं वह एक वर्ग व्यवहार है, कि अनुबंध (घोषणा क्या वर्ग को करना है) संतुष्ट है। इसलिए, आपकी इकाई परीक्षणों को इस मामले के लिए उजागर की जाने वाली सभी आवश्यकताओं (सार्वजनिक विधियों / गुणों का उपयोग करके) को कवर करना चाहिए, जिसमें असाधारण मामले भी शामिल हैं

हैलो, प्रतिक्रिया के लिए धन्यवाद, लेकिन यह मेरे सवाल का जवाब नहीं दिया। मुझे कोई फर्क नहीं पड़ता कि क्या फंक्शनबी निजी / संरक्षित था। यह सार्वजनिक भी हो सकता है और फिर भी इसे फंक्शन से बुलाया जा सकता है।

आधार वर्ग को फिर से डिज़ाइन किए बिना, इस समस्या को संभालने का सबसे आम तरीका है, MyClassउस पद्धति को उप-वर्ग करना और उसे ओवरराइड करना, जिसे आप स्टब करना चाहते हैं। FunctionBसार्वजनिक होने के लिए अपने प्रश्न को अपडेट करना भी एक अच्छा विचार हो सकता है।
एरिक पी।

1
protectedविधियाँ किसी वर्ग की सार्वजनिक सतह का हिस्सा होती हैं, जब तक आप यह सुनिश्चित नहीं करते हैं कि विभिन्न विधानसभाओं में आपकी कक्षा का कार्यान्वयन नहीं हो सकता है।
कोडइन्चोस

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

11

मैं इस सिद्धांत की सदस्यता लेता हूं कि यदि कोई फ़ंक्शन परीक्षण करना महत्वपूर्ण है, या प्रतिस्थापित करना महत्वपूर्ण है, तो यह महत्वपूर्ण है कि परीक्षण के तहत कक्षा का एक निजी कार्यान्वयन विवरण न हो, लेकिन एक अलग वर्ग का सार्वजनिक कार्यान्वयन विवरण हो ।

इसलिए अगर मैं ऐसे परिदृश्य में हूं जहां मेरे पास है

class A 
{
     public B C()
     {
         D();
     }

     private E D();
     {
         // i actually want to control what this produces when I test C()
         // or this is important enough to test on its own
         // and, typically, both of the above
     }
}

फिर मैं रिफ्लेक्टर करने जा रहा हूं।

class A 
{
     ICollaborator collaborator;

     public A(ICollaborator collaborator)
     {
         this.collaborator = collaborator;
     }

     public B C()
     {
         collaborator.D();
     }
}

अब मेरे पास एक परिदृश्य है जहां डी () स्वतंत्र रूप से परीक्षण योग्य है, और पूरी तरह से बदली है।

संगठन के साधन के रूप में, मेरा सहयोगी समान नामस्थान स्तर पर नहीं रह सकता है। उदाहरण के लिए, अगर AFooCorp.BLL में है, तो मेरे सहयोगी एक और परत गहरी हो सकती है, जैसा कि FooCorp.BLL.Collaborators (या जो भी नाम उपयुक्त हो)। मेरे सहयोगी आगे केवल विधानसभा के अंदर internalपहुंच संशोधक के माध्यम से दिखाई दे सकते हैं, जिसे मैं तब InternalsVisibleToविधानसभा विशेषता के माध्यम से अपनी इकाई परीक्षण परियोजना (एस) में भी उजागर करूंगा । Takeaway यह है कि आप अभी भी अपने एपीआई को साफ रख सकते हैं, जहां तक ​​कॉल करने वालों का सवाल है, जबकि वेरीफाइड कोड का उत्पादन करते हैं।


हाँ अगर ICollaborator को कई तरीकों की ज़रूरत है। यदि आपके पास एक ऐसी वस्तु है जो केवल एक ही काम है, तो एक ही विधि लपेटना है, लेकिन मैं इसे एक प्रतिनिधि के साथ प्रतिस्थापित करना चाहता हूं।
जे.के.

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

0

मार्टिन क्या कहते हैं, इस पर जोड़ना

यदि आपकी विधि निजी / संरक्षित है - तो इसका परीक्षण न करें। यह कक्षा के लिए आंतरिक है और इसे कक्षा के बाहर एक्सेस नहीं किया जाना चाहिए।

आपके द्वारा बताए गए दोनों दृष्टिकोणों में, मुझे ये चिंताएँ हैं -

विधि 1 - यह वास्तव में परीक्षण में परीक्षण के व्यवहार के तहत कक्षा को बदलता है।

विधि 2 - यह वास्तव में उत्पादन कोड का परीक्षण नहीं करता है, इसके बजाय किसी अन्य कार्यान्वयन का परीक्षण करता है।

बताई गई समस्या में, मैं देखता हूं कि ए का एकमात्र तर्क यह देखना है कि क्या फंक्शनबी का आउटपुट भी है। हालांकि उदाहरण के लिए, फंक्शनबी रैंडम वैल्यू देता है, जो परीक्षण के लिए कठिन है।

मैं एक यथार्थवादी परिदृश्य की उम्मीद करता हूं, जहां हम MyClass को सेटअप कर सकते हैं ताकि हम जान सकें कि फंक्शनबी क्या वापस आएगा। तब हमारा अपेक्षित परिणाम ज्ञात होता है, हम वास्तविक परिणाम पर फ़ंक्शन और एसेट को कॉल कर सकते हैं।


3
protectedलगभग वैसा ही है public। केवल privateऔर internalकार्यान्वयन विवरण हैं।
कोडइन्चोस

@codeinchaos - मैं यहां उत्सुक हूं। परीक्षण के लिए, संरक्षित विधियां 'निजी' हैं जब तक आप विधानसभा विशेषताओं को संशोधित नहीं करते हैं। केवल व्युत्पन्न प्रकारों में संरक्षित सदस्यों की पहुंच है। आभासी के अपवाद के साथ, मैं यह नहीं देखता कि संरक्षित को एक परीक्षण से जनता के समान क्यों माना जाना चाहिए। क्या आप कृपया विस्तार से बता सकते हैं?
श्रीकांत वेणुगोपालन

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

@CodesInChaos, सहमत हैं कि व्युत्पन्न वर्ग विभिन्न विधानसभाओं में हो सकता है, लेकिन गुंजाइश अभी भी आधार और व्युत्पन्न प्रकारों तक सीमित है। एक्सेस मॉडिफ़ायर को संशोधित करने के लिए इसे परीक्षण योग्य बनाने के लिए कुछ ऐसा है जिसके बारे में मैं थोड़ा परेशान हूं। मैंने इसे किया है, लेकिन यह मेरे लिए एक एंटीपर्टन लगता है।
श्रीकांत वेणुगोपालन

0

मैं व्यक्तिगत रूप से मेथड 1 का उपयोग करता हूं अर्थात सभी विधियों को क्रिया या फंक बना रहा हूं क्योंकि इससे मेरे लिए कोड परीक्षण क्षमता में सुधार हुआ है। किसी भी समाधान के साथ इस दृष्टिकोण के साथ पेशेवरों और विपक्ष हैं:

पेशेवरों

  1. केवल कोड परीक्षण के लिए कोड पैटर्न का उपयोग करते हुए सरल कोड संरचना की अनुमति देता है जिससे अतिरिक्त जटिलता हो सकती है।
  2. मोके जैसे लोकप्रिय मॉकिंग फ्रेमवर्क द्वारा आवश्यक वर्चुअल तरीकों को हटाने के लिए और मोहरबंद करने की अनुमति देता है। सीलिंग कक्षाएं और आभासी तरीकों को खत्म करना उन्हें इनलाइनिंग और अन्य संकलक अनुकूलन के लिए उम्मीदवार बनाता है। ( https://msdn.microsoft.com/en-us/library/ff647802.aspx )
  3. यूनिट टेस्ट में फंक / एक्शन के क्रियान्वयन की जगह पर परीक्षण को सरल बनाना उतना ही सरल है जितना कि फंक / एक्शन को एक नया मूल्य प्रदान करना।
  4. यह भी परीक्षण के लिए अनुमति देता है कि क्या स्टैटिक फंक को किसी अन्य विधि से बुलाया गया था क्योंकि स्थैतिक विधियों का मजाक नहीं उड़ाया जा सकता है।
  5. कॉल साइट पर एक विधि को कॉल करने के लिए सिंटैक्स के बाद से फंड्स / क्रियाओं के लिए मौजूदा तरीकों को रिफलेक्टर करना आसान है। (ऐसे समय के लिए जब आप फंक / एक्शन में किसी विधि को रिफैक्ट नहीं कर सकते हैं)

विपक्ष

  1. यदि आपकी कक्षा फंकस / क्रियाओं का कोई विरासत पथ नहीं है, तो Funcs / Actions का उपयोग नहीं किया जा सकता है।
  2. डिफ़ॉल्ट मापदंडों का उपयोग नहीं कर सकते। डिफ़ॉल्ट पैरामीटर के साथ एक फंक बनाना एक नए प्रतिनिधि को बनाने की अनुमति देता है जो उपयोग के मामले के आधार पर कोड को भ्रमित कर सकता है
  3. कॉलिंग विधियों के लिए नामित पैरामीटर सिंटैक्स का उपयोग कुछ की तरह नहीं कर सकते (पहले नाम: "एस", अंतिम नाम: "के")
  4. सबसे बड़ा Con है कि आपके पास Funcs और Actions में 'इस' संदर्भ तक पहुंच नहीं है, और इसलिए कक्षा पर किसी भी निर्भरता को स्पष्ट रूप से मापदंडों के रूप में पारित किया जाना चाहिए। इसका अच्छा यह है कि आप सभी निर्भरताओं को जानते हैं, लेकिन यदि आपके पास कई गुण हैं, जो आपके फंड पर निर्भर करेगा। आपके उपयोग के मामले के आधार पर आपका माइलेज अलग-अलग होगा।

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

इसके अलावा, मैं आमतौर पर फंड के लिए प्रॉपर्टी नहीं बनाता लेकिन उन्हें सीधे इनलाइन करता हूं

public class MyClass
{
     public Func<int> FunctionB = () => new Random().Next();

     public bool FunctionA()
     {
         return FunctionB() % 2 == 0;
     }
}

उम्मीद है की यह मदद करेगा!


-1

मॉक का उपयोग करना संभव है। Nuget: https://www.nuget.org/packages/moq/

और मुझे विश्वास है कि यह काफी सरल है और समझ में आता है।

public class SomeClass
{
    public SomeClass(int a) { }

    public void A()
    {
        B();
    }

    public virtual void B()
    {

    }
}

[TestFixture]
public class Test
{
    [Test]
    public void Test_A_Calls_B()
    {
        var mockedObject = new Mock<SomeClass>(5); // You can also specify constructor arguments.
        //You can also setup what a function can return.
        var obj = mockedObject.Object;
        obj.A();

        Mock.Get(obj).Verify(x=>x.B(),Times.AtLeastOnce);//This test passes
    }
}

मॉक को ओवरराइड करने के लिए वर्चुअल तरीके की जरूरत है।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.