11

की सुविधा के लिए मेरे पास करने के लिए इकाई परीक्षण बुला container.Resolve < टी >() उनकी निर्भरता के लिए पर भरोसा नहीं कर कोशिश कर रहा हूँ।एक नेट यूनिट एक parameterless निर्माता के बिना टेस्ट, निर्भरता इंजेक्शन

मैं वर्तमान में AutoFac 2.2.4 का उपयोग कर रहा है, और करने की कोशिश की xUnit.NET और NUnit, लेकिन दोनों इस मुद्दे है:

कोई parameterless निर्माता इस वस्तु

के लिए परिभाषित

मैं इस मुद्दे को कैसे प्राप्त करूं? क्या यह एक विशेष इकाई परीक्षण ढांचा है जो इसका समर्थन करेगा, या सिर्फ फ्रेमवर्क कॉन्फ़िगर कैसे किया गया है?

क्या मुझे यह नहीं करना चाहिए? या क्या मैं कन्स्ट्रक्टर के साथ काम करने के लिए टेस्ट क्लास स्थापित कर सकता हूं जिसमें इसकी केवल निर्भरता है?

यहाँ कोड के कुछ है:

public class ProductTests : BaseTest 
{ 
    readonly private IProductRepository _repo; 

    public ProductTests(IProductRepository r) 
    { 
     _repo = r; 
    } 

    //working unit tests here with default constructor 
} 

मैं आधार वर्ग निर्माता में गलत तरीके से कंटेनर आरंभ करने के लिए चुना?

public abstract class BaseTest 
{ 
    protected BaseTest() 
    { 
     var builder = new ContainerBuilder(); 
     builder.RegisterType<ProductRepository>().As<IProductRepository>(); 
     builder.Build(); 
    } 
} 
+1

टेस्ट क्लास को कन्स्ट्रक्टर क्यों होना चाहिए? सेटअप विधि में "इंजेक्शन" रखें। –

उत्तर

11

प्रारंभिक समस्या वास्तव में परीक्षण ढांचे को कैसे डिजाइन किया गया है इस कारण है। टेस्ट इंस्टेंस को तुरंत चालू करने के लिए उन्हें सभी को पैरामीटर रहित कन्स्ट्रक्टर की आवश्यकता होती है। और ठीक है तो। इन ढांचे के साथ, कन्स्ट्रक्टर को टेस्ट प्रारंभिकरण के लिए भरोसा नहीं किया जाना चाहिए। यह SetUp विधि का उद्देश्य है। सब कुछ, टेस्ट कक्षाएं इंजेक्शन के लिए उपयुक्त नहीं हैं।

और आईएमओ, यह एक गैर-मुद्दा बन जाता है जब आप कंटेनर पर निर्भर नहीं होने के लिए अपने परीक्षण विकसित करते हैं। आखिरकार, प्रत्येक टेस्ट क्लास को "परीक्षण के तहत सिस्टम" (एसयूटी) पर ध्यान देना चाहिए। सेटअप विधि क्यों नहीं है कि उस प्रणाली को तुरंत चालू करें और प्रत्येक निर्भरता (आमतौर पर नकली रूपों के रूप में) प्रदान करें? ऐसा करने से आपने अपने परीक्षणों, अर्थात् आईओसी ढांचे से प्रभावी रूप से एक और अनावश्यक निर्भरता को हटा दिया है।

एक तरफ ध्यान दें: मेरे परीक्षणों में आईओसी ढांचे को शामिल करने का एकमात्र समय मेरे "कंटेनर परीक्षण" में है। ये परीक्षण यह सत्यापित करने पर ध्यान केंद्रित करते हैं कि कंटेनर को application or assembly modules के साथ प्रारंभ करने के बाद कुछ सेवाओं को कंटेनर से हल किया जा सकता है।

+0

+1। मैं पूरी तरह से पीटर से सहमत हूं। अपने परीक्षण विधियों में कंटेनर का उपयोग न करें, लेकिन मैन्युअल रूप से अपना एसयूटी बनाएं (या एक मॉकिंग फ्रेमवर्क का उपयोग करें)। – Steven

+1

+1 भी - यदि किसी घटक में परीक्षण की स्थिरता में हाथ से आराम से बनाए जाने के लिए बहुत अधिक निर्भरताएं हैं, तो इसकी बहुत ज़िम्मेदारियां हैं। –

+0

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

4

मैं अपने परीक्षणों को ऑटोफैक पर निर्भरता रखने की अनुमति देता हूं, हालांकि मैं इसे समाहित करता हूं। मेरे सभी टेस्टफिक्चर फिक्स्चर से प्राप्त होते हैं, जिन्हें इस प्रकार परिभाषित किया जाता है:

public class Fixture 
{ 
    private static readonly IContainer MainContainer = Ioc.Build(); 
    private readonly TestLifetime _testLifetime = new TestLifetime(MainContainer); 

    [SetUp] 
    public void SetUp() 
    { 
     _testLifetime.SetUp(); 
    } 

    [TearDown] 
    public void TearDown() 
    { 
     _testLifetime.TearDown(); 
    } 

    protected TService Resolve<TService>() 
    { 
     return _testLifetime.Resolve<TService>(); 
    } 

    protected void Override(Action<ContainerBuilder> configurationAction) 
    { 
     _testLifetime.Override(configurationAction); 
    } 
} 

public class TestLifetime 
{ 
    private readonly IContainer _mainContainer; 

    private bool _canOverride; 
    private ILifetimeScope _testScope; 

    public TestLifetime(IContainer mainContainer) 
    { 
     _mainContainer = mainContainer; 
    } 

    public void SetUp() 
    { 
     _testScope = _mainContainer.BeginLifetimeScope(); 
     _canOverride = true; 
    } 

    public void TearDown() 
    { 
     _testScope.Dispose(); 
     _testScope = null; 
    } 

    public TService Resolve<TService>() 
    { 
     _canOverride = false; 
     return _testScope.Resolve<TService>(); 
    } 

    public void Override(Action<ContainerBuilder> configurationAction) 
    { 
     _testScope.Dispose(); 

     if (!_canOverride) 
      throw new InvalidOperationException("Override can only be called once per test and must be before any calls to Resolve."); 

     _canOverride = false; 
     _testScope = _mainContainer.BeginLifetimeScope(configurationAction); 
    } 
}