6

मैं अपने नवीनतम एएसपी.नेट एमवीसी प्रोजेक्ट का निर्माण करते समय यूनिट टेस्टिंग, निर्भरता इंजेक्शन और जैज़ में जा रहा हूं।आईओसी कंटेनर के बिना आप अपने नियंत्रकों को यूनिट का परीक्षण कैसे कर सकते हैं?

मैं अब उस बिंदु पर हूं जहां मैं अपने नियंत्रकों को यूनिट परीक्षण करना चाहता हूं और मुझे यह पता लगाने में कठिनाई हो रही है कि आईओसी कंटेनर के बिना उचित तरीके से ऐसा कैसे किया जाए। उदाहरण के लिए

लें एक सरल नियंत्रक:

public class QuestionsController : ControllerBase 
{ 
    private IQuestionsRepository _repository = new SqlQuestionsRepository(); 

    // ... Continue with various controller actions 
} 

इस वर्ग SqlQuestionsRepository के अपने प्रत्यक्ष इन्स्टेन्शियशन की वजह से बहुत इकाई परीक्षण योग्य नहीं है। तो, निर्भरता इंजेक्शन मार्ग नीचे जाने दें और करें:

public class QuestionsController : ControllerBase 
{ 
    private IQuestionsRepository _repository; 

    public QuestionsController(IQuestionsRepository repository) 
    { 
     _repository = repository; 
    } 
} 

यह बेहतर लगता है। अब मैं आसानी से एक नकली IQuestionsRepository के साथ यूनिट परीक्षण लिख सकता हूं। हालांकि, अब नियंत्रक को तुरंत चालू करने जा रहा है क्या? कहीं भी कॉल चेन SqlQuestionRepository को तत्काल चालू किया जा रहा है। ऐसा लगता है कि मैंने बस कहीं और समस्या को स्थानांतरित कर दिया है, इससे छुटकारा नहीं मिला है।

अब, मुझे पता है कि यह एक अच्छा उदाहरण है जहां एक आईओसी कंटेनर मेरे लिए नियंत्रक निर्भरता को तारों में मदद कर सकता है जबकि साथ ही मेरे नियंत्रक को आसानी से इकाई परीक्षण योग्य रखता है।

मेरा सवाल यह है कि, इस प्रकृति की चीजों पर बिना आईओसी कंटेनर के यूनिट परीक्षण करने का अनुमान है?

नोट: मैं आईओसी कंटेनर का विरोध नहीं कर रहा हूं, और मैं जल्द ही उस सड़क पर जाउंगा। हालांकि, मैं उत्सुक हूं कि विकल्प उन लोगों के लिए क्या है जो उनका उपयोग नहीं करते हैं।

उत्तर

2

क्या क्षेत्र के प्रत्यक्ष तत्कालता को रखना और सेटटर प्रदान करना संभव नहीं है? इस मामले में आप केवल यूनिट परीक्षण के दौरान सेटटर को कॉल करेंगे। कुछ इस तरह:

public class QuestionsController : ControllerBase 
{ 
    private IQuestionsRepository _repository = new SqlQuestionsRepository(); 

    // Really only called during unit testing... 
    public QuestionsController(IQuestionsRepository repository) 
    { 
     _repository = repository; 
    } 
} 

मैं नेट के साथ भी परिचित नहीं हूँ, लेकिन जावा में एक पक्ष नोट के रूप में यह एक आम तरीका testability सुधार करने के लिए मौजूदा कोड refactor करने के लिए है। आईई, यदि आपके पास कक्षाएं हैं जो पहले से ही उपयोग में हैं और उन्हें संशोधित करने की आवश्यकता है ताकि मौजूदा कार्यक्षमता को तोड़ने के बिना कोड कवरेज में सुधार किया जा सके।

हमारी टीम ने पहले यह किया है, और आम तौर पर हम सेटटर की दृश्यता को पैकेज-प्राइवेट पर सेट करते हैं और टेस्ट क्लास के पैकेज को वही रखते हैं ताकि वह सेटर को कॉल कर सके।

+0

@ पीटर, बहुत अच्छा सुझाव। क्या आपको अपनी परियोजनाओं पर आईओसी कंटेनरों का उपयोग करने का कोई अनुभव है या क्या आपको अभी तक आवश्यकता नहीं मिली है? – mmcdole

+0

मेरे पास बहुत अधिक .NET अनुभव नहीं है, मेरा अधिकांश काम स्प्रिंग फॉर आईओसी का उपयोग कर जावा के साथ है। यह मॉड्यूलरिटी में सुधार के लिए बहुत उपयोगी रहा है। – Peter

+0

@ पीटर: यह काफी पैटर्न है जिसका मैं उपयोग करता हूं। मैंने एक उत्तर पोस्ट किया जो एक ही काम करता है, लेकिन कुछ सी # भाषा विशेषताओं का उपयोग करता है जो आप जावा लड़के होने के बारे में जानते हैं। –

2

आपके पास अपने नियंत्रक के साथ एक डिफ़ॉल्ट कन्स्ट्रक्टर हो सकता है जिसमें कुछ प्रकार का डिफ़ॉल्ट व्यवहार होगा।

