Moq

2016-02-15 10 views
6

का उपयोग कर इंटरफ़ेस के लिए 'ऑब्जेक्ट। एक्वाल्स (ऑब्जेक्ट ओबीजे)' का नकल कैसे करें मेरे पास अपने सिर को लपेटने में एक दिलचस्प समस्या है। इस तरह के कुछ इंटरफ़ेस पर विचार करें:Moq

public interface IMyThing 
{ 
    int Id { get; } 
} 

अब मैं इस इंटरफ़ेस का उपयोग करने वाले कोड का परीक्षण करना चाहता हूं। शायद कुछ LINQ जादू के साथ कुछ। कुछ इस तरह:

public class SomeClass 
{ 
    private IMyThing _thing; 

    ... 

    public bool HasThing(IEnumerable<IMyThing> things) 
    { 
     return things.Contains(_thing); 
    } 
} 

मैं Moq का उपयोग कर IMyThing को लागू सभी वस्तुओं मजाक कर रहा हूँ:

public static IMyThing MockMyThing(int newId) 
{ 
    var mock = new Mock<IMyThing>(); 
    mock.Setup(s => s.Id).Returns(newId); 
    mock.Setup(s => s.Equals(It.IsAny<object>())).Returns<object>(obj => 
    { 
     if (typeof(IMyThing).IsAssignableFrom(obj.GetType())) 
     { 
      return ((IMyThing)obj).Id == newId; 
     } 

     return false; 
    }); 

    return mock.Object; 
} 

यहाँ बात है। उपरोक्त कोड चेतावनियों के बिना संकलित करता है लेकिन कभी काम नहीं करेगा। MoqEquals() विधि के लिए एक इंटरसेप्टर बनाता है लेकिन यह कभी नहीं पहुंचता है। इसके बजाए, ऑब्जेक्ट प्रॉक्सी के बराबर विधि को बुलाया जाता है। मैं इस तथ्य को दोषी ठहरा रहा हूं कि मैं एक इंटरफेस का मज़ाक उड़ा रहा हूं लेकिन एक ठोस वर्ग नहीं।

अद्यतन: बस एहसास हुआ Moq एक इंटरसेप्टर भी नहीं बनाता है।

बेशक मैं इस तरह IMyThing इंटरफेस को बढ़ाने सकता है:

public interface IMyThing : IEquatable<IMyThing> 
{ 
    int Id { get; } 
} 

LINQ ऑपरेटर IEquatable<T> इंटरफ़ेस समझते हैं और यह प्रयोग करेंगे।

मैं क्योंकि यह करने के लिए नहीं करना चाहती:

  • यह अन्य IMyThing वस्तुओं
  • IEquatable<T> इस उद्देश्य के
  • मैं अपने मॉडल को कलंकित नहीं करना चाहती के लिए नहीं किया गया था के साथ ही प्रयोग करने योग्य है केवल इसे मॉक करने योग्य बनाने के लिए

आप इसे कैसे हल करेंगे?

+0

यदि आप 'HasThing() 'विधि के दौरान कुछ जटिल परिदृश्यों का परीक्षण करने का प्रयास कर रहे हैं, तो शायद इसे' IENumerable 'स्वीकार करना चाहिए। क्यों नहीं "एक अतिरिक्त स्तर का संकेत" पेश करें? –

उत्तर

1

