2010-09-24 12 views
11

एक्सटेंशन विधियों परीक्षण के लिए अच्छा नहीं है (यहां वर्णित है: Mocking Extension Methods with Moq, http://www.clariusconsulting.net/blogs/kzu/archive/2009/12/22/Howtomockextensionmethods.aspx)।नकली कैसे करें (Moq के साथ) एकता विधियों

लेकिन शायद यूनिटी विधियों के मज़ाक के लिए कुछ समाधान हैं?

public class MyManager 
{ 
    public MyManager(IUnityContainer container) : base(container) { } 

    public IResult DoJob(IData data) 
    { 
     IMyLog log = MyContainer.Resolve<IMyLog>(); 

     ... use log.Id ... 

     MyContainer.Resolve<...>();//usage for other purposes... 
    } 

मुझे यकीन है कि 'DoJob' विधि हमेशा अन्य स्रोतों से मिल जाएगा कंटेनर से 'IMyLog' वस्तु, लेकिन नहीं ... कैसे मुझे लगता है कि परीक्षण कर सकते हैं होना चाहता हूँ: मेरे मामले में मैं निम्नलिखित समारोह है?

IMyLog log = UnityContainer.Resolve(typeof(IMyLog)) as IMyLog; 

लेकिन 'हल (प्रकार टी, ...)' भी एक विस्तार विधि है ...

किसी भी:

मेरे मूल विचार 'DoJob' विधि के कार्यान्वयन और उपयोग को बदलने के लिए था विचारों का स्वागत है।

पीएस कृपया ध्यान दें, कि 'मेरा लॉग' ऑब्जेक्ट MyManager से दूर है। DoJob ...

+1

आईओसी कंटेनर पर निर्भरता एक एंटीपाटरन हैं। कंटेनर को एक शीर्ष-स्तरीय वर्ग तक सीमित करने का प्रयास करें और इसे अपनी अन्य वस्तुएं बनाएं। @TheCodeKing के सुझाव के अनुसार, ऑटो-कारखानों में सहायता मिल सकती है। Http://kozmic.pl/2010/06/20/how-i-use-inversion-of-control-containers/ – TrueWill

उत्तर

3

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

यह आवश्यकतानुसार करेगा।

 Mock<IMyLog> mockLog = new Mock<IMyLog>(); 
     mockLog.Setup(mock=>mock.Id).Returns(TestLogId); 

     IUnityContainer container = new UnityContainer(); 
     container 
      .RegisterInstance(mockCommandExecutionLog.Object) 
      ... 
      ; 

     ... 

     mockLog.Verify(
      mock => mock.Id, 
      Times.Once(), 
      "It seems like 'Log' object is not used" 
      ); 

धन्यवाद।

6

IUnityContainer पर निर्भरता को हटाएं और चीजें बहुत आसान और क्लीनर बन जाती हैं। इसके बजाय एकता अपनी निर्भरताओं को इंजेक्ट करने दें जो इंटरफेस में सारणीबद्ध हैं। ये आसानी से मजाक कर रहे हैं। यहां एकता के साथ एक छोटी सी चाल का उपयोग करके एक उदाहरण दिया गया है जो IMyLog के लिए एक ऑटो-फैक्ट्री इंजेक्ट करता है।

public class MyManager 
{ 
    private readonly Func<IMyLog> logFactory; 

    public MyManager(Func<IMyLog> logFactory) 
    { 
     this.logFactory = logFactory; 
    } 

    public IResult DoJob(IData data) 
    { 
     IMyLog log = logFactory(); 

     ... 
    } 
} 

या यदि आप हर बार उदाहरण बनाने की जरूरत नहीं है:

public class MyManager 
{ 
    private readonly IMyLog myLog; 

    public MyManager(IMyLog myLog) 
    { 
     this.myLog = myLog; 
    } 

    public IResult DoJob(IData data) 
    { 
     ... 
    } 
} 
+0

मेरे मामले में मुझे अन्य उद्देश्यों के लिए एकता की भी आवश्यकता है ... (मूल पोस्ट हाल ही में अपडेट किया गया था) । मैं शायद एक लॉग नहीं निकाल सकता लेकिन लॉग-फैक्ट्री निकाल सकता हूं और जांच सकता हूं कि यह 'GetLogObject' विधि कहलाता है ... विचार के लिए धन्यवाद। – Budda

1

मुझे दोनों उत्तरों से असहमत होना होगा। कोडकिंग, आईओसी इंटरफ़ेस का उपयोग करने के लिए यह पूरी तरह वैध है। एक उदाहरण ASP.NET प्रोजेक्ट में एक नियंत्रक फैक्ट्री हो सकता है - जहां कोई IUnityContainer इंटरफ़ेस पर एकाधिक विधियों का उपयोग करके गैर-तुच्छ समाधान कर सकता है।

हम्म ... ऑटो फैक्ट्री इंजेक्शन लैबडास के साथ, किसी को कभी भी आईओसी इंटरफ़ेस का परीक्षण करने की आवश्यकता नहीं होती है। आप बस एक लैम्ब्डा पास कर सकते हैं और सत्यापित कर सकते हैं कि इसे सही पैरामीटर के साथ बुलाया जाता है।

बुडा, आपको कभी अपने यूनिट परीक्षण में आईओसी कंटेनर लाने के लिए नहीं चाहिए। निर्भरताओं को मैन्युअल रूप से इंजेक्शन दिया जाना चाहिए।

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

public class MyManager 
{ 
    public MyManager(IDIContainer container) : base(container) { } 

    public IResult DoJob(IData data) 
    { 
     IMyLog log = MyContainer.Resolve<IMyLog>(); 

     ... use log.Id ... 

     MyContainer.Resolve<...>();//usage for other purposes... 
    } 
} 

और इसलिए तरह इकाई परीक्षण पुनर्लेखन:

public interface IDIContainer { 
    void RegisterType<TFrom>() where TFrom : class; 
    void RegisterType<TFrom, TTo>() where TTo : TFrom; 
    void RegisterType<TFrom, TTo>(string name) where TTo : TFrom; 
    void RegisterType(Type from, Type to); 
    void RegisterType(Type from, Type to, string name); 

    void RegisterInstance<TFrom>(TFrom instance) where TFrom : class; 

    T Resolve<T>(); 
    T Resolve<T>(string name); 
    IEnumerable<T> ResolveAll<T>(); 

    bool IsRegistered<TFrom>(string name) where TFrom : class; 
    bool IsRegistered<TFrom>() where TFrom : class; 
} 


public class DIContainer : IDIContainer { 
    IUnityContainer m_Container = new UnityContainer(); 

    #region IDIContainer Members 

    public void RegisterType<TFrom>() where TFrom : class { 
     m_Container.RegisterType<TFrom>(); 
    } 

    public void RegisterType<TFrom, TTo>() where TTo : TFrom { 
     m_Container.RegisterType<TFrom, TTo>(); 
    } 

    public void RegisterType<TFrom, TTo>(string name) where TTo : TFrom { 
     m_Container.RegisterType<TFrom, TTo>(name); 
    } 

    public void RegisterType(Type from, Type to) { 
     m_Container.RegisterType(from, to); 
    } 

    public void RegisterType(Type from, Type to, string name) { 
     m_Container.RegisterType(from, to, name); 
    } 

    public void RegisterInstance<TFrom>(TFrom instance) where TFrom : class { 
     m_Container.RegisterInstance<TFrom>(instance); 
    } 

    public T Resolve<T>() { 
     return m_Container.Resolve<T>(); 
    } 

    public IEnumerable<T> ResolveAll<T>() { 
     return m_Container.ResolveAll<T>(); 
    } 

    public T Resolve<T>(string name) { 
     return m_Container.Resolve<T>(name); 
    } 

    public bool IsRegistered<TFrom>(string name) where TFrom : class { 
     return m_Container.IsRegistered<TFrom>(name); 
    } 

    public bool IsRegistered<TFrom>() where TFrom : class { 
     return m_Container.IsRegistered<TFrom>(); 
    } 

    #endregion 
} 

अब, IDIContainer उपयोग करने के लिए अपनी कक्षा को फिर से लिखने

[TestClass] 
public class Test { 
    [TestMethod] 
    public void TestDoJob() { 
    Mock<IMyLog> mockLog = new Mock<IMyLog>(); 
    Mock<IDIContainer> containerMock = new Mock<IDIContainer>(); 

    //Setup mock container to return a log mock we set up earlier 
    containerMock.Setup(c=>c.Resolve<IMyLog>()),Returns(mockLog); 
    //Verify that all setups have been performed 
    containerMock.VerifyAll(); 
    } 
} 
+0

मैं यह नहीं कह रहा था कि आईओसी का उपयोग करना वैध नहीं है, बस यह प्रत्यक्ष निर्भरताओं के साथ परीक्षण करना आसान बनाता है। कारखाना परिदृश्य वास्तव में मेरे उदाहरण में उपयोग किया गया था। इसे आईओसी के संदर्भ की आवश्यकता के बिना एकता की ऑटो फैक्ट्री सुविधा का उपयोग करके हासिल किया जा सकता है। – TheCodeKing

+0

इगोर, धन्यवाद, यह वास्तव में एक अच्छा समाधान है। धन्यवाद! – Budda

+0

@TheCodeKing बस अपनी पोस्ट को फिर से पढ़ें। मुझे ऑटो फैक्ट्री समर्थन के बारे में पता नहीं था। 'इंजेक्शन फैक्ट्री' के साथ मिलकर आप कंटेनर इंजेक्ट करने की आवश्यकता को दूर कर सकते हैं। मैं इसे सब वापस लेता हूं :) –

6

मैं जानता हूँ कि मैं पार्टी के लिए देर हो रही है, लेकिन मैं एक ही समस्या थी, और निम्नलिखित विधि का मज़ाक उड़ाकर इसे हल किया -

IUnityContainer.Resolve(Type t, string name, params ResolverOverride[] resolverOverrides); 

उदाहरण के लिए -

unityMock = new Mock<IUnityContainer>(MockBehavior.Strict); 
validatorMock = new Mock<IValidator>(MockBehavior.Strict); 
unityMock.Setup(p => p.Resolve(typeof(IValidator), null)) 
    .Returns(validatorMock.Object); 

शायद ज़रुरत पड़े कोई इस बाहर उपहास करने के लिए की जरूरत है और कंटेनर पर निर्भरता नहीं निकाल सकते।

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