2011-09-21 14 views
59

मैं Pex उपयोग करने के लिए कुछ कोड का परीक्षण करने के कोशिश कर रहा हूँ है जो रुकावट के लिए नहीं। मेरे पास चार ठोस कार्यान्वयन के साथ एक अमूर्त वर्ग है। मैंने चार ठोस प्रकारों में से प्रत्येक के लिए कारखाने के तरीकों का निर्माण किया है। मैं भी, सार प्रकार के लिए बनाया सिवाय के रूप में this nice thread बताते हैं, Pex- सार कारखाने विधि का उपयोग नहीं होगा, और न ही यह होना चाहिए था।Pex बताने के लिए कैसे एक अमूर्त वर्ग ठोस कार्यान्वयन

समस्या यह है कि मेरा कुछ कोड चार ठोस प्रकारों पर निर्भर करता है जो सभी हैं (क्योंकि यह बहुत ही असंभव है कि कोई और सबक्लास बनाए जाएंगे), लेकिन Pex बनाने के लिए मोल्स का उपयोग करके कोड तोड़ रहा है एक स्टब

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

संपादित करें:

ऐसा लगता है कि मैं, कैसे वर्ग संरचना में ही काम करता है पर कुछ अधिक जानकारी जोड़ने हालांकि याद अब भी है कि लक्ष्य को कैसे करने के लिए की जरूरत है कक्षाओं को रोकने के लिए Pex प्राप्त करें।

यहाँ सार आधार वर्ग का सरलीकृत संस्करण और चार ठोस उसके कार्यान्वयन हैं।

public abstract class AbstractClass 
{ 
    public abstract AbstractClass Distill(); 

    public static bool operator ==(AbstractClass left, AbstractClass right) 
    { 
     // some logic that returns a bool 
    } 

    public static bool operator !=(AbstractClass left, AbstractClass right) 
    { 
     // some logic that basically returns !(operator ==) 
    } 

    public static Implementation1 Implementation1 
    { 
     get 
     { 
      return Implementation1.GetInstance; 
     } 
    } 
} 

public class Implementation1 : AbstractClass, IEquatable<Implementation1> 
{ 
    private static Implementation1 _implementation1 = new Implementation1(); 

    private Implementation1() 
    { 
    } 

    public override AbstractClass Distill() 
    { 
     return this; 
    } 

    internal static Implementation1 GetInstance 
    { 
     get 
     { 
      return _implementation1; 
     } 
    } 

    public bool Equals(Implementation1 other) 
    { 
     return true; 
    } 
} 

public class Implementation2 : AbstractClass, IEquatable<Implementation2> 
{ 
    public string Name { get; private set; } 
    public string NamePlural { get; private set; } 

    public Implementation2(string name) 
    { 
     // initializes, including 
     Name = name; 
     // and sets NamePlural to a default 
    } 

    public Implementation2(string name, string plural) 
    { 
     // initializes, including 
     Name = name; 
     NamePlural = plural; 
    } 

    public override AbstractClass Distill() 
    { 
     if (String.IsNullOrEmpty(Name)) 
     { 
      return AbstractClass.Implementation1; 
     } 
     return this; 
    } 

    public bool Equals(Implementation2 other) 
    { 
     if (other == null) 
     { 
      return false; 
     } 

     return other.Name == this.Name; 
    } 
} 

public class Implementation3 : AbstractClass, IEquatable<Implementation3> 
{ 
    public IEnumerable<AbstractClass> Instances { get; private set; } 

    public Implementation3() 
     : base() 
    { 
     Instances = new List<AbstractClass>(); 
    } 

    public Implementation3(IEnumerable<AbstractClass> instances) 
     : base() 
    { 
     if (instances == null) 
     { 
      throw new ArgumentNullException("instances", "error msg"); 
     } 

     if (instances.Any<AbstractClass>(c => c == null)) 
     { 
      thrown new ArgumentNullException("instances", "some other error msg"); 
     } 

     Instances = instances; 
    } 

