2011-11-09 32 views
5

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

वर्तमान में मैं जो उदाहरण के लिए एक छात्र के लिए निम्नलिखित बिल्डर का उत्पादन एक टी -4 टेम्पलेट है:

public class StudentBuilder : Builder<Student, StudentBuilder> 
{ 
    public StudentBuilder() 
    { 
     IsMale = true; 
    } 

    public StudentBuilder WithFirstName(string firstName) 
    { 
     this.FirstName = firstName; 
     return this; 
    } 

    public StudentBuilder WithLastName(string lastName) 
    { 
     this.LastName = lastName; 
     return this; 
    } 

    public StudentBuilder WithIsMale(bool isMale) 
    { 
     this.IsMale = isMale; 
     return this; 
    } 

    internal override Student Construct() 
    { 
     Student result = new Student() 
     { 
      FirstName = FirstName ?? "FirstName:" + id.ToString(), 
      LastName = LastName ?? "LastName:" + id.ToString(), 
      IsMale = IsMale, 
      Id = id, 
     }; 

    / return result; 
    } 
} 

गर्त आधार वर्ग मैं निम्नलिखित तरीके से इस का उपयोग कर सकते हैं:

Student wouter = StudentBuilder.Build() 
    .WithFirstName("Wouter") 
    .WithLastName("de Kort"); 
List<Student> students = StudentBuilder.Build().Multiple(10, (builder, index) => builder.WithFirstName("FirstName" + index)); 

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

उदाहरण के लिए, एक छात्र एक संरक्षक के लिए आवश्यक है, एक संरक्षक, एक स्कूल के अंतर्गत आता है एक शहर, एक करने के लिए एक शहर के लिए एक स्कूल ....

इस तरह कोड में परिणाम होगा:

