2010-04-12 15 views
10

मैंने ऑटोफिक्चर http://autofixture.codeplex.com/ का उपयोग शुरू किया क्योंकि मेरे यूनिट परीक्षणों को बहुत सारे डेटा सेटअप के साथ फूला गया था। मैं अपने यूनिट परीक्षण लिखने के बजाय डेटा सेट अप करने पर अधिक समय बिता रहा था। यहाँ कैसे मेरी प्रारंभिक इकाई परीक्षण (DDD नीला किताब से कार्गो आवेदन नमूना से लिया उदाहरण)ऑटोफिक्चर रीफैक्टरिंग

[Test] 
public void should_create_instance_with_correct_ctor_parameters() 
{ 
    var carrierMovements = new List<CarrierMovement>(); 

    var deparureUnLocode1 = new UnLocode("AB44D"); 
    var departureLocation1 = new Location(deparureUnLocode1, "HAMBOURG"); 
    var arrivalUnLocode1 = new UnLocode("XX44D"); 
    var arrivalLocation1 = new Location(arrivalUnLocode1, "TUNIS"); 
    var departureDate1 = new DateTime(2010, 3, 15); 
    var arrivalDate1 = new DateTime(2010, 5, 12); 

    var carrierMovement1 = new CarrierMovement(departureLocation1, arrivalLocation1, departureDate1, arrivalDate1); 

    var deparureUnLocode2 = new UnLocode("CXRET"); 
    var departureLocation2 = new Location(deparureUnLocode2, "GDANSK"); 
    var arrivalUnLocode2 = new UnLocode("ZEZD4"); 
    var arrivalLocation2 = new Location(arrivalUnLocode2, "LE HAVRE"); 
    var departureDate2 = new DateTime(2010, 3, 18); 
    var arrivalDate2 = new DateTime(2010, 3, 31); 

    var carrierMovement2 = new CarrierMovement(departureLocation2, arrivalLocation2, departureDate2, arrivalDate2); 

    carrierMovements.Add(carrierMovement1); 
    carrierMovements.Add(carrierMovement2); 

    new Schedule(carrierMovements).ShouldNotBeNull(); 
} 

यहाँ की तरह दिखता है का एक उदाहरण है कि कैसे मैं AutoFixture

साथ यह refactor करने के लिए
[Test] 
public void should_create_instance_with_correct_ctor_parameters_AutoFixture() 
{ 
    var fixture = new Fixture(); 

    fixture.Register(() => new UnLocode(UnLocodeString())); 

    var departureLoc = fixture.CreateAnonymous<Location>(); 
    var arrivalLoc = fixture.CreateAnonymous<Location>(); 
    var departureDateTime = fixture.CreateAnonymous<DateTime>(); 
    var arrivalDateTime = fixture.CreateAnonymous<DateTime>(); 

    fixture.Register<Location, Location, DateTime, DateTime, CarrierMovement>(
     (departure, arrival, departureTime, arrivalTime) => new CarrierMovement(departureLoc, arrivalLoc, departureDateTime, arrivalDateTime)); 

    var carrierMovements = fixture.CreateMany<CarrierMovement>(50).ToList(); 

    fixture.Register<List<CarrierMovement>, Schedule>((carrierM) => new Schedule(carrierMovements)); 

    var schedule = fixture.CreateAnonymous<Schedule>(); 

    schedule.ShouldNotBeNull(); 
} 

private static string UnLocodeString() 
{ 
    var stringBuilder = new StringBuilder(); 

    for (int i = 0; i < 5; i++) 
     stringBuilder.Append(GetRandomUpperCaseCharacter(i)); 

    return stringBuilder.ToString(); 
} 

private static char GetRandomUpperCaseCharacter(int seed) 
{ 
    return ((char)((short)'A' + new Random(seed).Next(26))); 
} 

मैं चाहूँगा करने की कोशिश की पता है कि इसे पुन: सक्रिय करने का बेहतर तरीका है या नहीं। इससे कम और आसान करना चाहते हैं।

उत्तर

