2011-02-01 15 views
9

मैं सोच रहा हूं कि इस के आसपास कैसे जाना है। मैं निहिरनेट और धाराप्रवाह का उपयोग कर रहा हूँ।इकाई परीक्षण और निषेध?

मैं इस

public class User 
{ 
    public virtual int UserId {get; private set;} 
} 

की तरह एक डोमेन वर्ग इस जब nhibernate कर के रूप में यह स्थापित करने और आईडी के रूप में यह स्वचालित रूप से जेनरेट है से लोगों से बंद हो जाता सम्मेलन हो रहा है है।

अब समस्या तब आती है जब मैं इकाई परीक्षण करता हूं।

मेरे पास एक रेपो में मेरा निबर्ननेट कोड है जो मैं नकल करता हूं इसलिए मैं केवल अपनी सेवा परत का परीक्षण कर रहा हूं। समस्या तब होती है जब ऐसा होता है।

User user = repo.GetUser(email); 

यह उपयोगकर्ता ऑब्जेक्ट को वापस करना चाहिए।

तो मैं moq उपयोग करने के लिए इस

repo.Setup(x => x.GetUser(It.IsAny<string>())).Return(/* UserObject here */) 

क्या करना चाहते हैं अब यहाँ समस्या

मुझे लगता है कि उपयोगकर्ता वस्तु बनाने के लिए और वापसी हिस्से में डाल दिया की जरूरत है।

तो मैं जैसे

User user = new User() 
{ 
    UserId = 10, 
} 

कुछ करना होगा लेकिन यह वह जगह है जहाँ समस्या है मैं ईद स्थापित करने के लिए है क्योंकि मैं वास्तव में इसे बाद में उपयोग करें (कुछ संग्रह के बारे में कुछ LINQ करने के लिए के रूप में सेवा परत में की जरूरत है यह मेरे डीबी को मार नहीं रहा है, इसलिए यह मेरे रेपो में नहीं होना चाहिए) इसलिए मुझे इसे सेट करने की ज़रूरत है, लेकिन मैं इसे सेट नहीं कर सकता क्योंकि यह एक निजी सेट है।

तो मुझे क्या करना चाहिए? क्या मुझे सिर्फ निजी को हटा देना चाहिए या क्या कोई और तरीका है?

उत्तर

14

आप नकली Repository वस्तु एक नकली User ऑब्जेक्ट प्रदान हो सकता है:

var stubUser = new Mock<User>(); 
stubUser.Setup(s => s.UserId).Returns(10); 

var stubRepo = new Mock<IUserRepository>(); 
stubRepo.Setup(s => s.GetUser(It.IsAny<string>())).Return(stubUser); 

चीज़ें यहाँ निरीक्षण करने के लिए के एक जोड़े हैं:

  1. Moq कर सकते हैं ठोस वर्ग के केवल नकली सदस्यों यदि उन्हें आभासी के रूप में चिह्नित किया गया है। यह कुछ परिदृश्यों में लागू नहीं हो सकता है, इस मामले में मोक के माध्यम से नकली ऑब्जेक्ट का एकमात्र तरीका यह एक इंटरफेस को कार्यान्वित कर रहा है।
    हालांकि, इस मामले में, समाधान आलसी लोडिंग करने के लिए User कक्षा के गुणों पर NHibernate already imposes the same requirement अच्छी तरह से काम करता है।
  2. अन्य नकली लौटने वाली नकली वस्तुओं को कभी-कभी निर्दिष्ट यूनिट परीक्षणों का कारण बन सकता है। इन परिस्थितियों में, स्टब्स और मैक्स से बने समृद्ध ऑब्जेक्ट मॉडल का निर्माण उस बिंदु तक बढ़ता है जहां यह निर्धारित करना मुश्किल हो जाता है कि वास्तव में परीक्षण किया जा रहा है, परीक्षण स्वयं को पढ़ने योग्य और बनाए रखने के लिए कठिन बना रहा है। स्पष्ट होने के लिए यह एक पूरी तरह से ठीक इकाई परीक्षण अभ्यास है, लेकिन इसे जानबूझकर इस्तेमाल किया जाना चाहिए।

संबंधित संसाधन:

+0

धन्यवाद यह सिर्फ काम करता है पूरी तरह से। मुझे संख्या 2 के लक्षण हो सकते हैं। यह सिर्फ इतनी सारी चीजों का मज़ाक उड़ाता है। मैं इसे केवल इतना ही रखने की कोशिश कर रहा हूं कि मुझे परीक्षण करने की क्या ज़रूरत है। उदाहरण के लिए, मैं उपयोगकर्ता के लिए उपयोगकर्ता नाम का नकल नहीं करता क्योंकि इसका उपयोग विधि में नहीं किया जाता है। मैं autofixture moq का प्रयास करना चाहता हूं लेकिन मैं यह नहीं समझ सकता कि वास्तव में कैसे उपयोग किया जाए और ऐसा कोई वास्तविक ट्यूटोरियल नहीं लगता है, इसलिए मैं यह निर्धारित नहीं कर सकता कि इससे मेरी मदद मिलेगी या नहीं। – chobo2

2

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

असल में आप एक स्थिर नेस्टेड कक्षा के माध्यम से निजी सदस्य तक पहुंच प्रदान करते हैं जिसमें संपत्ति सेट करने के लिए एक विधि होती है।

public class User { 
    public int Id { get; private set; } 