कुछ की तरह ...

public QuestionsController() 
    : this(new QuestionsRepository()) 
{ 
} 

डिफ़ॉल्ट रूप से इस तरह जब नियंत्रक कारखाने नियंत्रक यह डिफ़ॉल्ट निर्माता के व्यवहार का उपयोग करेगा का एक नया उदाहरण पैदा कर रही है। फिर आपके यूनिट परीक्षणों में आप अन्य कन्स्ट्रक्टर में नकली में जाने के लिए एक मॉकिंग फ्रेमवर्क का उपयोग कर सकते हैं।

1

एक विकल्प नकली का उपयोग करना है।

public class FakeQuestionsRepository : IQuestionsRepository { 
    public FakeQuestionsRepository() { } //simple constructor 
    //implement the interface, without going to the database 
} 

[TestFixture] public class QuestionsControllerTest { 
    [Test] public void should_be_able_to_instantiate_the_controller() { 
     //setup the scenario 
     var repository = new FakeQuestionsRepository(); 
     var controller = new QuestionsController(repository); 
     //assert some things on the controller 
    } 
} 

अन्य विकल्प मॉक्स और मॉकिंग फ्रेमवर्क का उपयोग करना है, जो इन मोक्सों को फ्लाई पर स्वचालित रूप से उत्पन्न कर सकता है।

[TestFixture] public class QuestionsControllerTest { 
    [Test] public void should_be_able_to_instantiate_the_controller() { 
     //setup the scenario 
     var repositoryMock = new Moq.Mock<IQuestionsRepository>(); 
     repositoryMock 
      .SetupGet(o => o.FirstQuestion) 
      .Returns(new Question { X = 10 }); 
     //repositoryMock.Object is of type IQuestionsRepository: 
     var controller = new QuestionsController(repositoryMock.Object); 
     //assert some things on the controller 
    } 
} 

सभी वस्तुओं का निर्माण कहां से मिलता है। एक यूनिट टेस्ट में, आप केवल ऑब्जेक्ट्स का एक न्यूनतम सेट सेट करते हैं: एक वास्तविक ऑब्जेक्ट जो परीक्षण में है, और कुछ फ़ेक्ड या मॉक निर्भरताएं जो परीक्षण के तहत असली वस्तु की आवश्यकता होती है। उदाहरण के लिए, परीक्षण के तहत वास्तविक वस्तु QuestionsController का एक उदाहरण है - इसकी IQuestionsRepository पर निर्भरता है, इसलिए हम इसे पहले उदाहरण में नकली IQuestionsRepository या दूसरे उदाहरण की तरह नकली IQuestionsRepository देते हैं।

असली प्रणाली में, हालांकि, आपने सॉफ़्टवेयर के शीर्ष स्तर पर पूरे कंटेनर को स्थापित किया है। एक वेब अनुप्रयोग में, उदाहरण के लिए, आप GlobalApplication.Application_Start में सभी इंटरफेस और कार्यान्वयन कक्षाओं को तारों के साथ कंटेनर सेट अप करते हैं।

0

मैं पीटर के जवाब पर थोड़ा सा विस्तार कर रहा हूं।

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

public class QuestionsController : ControllerBase 
{ 
    private IQuestionsRepository Repository 
    { 
     get { return _repo ?? (_repo = IoC.GetInstance<IQuestionsRepository>()); } 
     set { _repo = value; } 
    } 
    private IQuestionsRepository _repo; 

    // Don't need anything fancy in the ctor 
    public QuestionsController() 
    { 
    } 
} 

जो कुछ वाक्य रचना अपने विशेष आईओसी फ्रेमवर्क का उपयोग करते साथ IoC.GetInstance<> बदलें।

उत्पादन के उपयोग में कुछ भी संपत्ति सेटर का आह्वान नहीं करेगा, इसलिए पहली बार गेटर को नियंत्रक कहा जाता है, आपके आईओसी ढांचे को कॉल करेगा, एक उदाहरण प्राप्त करें, और इसे स्टोर करें।

परीक्षण में, आप बस किसी भी नियंत्रक तरीकों लागू करने से पहले सेटर कॉल करने की आवश्यकता:

var controller = new QuestionsController { 
    Repository = MakeANewMockHoweverYouNormallyDo(...); 
} 

इस दृष्टिकोण का लाभ, IMHO:

  1. फिर भी उत्पादन में आईओसी का लाभ लेता है।
  2. परीक्षण के दौरान मैन्युअल रूप से अपने नियंत्रकों का निर्माण करने के लिए आसान है। आपको केवल उन निर्भरताओं को प्रारंभ करने की आवश्यकता है, जिन पर आपका परीक्षण वास्तव में उपयोग करेगा।
  3. यदि आप सामान्य निर्भरताओं को मैन्युअल रूप से कॉन्फ़िगर नहीं करना चाहते हैं तो परीक्षण-विशिष्ट आईओसी कॉन्फ़िगरेशन बनाना संभव है।
संबंधित मुद्दे