    public override AbstractClass Distill() 
    { 
     IEnumerable<AbstractClass> newInstances = new List<AbstractClass>(Instances); 

     // "Flatten" the collection by removing nested Implementation3 instances 
     while (newInstances.OfType<Implementation3>().Any<Implementation3>()) 
     { 
      newInstances = newInstances.Where<AbstractClass>(c => c.GetType() != typeof(Implementation3)) 
             .Concat<AbstractClass>(newInstances.OfType<Implementation3>().SelectMany<Implementation3, AbstractUnit>(i => i.Instances)); 
     } 

     if (newInstances.OfType<Implementation4>().Any<Implementation4>()) 
     { 
      List<AbstractClass> denominator = new List<AbstractClass>(); 

      while (newInstances.OfType<Implementation4>().Any<Implementation4>()) 
      { 
       denominator.AddRange(newInstances.OfType<Implementation4>().Select<Implementation4, AbstractClass>(c => c.Denominator)); 
       newInstances = newInstances.Where<AbstractClass>(c => c.GetType() != typeof(Implementation4)) 
              .Concat<AbstractClass>(newInstances.OfType<Implementation4>().Select<Implementation4, AbstractClass>(c => c.Numerator)); 
      } 

      return (new Implementation4(new Implementation3(newInstances), new Implementation3(denominator))).Distill(); 
     } 

     // There should only be Implementation1 and/or Implementation2 instances 
     // left. Return only the Implementation2 instances, if there are any. 
     IEnumerable<Implementation2> i2s = newInstances.Select<AbstractClass, AbstractClass>(c => c.Distill()).OfType<Implementation2>(); 
     switch (i2s.Count<Implementation2>()) 
     { 
      case 0: 
       return AbstractClass.Implementation1; 
      case 1: 
       return i2s.First<Implementation2>(); 
      default: 
       return new Implementation3(i2s.OrderBy<Implementation2, string>(c => c.Name).Select<Implementation2, AbstractClass>(c => c)); 
     } 
    } 

    public bool Equals(Implementation3 other) 
    { 
     // omitted for brevity 
     return false; 
    } 
} 

public class Implementation4 : AbstractClass, IEquatable<Implementation4> 
{ 
    private AbstractClass _numerator; 
    private AbstractClass _denominator; 

    public AbstractClass Numerator 
    { 
     get 
     { 
      return _numerator; 
     } 

     set 
     { 
      if (value == null) 
      { 
       throw new ArgumentNullException("value", "error msg"); 
      } 

      _numerator = value; 
     } 
    } 

    public AbstractClass Denominator 
    { 
     get 
     { 
      return _denominator; 
     } 

     set 
     { 
      if (value == null) 
      { 
       throw new ArgumentNullException("value", "error msg"); 
      } 
      _denominator = value; 
     } 
    } 

    public Implementation4(AbstractClass numerator, AbstractClass denominator) 
     : base() 
    { 
     if (numerator == null || denominator == null) 
     { 
      throw new ArgumentNullException("whichever", "error msg"); 
     } 

     Numerator = numerator; 
     Denominator = denominator; 
    } 

