2009-06-01 9 views
13

मैं इस जैसे एक आवेदन भंडार पैटर्न का उपयोग करता है, और फिर एक अलग सेवा परत तैयार किया है:टीडीडी - नकली रिपोजिटरी के साथ मेरी सेवा परत का परीक्षण करना चाहते हैं, लेकिन कैसे?

public class RegistrationService: IRegistrationService 
{ 
    public void Register(User user) 
    { 
     IRepository<User> userRepository = new UserRepository(); 
     // add user, etc 
    } 
} 

आप देख सकते हैं, मैं रजिस्टर विधि के अंदर मेरी भंडार instantiating कर रहा हूँ। अब जब मैं कुछ यूनिट परीक्षण लिखना चाहता हूं, तो मैं वास्तव में इसे प्राप्त नहीं कर सकता और नकली भंडार के साथ इसे प्रतिस्थापित कर सकता हूं?

मैं भंडार को क्लास वैरिएबल के रूप में जोड़ना नहीं चाहता हूं (और इसे एक कन्स्ट्रक्टर के माध्यम से सेट करें) क्योंकि मुझे लगता है कि मेरा कोड "सुगंधित" होगा (सभी विधियों के लिए सभी रिपॉजिटरीज की आवश्यकता नहीं है, और मैं नहीं ' भंडार आदि के बारे में जानने के लिए कॉलिंग परत की आवश्यकता नहीं है)।

सुझाव?

+1

आप सही रास्ते पर हैं, लेकिन कन्स्ट्रक्टर के माध्यम से निर्भरता में गुजरने से आपका कोड खराब नहीं होता है। यह एक स्वीकार्य डिजाइन प्रिंसिपल (नियंत्रण में उलटा) है। – ebrown

+0

यह डीआई के साथ यूनिट परीक्षण पर एक बहुत अच्छी बात है: http://www.youtube.com/watch?v=wEhu57pih5w –

उत्तर

16

आपको निर्भरता इंजेक्शन का उपयोग करने की आवश्यकता है। UserRepository आपकी पंजीकरण सेवा कक्षा की निर्भरता है। अपने वर्गों को उचित रूप से यूनिट टेस्टेबल बनाने के लिए (यानी उनकी निर्भरताओं के अलगाव में), आपको अपनी निर्भरता निर्माण को नियंत्रित करने वाले "उलटा" करने की आवश्यकता है। वर्तमान में, आपके पास प्रत्यक्ष नियंत्रण है, और उन्हें आंतरिक रूप से बना रहे हैं। बस ऐसे ही नियंत्रण को उलटने के लिए, और (कैसल विंडसर की तरह एक आईओसी कंटेनर के रूप में इस तरह के) कुछ बाहरी अनुमति देने के लिए उन्हें इंजेक्षन:

public class RegistrationService: IRegistrationService 
{ 
    public RegistrationService(IRepository<User> userRepo) 
    { 
     m_userRepo = userRepo; 
    } 

    private IRepository<User> m_userRepo; 

    public void Register(User user) 
    { 
     // add user, etc with m_userRepo 
    } 
} 

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

+0

मुझे लगता है कि यह तब जाने का तरीका है ... मैं अपनी आईरिपोजिटरी को कक्षा चर के रूप में नहीं रखना चाहता था, लेकिन मुझे लगता है कि इंजेक्शन के लिए यह जाने का तरीका है और फिर कन्स्ट्रक्टर को अधिभारित करना है। धन्यवाद! – Alex

+0

रेपो खुद को इंजेक्शन देने के बजाय, आप एक कारखाने इंजेक्ट कर सकते हैं, और उस से मांग पर रेपो बना सकते हैं। – jrista

0

आप एक आईओसी कंटेनर का उपयोग कर सकते हैं और फिर एक अलग भंडार रजिस्टर कर सकते हैं।

public class RegistrationService: IRegistrationService 
{ 
    public void Register(User user) 
    { 
     this(user, new UserRepository()); 
    } 

    public void Register(User user, IRepository<User> userRepository) 
    { 
     // add user, etc 
    } 

} 

इस तरह से आप की क्षमता अपने डिफ़ॉल्ट के रूप UserRepository उपयोग करने के लिए, और एक कस्टम में पारित (नकली या परीक्षण के लिए स्टब) प्राप्त IRepository:

9

निर्भरता इंजेक्शन वास्तव में चीजों के इन प्रकार के लिए काम में आ सकता है जो कुछ भी आपको चाहिए यदि आप इसे सब कुछ के लिए बेनकाब नहीं करना चाहते हैं तो आप दूसरी रजिस्टर विधि के स्कोपिंग को भी बदलना चाहेंगे।

एक आईओसी कंटेनर का उपयोग करने के लिए एक बेहतर विकल्प होगा जो आपको सिंटैक्स खरीद देगा जो उपर्युक्त उदाहरण से भी अधिक decoupled है। आप कुछ इस तरह हो सकता है:

public class RegistrationService: IRegistrationService 
{ 
    public void Register(User user) 
    { 
     this(user, IoC.Resolve<IRepository<User>>()); 
    } 

    public void Register(User user, IRepository<User> userRepository) 
    { 
     // add user, etc 
    } 

} 

कई आईओसी कंटेनर, वहाँ रहे हैं अन्य लोगों के जवाब के आधार पर सूचीबद्ध के रूप में, या आप roll your own सकता है।

+0

यह बहुत अच्छा लग रहा है लेकिन मुझे लगता है कि मुझे उपयोगकर्ता के लिए दूसरी विधि बनाने की आवश्यकता होगी पंजीकरण सेवा में हर विधि ... – Alex

+1

@Alex यह सही है। यदि आप ऐसा नहीं करना चाहते थे तो आप अपने कन्स्ट्रक्टर में रिपोजिटरी को वैकल्पिक रूप से इंजेक्ट कर सकते हैं, लेकिन आप अपने प्रश्न में उस से दूर रह रहे थे। – Joseph

+0

@Alex हालांकि, यह भी ध्यान रखें कि विधियों में से एक "डिफ़ॉल्ट" व्यवहार को परिभाषित करने के लिए केवल एक रैपर है, और इसमें कोई वास्तविक व्यवहार नहीं है। – Joseph

0

मेरा सुझाव अभी भी एक वर्ग पैरामीटर में भंडार को स्थानांतरित करना होगा, और आवश्यक होने पर भंडार इंजेक्ट करने के लिए एक आईओसी कंटेनर का उपयोग करना होगा। इस तरह, कोड सरल और टेस्टेबल है।

2

आपको क्या करना चाहिए नियंत्रण या निर्भरता इंजेक्शन का उपयोग करना है।

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

बहुत से आईओसी कंटेनर, एकता, निंजा, कैसल विंडसर, और संरचना मैप कुछ नाम हैं।


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

आपके यूनिट परीक्षण शायद 'शुद्ध' रहें और केवल उन वस्तुओं के साथ काम करें जिन्हें आप परीक्षण करने का प्रयास कर रहे हैं। कम से कम यही मेरे लिए काम करता है।

1

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

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

आईओसी कुछ मजेदार-प्यारा सामान है।

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

+0

आईओसी पर अतिरिक्त जानकारी के लिए धन्यवाद :) – Alex