StudentBuilder.Build().WithMentor(MentorBuilder.Build().WithSchool(SchoolBuilder.Build().WithCity(CityBuilder.Build())) 

मुझे इसे कैसे अनुकूलित करना चाहिए? मैंने प्रत्येक बिल्डर के निर्माण पद्धति में 'डिफ़ॉल्ट इमारत' करने के बारे में सोचा है, लेकिन अगर मैं 10 छात्रों का निर्माण करूंगा तो 10 में 10 शहरों में 10 स्कूलों में 10 सलाहकार होंगे ....

या शायद WithAllCity (..), WithAll (स्कूल)

कोई भी विचार बनाने के तरीके बनाना क्या मैं वास्तव में बिल्डर पैटर्न का सही तरीके से उपयोग कर रहा हूं? क्या एक निदेशक वर्ग मदद कर सकता है? या क्या मुझे छात्रबिल्डर से विरासत में कक्षाएं मिलनी चाहिए जो इन विभिन्न मामलों को हल करती हैं?

या कोई अन्य विचार, क्या मुझे डेटाबेस में डेटा भेजने से पहले मेरी सेवा परत में और अधिक सत्यापन जोड़ना चाहिए? फिर मैं मेमोरी डेटाबेस में अपने यूनिट परीक्षणों में और त्रुटियों को पकड़ूंगा।

+0

'हम विकास मशीनों पर और बिल्ड सर्वर पर डेटाबेस के खिलाफ स्मृति में हमारे यूनिट परीक्षण चलाते हैं।' - यह समस्या निवारण के लिए दर्दनाक लगता है। यह आपके प्रोजेक्ट के लिए कैसे काम कर रहा है? –

+0

मैं स्मृति में डेटाबेस से स्विच करने के लिए एकता अनुभाग के साथ विभिन्न app.config सेटिंग्स बनाने के लिए http://visualstudiogallery.msdn.microsoft.com/69023d00-a4f9-4a34-a6cd-7e854ba318b5 का उपयोग कर रहा हूं। डेवलपर वहां कॉन्फ़िगरेशन बनाने के लिए यूनिट परीक्षण डेटाबेस को लक्षित कर सकते हैं। यह विकास को गति देता है (स्मृति परीक्षणों में वास्तव में तेज़ी से चलता है) लेकिन यह भी उन बगों को पकड़ता है जिनमें नकली नकली पकड़ नहीं है। लेकिन वहां आपको तुरंत समस्या है कि मैं यह सवाल क्यों पूछ रहा हूं। परीक्षण सुनिश्चित करने के लिए वास्तव में एक लंबा बिल्डर सेटअप –

उत्तर

1

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

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

+0

समस्या यह है कि हम असली डेटाबेस (बिल्ड सर्वर पर एकीकरण परीक्षण) के खिलाफ भी हमारे यूनिट परीक्षण चला रहे हैं और डेटामोडेल को उन गुणों की आवश्यकता होती है। यह भी इसलिए है क्योंकि कभी-कभी उदाहरण के लिए लोग डेटटाइम प्रारंभ करना भूल जाते हैं जो SQL सर्वर में त्रुटि उत्पन्न करता है लेकिन स्मृति में नहीं। –

+1

अपनी इकाई और एकीकरण परीक्षण अलग करें। इकाई परीक्षण (इन-मेमोरी) विशिष्ट रखें। एकीकरण परीक्षण (डेटाबेस के साथ) सामान्य रखें, और एक पूर्ण डेटा सेट का उपयोग करें जो कई परीक्षणों के बीच साझा किया जाता है (जो मुझे पता है, वही है जो आप टालने का प्रयास कर रहे हैं)। साझा परीक्षण डेटा का उपयोग आसान बनाने के लिए http://stackoverflow.com/questions/482827 पर कुछ अच्छे सुझाव दिए गए हैं, लेकिन मुझे नहीं लगता कि आपको इसे एकीकरण परीक्षणों में पूरी तरह से टालने का प्रयास करना चाहिए। क्षमा करें अगर यह एक गैर-उत्तर है :) –

+0

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

0

यदि आप छात्रों की सूचियां बनाने जा रहे हैं तो आप सूची बिल्डर क्लास - छात्र बिल्डर बना सकते हैं। डिफ़ॉल्ट रूप से बिल्डर क्लास छात्रों की एक सूची उत्पन्न करेगी जो आपके द्वारा परिभाषित psuedo- यादृच्छिक गुण होंगे। यह AutoPoco के दृष्टिकोण के समान है।

मुझे लगता है कि सृजन व्यवहार को परिभाषित करने और किसी भी प्रकार के वर्ग का समर्थन करने के मामले में अपना खुद का list builder वर्ग बनाना अधिक लचीला है। मैं IList<T> फ़ील्ड्स के साथ एक बिल्डर क्लास बना देता हूं (एरेज़ (एसओए) दृष्टिकोण की डेटा उन्मुख संरचना के समान)।

public class StudentsBuilder 
{ 
    private int _size; 
    private IList<string> _firstNames; 
    private IList<string> _lastNames; 
    private IList<MentorBuilder> _mentors; 

    public StudentsBuilder(int size = 10) 
    { 
     _size = 10; 
     _firstNames = new RandomStringGenerator(size).Generate(); 
     _lastNames = new RandomStringGenerator(size).Generate(); 
     _mentors = Enumerable.Range(0, size).Select(_ => new MentorBuilder()).ToList(); 
    } 

    public StudentsBuilder WithFirstNames(params string[] firstNames) 
    { 
     _firstNames = firstNames; 
     return this; 
    } 

    public IList<Student> Build() 
    { 
     students = new List<Student>(); 
     for (int i = 0; i < size; i++) 
      students.Add(new Student(_firstNames[i], _lastNames[i], _mentors[i].Build()); 
     return students; 
    } 
} 

प्रत्येक क्षेत्र सूची एक अलग विधि एक पैरामीटर सरणी तर्क लेने का उपयोग कर ओवरराइड की गई है। ओवरराइडिंग मानों के लिए एक fancier With(Action<StudentsBuilder> action) वाक्यविन्यास का उपयोग करने के लिए आप क्षेत्र सूचियां भी सार्वजनिक कर सकते हैं। टेस्ट कोड इस तरह दिखता है:

var students = new StudentBuilder(size: 4) 
    .WithFirstNames("Jim", "John", "Jerry", "Judy") 
    .Build();