14

आपका प्रारंभिक प्रयास अच्छा दिखता है, लेकिन कम से कम कुछ चीजें हैं जो आप थोड़ा सा सरल बना सकते हैं। जब से तुम उन अन्य चर उपयोग नहीं कर रहे

fixture.Register<Location, Location, DateTime, DateTime, CarrierMovement>(
    () => new CarrierMovement(departureLoc, arrivalLoc, departureDateTime, arrivalDateTime)); 

:

सबसे पहले, आप इस कम करने के लिए सक्षम होना चाहिए:

fixture.Register<Location, Location, DateTime, DateTime, CarrierMovement>(
    (departure, arrival, departureTime, arrivalTime) => 
     new CarrierMovement(departureLoc, arrivalLoc, departureDateTime, arrivalDateTime)); 
इस के लिए

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

के रूप में ऊपर उसी तरह से, के बजाय

fixture.Register<List<CarrierMovement>, Schedule>((carrierM) => 
    new Schedule(carrierMovements)); 

आप

fixture.Register(() => new Schedule(carrierMovements)); 

बारे में जब से तुम carrierM चर का उपयोग नहीं करते कर सकते हैं। टाइपिंग कॉन्फ़्रेंसिंग यह पता लगाएगी कि आप फनक के रिटर्न प्रकार की वजह से शेड्यूल पंजीकृत कर रहे हैं।

हालांकि, यह सोचते हैं कि अनुसूची निर्माता इस तरह दिखता है:

public Schedule(IEnumerable<CarrierMovement> carrierMovements) 

आप के बजाय सिर्फ इस तरह carrierMovements पंजीकृत किया जा सकता था:

fixture.Register<IEnumerable<CarrierMovement>>(carrierMovements); 

जो AutoFixture स्वचालित रूप से अनुसूची सही ढंग से हल करने के लिए कारण होगा। यह दृष्टिकोण अधिक रखरखाव योग्य है क्योंकि यह आपको परीक्षण को तोड़ने के बिना भविष्य में अनुसूची कन्स्ट्रक्टर में पैरामीटर जोड़ने की अनुमति देता है (जब तक ऑटोफिक्चर पैरामीटर प्रकार को हल कर सकता है)।

हालांकि, हम इस मामले में उससे बेहतर कर सकते हैं क्योंकि हम वास्तव में पंजीकरण से कुछ और के लिए carrierMovements चर का उपयोग नहीं करते हैं। हमें वास्तव में क्या करने की आवश्यकता है ऑटोफिक्चर को बताने के लिए IEnumerable<CarrierMovement> के उदाहरण कैसे बनाएं।

fixture.Register(fixture.CreateMany<CarrierMovement>); 

सूचना विधि मंगलाचरण parantheses की कमी: आप नंबर 50 के बारे में परवाह नहीं है (आप नहीं करना चाहिए), हम भी इस तरह विधि समूह सिंटैक्स का उपयोग कर सकते हैं कि हम एक समारोह पंजीकृत कर रहे हैं, और चूंकि CreateMany<T> विधि IEnumerable<T> टाइप कॉन्फ़्रेंसिंग बाकी का ख्याल रखती है।

हालांकि, ये सभी विवरण हैं।उच्च स्तर पर, आप कैरियरमोवमेंट को बिल्कुल पंजीकृत नहीं करने पर विचार करना चाहेंगे। इस कन्स्ट्रक्टर को मानते हुए:

public CarrierMovement(Location departureLocation, 
    Location arrivalLocation, 
    DateTime departureTime, 
    DateTime arrivalTime) 

ऑटोफिक्चर इसे स्वयं ही समझने में सक्षम होना चाहिए।

यह प्रत्येक प्रस्थान स्थान और आगमन स्थान के लिए एक नया स्थान उदाहरण तैयार करेगा, लेकिन यह मूल परीक्षण में मैन्युअल रूप से किए गए कार्यों से अलग नहीं है।