1

मैं इस समय कुछ, अच्छी तरह से, लगभग क्रियात्मक पर काम कर रहा हूं। मैं अपने IUserDataRepository के प्रॉक्सी इंस्टेंस का नकल करने के लिए MOQ का उपयोग कर रहा हूं।

परीक्षण दृष्टिकोण से:

//arrange 
var mockrepository = new Mock<IUserDataRepository>(); 
// InsertUser method takes a MembershipUser and a string Role 
mockrepository.Setup(repos => repos.InsertUser(It.IsAny<MembershipUser>(), It.IsAny<string>())).AtMostOnce(); 

//act 
var service = new UserService(mockrepository.Object); 
var createResult = service.CreateUser(new MembershipUser(), "WebUser"); 

//assert 
Assert.AreEqual(MembershipCreateUserResult.Success, createResult); 
mockrepository.VerifyAll(); 

MOQ भी विशिष्ट अपवाद फेंकने के लिए और साथ ही इसलिए है कि मेरी UserService उन स्थितियों के तहत परीक्षण किया जा सकता इस्तेमाल किया जा सकता।

जैसे कि अन्य लोगों ने उल्लेख किया है कि आईओसी आवश्यक अवशेषों को संभालेगा। इस मामले में उपयोगकर्ता सेवा वर्ग के लिए एक वास्तविक भंडार बनाना।

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