मैंने गीथब पर Moq प्रोजेक्ट में कोड का योगदान समाप्त कर दिया (issue #248 देखें)। इस परिवर्तन के साथ इंटरफेस मैक्स के लिए भी object.Equals(object obj), object.GetHashCode() और object.ToString() नकल करना संभव है।

चलिए देखते हैं कि यह स्वीकार किया जाता है या नहीं।

+1

योगदान स्वीकार कर लिया गया! हुर्रे! – koloman

0

शायद मैं आपको पूरी तरह से समझ नहीं पा रहा हूं लेकिन मुझे IMyThing के लिए Equals() फ़ंक्शन को विस्तारित नहीं किया गया है या नहीं? तो मुझे लगता है कि मेरा पहला दृष्टिकोण ओवरराइड होगा, या फ़ंक्शन को बढ़ाएगा, यदि यह काम नहीं करता है तो मैं IMyThing : IEquatable<IMyThing>, मुझे पता है कि IEquatable<T> इस उद्देश्य के लिए नहीं था, लेकिन यह बंद है। और पिछले और मुझे लगता है कि आप इसे करने की जरूरत है, लेकिन यह मौजूद है, जैसे bool IsEquals(IMyThing other){//check if equals}

+1

'बराबर()' का कोई ठोस ओवरराइड नहीं है क्योंकि 'IMyThing' का कोई ठोस कार्यान्वयन नहीं है। मैं मॉकिंग फ्रेमवर्क 'मोक' का उपयोग ** ** का दावा कर रहा हूं ** यदि 'IMyThing' है तो एक कार्यान्वयन है। इसलिए बस 'बराबर() 'ओवरराइडिंग काम नहीं करेगा। कुछ 'IsEqual() 'विधि को कार्यान्वित करना उसी कारण से काम नहीं करेगा। – koloman

1

एक समारोह बनाने मुझे लगता है कि समस्या यह है कि Equals विधि इंटरफेस आप मजाक कर रहे हैं पर नहीं है से आता है न। यदि आप ओवरराइड करने योग्य Equals विधि (यहां तक ​​कि यदि यह कुछ भी नहीं करता है) के साथ कक्षा बनाते हैं, तो आप इसे नकल करने में सक्षम हैं।

public class MyTestThing : IMyThing 
{ 
    public virtual int Id { get; } 

    public override bool Equals(object obj) 
    { 
     return base.Equals(obj); 
    } 
} 

[TestCase(55)] 
public void Can_mock_equals_method(int newId) 
{ 
    var mockThing = new Mock<MyTestThing>(); 

    mockThing.Setup(t => t.Id).Returns(newId); 
    mockThing.Setup(t => t.Equals(It.IsAny<object>())) 
     .Returns<object>(t => (t as IMyThing)?.Id == newId); 

    Assert.That(mockThing.Object.Equals(new MyRealThing(newId))); 
} 

सूचना है कि यदि आप MyTestThing पर Equals विधि बाहर टिप्पणी इस परीक्षण, असफल हो जायेगी क्योंकि Moq अब इसे नकली कर सकते हैं।

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

+0

यह काम करेगा। जैसे ही 'IMyThing' के कई सदस्यों और कई असेंबली में कई कार्यान्वयन होते हैं, यह अजीब हो जाता है। बस इंटरफेस के लिए व्यवहार का मज़ाक उड़ाने में सक्षम होना इतना सुविधाजनक होगा। – koloman

1

यदि आप तुलना करने के तरीके को बदल सकते हैं, तो आपको सीधे तुलना का उपयोग नहीं करना चाहिए, लेकिन उन वस्तुओं का उपयोग करना जो आपके लिए तुलना कर सकते हैं।

इस तरह अपनी कक्षा में परिवर्तन करने पर विचार करें:

public class SomeClass 
{ 
    private IMyThing _thing; 

    public bool HasThing(IEnumerable<IMyThing> things, IEqualityComparer<IMyThing> comparer = null) 
    { 
     comparer = comparer ?? EqualityComparer<IMyThing>.Default; 
     return things.Contains(_thing, comparer); 
    } 
} 

तो फिर तुम सिर्फ comparer उपहास करने के लिए भी है।

+0

वास्तव में यह एक दिलचस्प समाधान है! लेकिन 'हैशिंग (...)' के कॉलर को 'IMTThing' के कार्यान्वयन को जरूरी नहीं है। इसलिए यह स्वाभाविक रूप से नहीं जानता कि किस समानता तुलनाकर्ता का उपयोग करना है। इसलिए, जब यह मेरी परीक्षण समस्या हल करता है तो यह वास्तविक कोड को आवश्यकतानुसार अधिक जटिल बनाता है। अंत में कंक्रीट 'IMyThing' कार्यान्वयन की तुलना में बदलाव का विषय नहीं है। क्या आप सहमत हैं? – koloman