    public static class Reveal { 
     public static void SetId(User user, int id) { 
      user.Id = id; 
     } 
    } 
} 

आप इस तरह इसका इस्तेमाल:

User user = new User(); 
User.Reveal.SetId(user, 43); 
बेशक

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

  • कोई Intellisense, संपत्ति सेटर या एक SetId() विधि
  • प्रोग्रामर स्पष्ट रूप से अजीब सिंटैक्स का उपयोग अनिवार्य Reveal वर्ग के साथ गुण सेट करने के लिए उत्साह जिससे उन्हें उत्साह है कि वे शायद इस
  • आप आसानी से स्थिर विश्लेषण Reveal वर्ग के उपयोगों पर क्या कोड मानक पहुंच पैटर्न

को दरकिनार कर रहा है आप केवल टी देख रहे हैं देखने के लिए प्रदर्शन कर सकते हैं कर नहीं किया जाना चाहिए o यूनिट परीक्षण उद्देश्यों के लिए एक निजी संपत्ति को संशोधित करें, और आप वस्तु को Moq करने में सक्षम हैं, फिर भी मैं एनरिको के सुझाव की सिफारिश करता हूं; लेकिन आप समय-समय पर इस तकनीक को उपयोगी पा सकते हैं।

+0

+1। बहुत सारे विकल्प - InternalsVisibleToAttribute, संरक्षित और subclass, प्रतिबिंब के साथ दरार, उचित चेतावनी पाठ के साथ ObsoleteAttribute के साथ उपयोगकर्ता पर SetId डाल, अधिभारित कन्स्ट्रक्टर (संभवतः मेरी प्राथमिकता) के लिए आईडी में पास करें, मुझे यह पसंद है क्योंकि यह परीक्षण के साथ प्रोत्साहित करता है Pocos/DTOs। जितना अधिक मैं मोक से प्यार करता हूं, @ एनरिको के अति-विनिर्देश के खतरों के बारे में अधिकार। – TrueWill

0

अपनी रिपॉजिटरी को नकल करने की कोशिश करने के बजाय, मैं सुझाव दूंगा कि आप परीक्षण के लिए इन-मेमोरी SQLite डेटाबेस का उपयोग करने का प्रयास करें। यह आपको वह गति देगा जो आप खोज रहे हैं और यह चीजों को भी बहुत आसान बना देगा। यदि आप एक कामकाजी नमूना देखना चाहते हैं तो आप मेरी गिटहब परियोजनाओं में से एक को देख सकते हैं: https://github.com/dlidstrom/GridBook

1

यदि आप अपनी इकाई कक्षाओं का नकल नहीं करना चाहते हैं तो एक और विकल्प है प्रतिबिंब का उपयोग करके निजी/संरक्षित आईडी सेट करना।

हां, मुझे पता है कि यह आमतौर पर बहुत अनुकूलता पर नहीं देखा जाता है, और अक्सर कहीं खराब डिजाइन के संकेत के रूप में उद्धृत किया जाता है। लेकिन इस मामले में, आपके एनएचबीर्नेट इकाइयों पर एक सुरक्षित आईडी होने के मानक मानक है, इसलिए यह एक काफी उचित समाधान लगता है।

हम इसे कम से कम अच्छी तरह से लागू करने का प्रयास कर सकते हैं। मेरे मामले में, मेरी 9 5% इकाइयां सभी एक सिंगल ग्रिड का उपयोग अद्वितीय पहचानकर्ता के रूप में करती हैं, केवल कुछ ही पूर्णांक का उपयोग करते हैं।इसलिए हमारे इकाई वर्गों आमतौर पर एक बहुत ही सरल HasID इंटरफ़ेस को लागू:

public interface IHasID<T> 
{ 
    T ID { get; } 
} 

एक वास्तविक इकाई वर्ग में, हम इसे इस तरह लागू हो सकता है:

public class User : IHasID<Guid> 
{ 
    Guid ID { get; protected set; } 
} 

इस आईडी में एक प्राथमिक कुंजी के रूप NHibernate करने के लिए मैप किया गया है सामान्य तरीके से।

हमारे इकाई परीक्षण में इस की स्थापना करने के लिए

, हम इस इंटरफ़ेस का उपयोग एक आसान विस्तार विधि प्रदान करने के लिए कर सकते हैं:

public static T WithID<T, K>(this T o, K id) where T : class, IHasID<K> 
{ 
    if (o == null) return o; 
    o.GetType().InvokeMember("ID", BindingFlags.SetProperty | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance, null, o, new object[] { id }); 
    return o; 
} 

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

विस्तार विधि को भी मूल वस्तु देता है, वैसे-वैसे उपयोग में मैं आम तौर पर सिर्फ यह निर्माता के अंत पर श्रृंखला:

var testUser = new User("Test User").WithID(new Guid("DC1BA89C-9DB2-48ac-8CE2-E61360970DF7")); 

या वास्तव में, के बाद से GUIDs के लिए मैं क्या आईडी वास्तव में परवाह नहीं है है, मैं एक और विस्तार विधि है:

public static T WithNewGuid<T>(this T o) where T : class, IHasID<Guid> 
{ 
    if (o == null) return o; 
    o.GetType().InvokeMember("ID", BindingFlags.SetProperty | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance, null, o, new object[] { Guid.NewGuid() }); 
    return o; 
} 

और उपयोग में:

var testUser = new User("Test User").WithNewGuid(); 
संबंधित मुद्दे