जब समय की बात आती है, डिफ़ॉल्ट रूप से ऑटोफिक्चर DateTime.Now का उपयोग करता है, जो कम से कम सुनिश्चित करता है कि आगमन का समय प्रस्थान समय से पहले कभी नहीं होगा। हालांकि, वे समान होने की संभावना है, लेकिन यदि आप एक समस्या है तो आप हमेशा एक ऑटो-वृद्धिशील फ़ंक्शन पंजीकृत कर सकते हैं।

उन कारणों को देखते हुए यहां एक विकल्प है:

public void should_create_instance_with_correct_ctor_parameters_AutoFixture() 
{ 
    var fixture = new Fixture(); 

    fixture.Register(() => new UnLocode(UnLocodeString())); 

    fixture.Register(fixture.CreateMany<CarrierMovement>); 

    var schedule = fixture.CreateAnonymous<Schedule>(); 

    schedule.ShouldNotBeNull(); 
} 

IList<CarrierMovement> के साथ इस मुद्दे आप इसे रजिस्टर करने की आवश्यकता होगी हल करने के लिए। यहाँ एक तरीका यह करना है:

fixture.Register<IList<CarrierMovement>>(() => 
    fixture.CreateMany<CarrierMovement>().ToList()); 

हालांकि, बाद से आप से पूछना, मैं मतलब है कि अनुसूची निर्माता इस तरह दिखता है:

public Schedule(IList<CarrierMovement> carrierMovements) 

और मैं वास्तव में लगता है कि तुम कि एपीआई को बदलने में कोई लेने के लिए पर पुनर्विचार करना चाहिए IEnumerable<Carriemovement>। किसी एपीआई डिज़ाइन परिप्रेक्ष्य से, किसी भी सदस्य (कन्स्ट्रक्टर समेत) के माध्यम से संग्रह की आपूर्ति करने का तात्पर्य है कि सदस्य को संग्रह को संशोधित करने की अनुमति है (उदा। इसे जोड़ने, निकालने और साफ़ करने के तरीकों का आह्वान करके)। यह शायद ही व्यवहार है कि आप एक निर्माता से अपेक्षा करेंगे, इसलिए इसे अनुमति न दें।

ऑटोफिक्स्चर स्वचालित रूप से मेरे उपरोक्त उदाहरण में सभी Location ऑब्जेक्ट्स के लिए नए मान उत्पन्न करेगा, लेकिन सीपीयू की गति के कारण, डेटटाइम के बाद के उदाहरण समान होने की संभावना है।

यदि आप डेटटाइम्स को बढ़ाना चाहते हैं, तो आप एक छोटी कक्षा लिख ​​सकते हैं जो हर बार लौटाए गए डेटटाइम को हर बार बढ़ाता है। (नोटिस एक बार फिर विधि समूह वाक्य रचना के ऊपर)

var dtg = new DateTimeGenerator(); 
fixture.Register(dtg.Next); 

इस एपीआई संभालने:

public class DateTimeGenerator 
{ 
    public DateTime Next(); 
} 
+0

मुझे कोई दिलचस्पी पाठक के लिए उस वर्ग के कार्यान्वयन छोड़ देंगे, लेकिन आप तो इतना है कि यह रजिस्टर कर सकता है आपकी टिप्पणियों के लिए आभार। हालांकि मेरे पास ऑटोफिक्चर Ploeh.AutoFixture.ObjectCreationException: ऑटोफिक्स्चर सिस्टम प्रकार का उदाहरण बनाने में असमर्थ था। Colections.Generic.IList'1 [DDDBookingApplication.Domain.Voyage.CarrierMovement], क्योंकि इसमें कोई सार्वजनिक नहीं है निर्माता। मुझे लगता है कि मुझे कैरियर मेवमेंट बनाने का तरीका कहना चाहिए? –

+0

मैं सभी इंस्टॉलेशन के लिए डेटा के अलग-अलग सेट भी रखना चाहता हूं। आपके विचार क्या हैं? –

+0

सभी विवरणों के लिए धन्यवाद। परीक्षण अब छोटा है और गुजरता है :) –

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