    public override AbstractClass Distill() 
    { 
     AbstractClass numDistilled = Numerator.Distill(); 
     AbstractClass denDistilled = Denominator.Distill(); 

     if (denDistilled.GetType() == typeof(Implementation1)) 
     { 
      return numDistilled; 
     } 
     if (denDistilled.GetType() == typeof(Implementation4)) 
     { 
      Implementation3 newInstance = new Implementation3(new List<AbstractClass>(2) { numDistilled, new Implementation4(((Implementation4)denDistilled).Denominator, ((Implementation4)denDistilled).Numerator) }); 
      return newInstance.Distill(); 
     } 
     if (numDistilled.GetType() == typeof(Implementation4)) 
     { 
      Implementation4 newImp4 = new Implementation4(((Implementation4)numReduced).Numerator, new Implementation3(new List<AbstractClass>(2) { ((Implementation4)numDistilled).Denominator, denDistilled })); 
      return newImp4.Distill(); 
     } 

     if (numDistilled.GetType() == typeof(Implementation1)) 
     { 
      return new Implementation4(numDistilled, denDistilled); 
     } 

     if (numDistilled.GetType() == typeof(Implementation2) && denDistilled.GetType() == typeof(Implementation2)) 
     { 
      if (((Implementation2)numDistilled).Name == (((Implementation2)denDistilled).Name) 
      { 
       return AbstractClass.Implementation1; 
      } 
      return new Implementation4(numDistilled, denDistilled); 
     } 

     // At this point, one or both of numerator and denominator are Implementation3 
     // instances, and the other (if any) is Implementation2. Because both 
     // numerator and denominator are distilled, all the instances within either 
     // Implementation3 are going to be Implementation2. So, the following should 
     // work. 
     List<Implementation2> numList = 
      numDistilled.GetType() == typeof(Implementation2) ? new List<Implementation2>(1) { ((Implementation2)numDistilled) } : new List<Implementation2>(((Implementation3)numDistilled).Instances.OfType<Implementation2>()); 

     List<Implementation2> denList = 
      denDistilled.GetType() == typeof(Implementation2) ? new List<Implementation2>(1) { ((Implementation2)denDistilled) } : new List<Implementation2>(((Implementation3)denDistilled).Instances.OfType<Implementation2>()); 

     Stack<int> numIndexesToRemove = new Stack<int>(); 
     for (int i = 0; i < numList.Count; i++) 
     { 
      if (denList.Remove(numList[i])) 
      { 
       numIndexesToRemove.Push(i); 
      } 
     } 

     while (numIndexesToRemove.Count > 0) 
     { 
      numList.RemoveAt(numIndexesToRemove.Pop()); 
     } 

     switch (denList.Count) 
     { 
      case 0: 
       switch (numList.Count) 
       { 
        case 0: 
         return AbstractClass.Implementation1; 
        case 1: 
         return numList.First<Implementation2>(); 
        default: 
         return new Implementation3(numList.OfType<AbstractClass>()); 
       } 
      case 1: 
       switch (numList.Count) 
       { 
        case 0: 
         return new Implementation4(AbstractClass.Implementation1, denList.First<Implementation2>()); 
        case 1: 
         return new Implementation4(numList.First<Implementation2>(), denList.First<Implementation2>()); 
        default: 
         return new Implementation4(new Implementation3(numList.OfType<AbstractClass>()), denList.First<Implementation2>()); 
       } 
      default: 
       switch (numList.Count) 
       { 
        case 0: 
         return new Implementation4(AbstractClass.Implementation1, new Implementation3(denList.OfType<AbstractClass>())); 
        case 1: 
         return new Implementation4(numList.First<Implementation2>(), new Implementation3(denList.OfType<AbstractClass>())); 
        default: 
         return new Implementation4(new Implementation3(numList.OfType<AbstractClass>()), new Implementation3(denList.OfType<AbstractClass>())); 
       } 
     } 
    } 

    public bool Equals(Implementation4 other) 
    { 
     return Numerator.Equals(other.Numerator) && Denominator.Equals(other.Denominator); 
    } 
} 

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

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

एक और संपादित करें:

यहाँ कारखाने वर्गों में से एक का एक उदाहरण है। यह Pex- जेनरेटेड टेस्ट प्रोजेक्ट की फैक्ट्रीज़ निर्देशिका में है।

public static partial class Implementation3Factory 
{ 
    [PexFactoryMethod(typeof(Implementation3))] 
    public static Implementation3 Create(IEnumerable<AbstractClass> instances, bool useEmptyConstructor) 
    { 
     Implementation3 i3 = null; 
     if (useEmptyConstructor) 
     { 
      i3 = new Implementation3(); 
     } 
     else 
     { 
      i3 = new Implementation3(instances); 
     } 

     return i3; 
    } 
} 

इन ठोस कार्यान्वयन के लिए मेरे कारखाने के तरीकों में, कंक्रीट कार्यान्वयन के लिए किसी भी निर्माता का उपयोग करना संभव है। उदाहरण में, useEmptyConstructor पैरामीटर नियंत्रण जो उपयोग करने के लिए कन्स्ट्रक्टर। अन्य कारखाने के तरीकों में समान विशेषताएं हैं। मुझे पढ़ना याद है, हालांकि मुझे तुरंत लिंक नहीं मिल रहा है, कि इन फैक्ट्री विधियों को ऑब्जेक्ट को प्रत्येक संभावित कॉन्फ़िगरेशन में बनाने की अनुमति देनी चाहिए।

+2

सुनिश्चित नहीं है कि आप उस कार्यान्वयन के साथ क्या समस्या हल कर रहे हैं, लेकिन यदि कोई भी कभी भी बेस क्लास से प्राप्त एक और प्रकार बनाता है, तो ऐसा लगता है कि वे आपके कार्यान्वयन को भी तोड़ देंगे। ऐसा लगता है जैसे यह दोनों एक्स्टेंसिबिलिटी को तोड़ सकता है और उपयोगकर्ता को आश्चर्यचकित कर सकता है, जिनमें से दोनों डिजाइन गंध हैं। क्या आप इसके बजाय अपनी व्युत्पन्न कक्षाओं में एक विशेषता (संभवतः 'आंतरिक') जोड़ सकते हैं, और बस इसके लिए खोज सकते हैं? तब आपको यह ध्यान देने की ज़रूरत नहीं है कि PEX एक स्टब बनाता है, क्योंकि आपको इसका उपयोग करने की आवश्यकता नहीं है, और यह इस तरह से एनोटेट नहीं किया जाएगा जिससे आपका कोड टूट जाए। यह उपयोगकर्ता कोड तोड़ नहीं देगा। –

+0

@ MerlynMorgan-ग्राहम आपके इनपुट के लिए धन्यवाद। सच में, यह परियोजना सी # से F # के लिए अधिक उपयुक्त है, लेकिन भविष्य की रखरखाव एक चिंता है। व्यवहार वास्तविक विरासत की तुलना में "भेदभाव संघ" के करीब है। उस ने कहा, अमूर्त बेस क्लास के चार उप-वर्गों ने मैंने स्थापित गणना संरचना के भीतर संचालन के एक बंद सेट का प्रतिनिधित्व किया है। कोई भी इसका विस्तार नहीं करेगा, लेकिन अमूर्त बेस क्लास और कंक्रीट सबक्लास दोनों को उनकी असेंबली के बाहर दिखाई देने की आवश्यकता है। यदि कुछ और है तो आपका मतलब _internal_ है, मुझे यकीन नहीं है कि यह क्या है। – Andrew

+0

यदि व्युत्पन्न कक्षाओं के लिए केवल वे लोग समझते हैं, तो चिंता क्यों करें - क्या यह वास्तव में * कुछ तोड़ देगा? यदि हां, तो आप व्युत्पन्न कक्षाओं का पता कैसे लगाते हैं? मैं आपकी पहचान तंत्र के लिए एक विकल्प प्रदान करने की कोशिश कर रहा था। इसके अलावा, आपको एक प्रकार-सुरक्षित enum के समान पैटर्न लगता है। आप पूरी तरह से उस पैटर्न का पालन कर सकते हैं और अपने सभी कार्यान्वयन आंतरिक बना सकते हैं, और चार कार्यान्वयन के लिए बेस क्लास पर स्थिर फैक्ट्री गुण बना सकते हैं। उन्हें सही तरीके से नाम दें ताकि वे सही प्रकार का निर्माण कर सकें, लेकिन उन्हें मूल प्रकार के रूप में वापस कर दें। –

उत्तर

1

क्या आपने [PexUseType] विशेषता का उपयोग करके Pex को बताने का प्रयास किया है, कि आपके सार वर्ग के लिए गैर-सार उपप्रकार मौजूद हैं? यदि Pex किसी भी गैर-सार उपप्रकारों से अवगत नहीं है, तो Pex की बाधा सॉल्वर यह निर्धारित करेगी कि एक कोड पथ जो गैर-सार उपप्रकार के अस्तित्व पर निर्भर करता है वह अक्षम है।

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