डोमेन ऑब्जेक्ट के निर्माण का परीक्षण करने के लिए यूनिट टेस्ट


11

मेरा एक यूनिट टेस्ट है, जो इस तरह दिखता है:

[Test]
public void Should_create_person()
{
     Assert.DoesNotThrow(() => new Person(Guid.NewGuid(), new DateTime(1972, 01, 01));
}

मैं दावा कर रहा हूं कि एक व्यक्ति वस्तु यहां बनाई गई है यानी सत्यापन विफल नहीं होता है। उदाहरण के लिए, यदि गाइड अशक्त है या जन्मतिथि 01/01/1900 से पहले है, तो सत्यापन विफल हो जाएगा और एक अपवाद फेंक दिया जाएगा (जिसका अर्थ है कि परीक्षण विफल)।

कंस्ट्रक्टर इस तरह दिखता है:

public Person(Id id, DateTime dateOfBirth) :
        base(id)
    {
        if (dateOfBirth == null)
            throw new ArgumentNullException("Date of Birth");
        elseif (dateOfBith < new DateTime(1900,01,01)
            throw new ArgumentException("Date of Birth");
        DateOfBirth = dateOfBirth;
    }

क्या यह एक परीक्षण के लिए एक अच्छा विचार है?

नोट : मैं किसी भी असर रखने वाले डोमेन मॉडल के यूनिट परीक्षण के लिए एक क्लासिकिस्ट दृष्टिकोण का पालन कर रहा हूं।


क्या निर्माणकर्ता के पास कोई ऐसा तर्क है जो आरंभीकरण के दौरान मुखर होने के लायक है?
लैवि

2
परीक्षण निर्माताओं को कभी परेशान मत करो !!! निर्माण सीधे आगे होना चाहिए। क्या आप उम्मीद कर रहे हैं कि Guide.NewGuid (), या DateTime का निर्माता विफल हो रहा है?
इवैंक्सु

@Laiv, कृपया प्रश्न का अद्यतन देखें।
w0051977

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

3
परीक्षण ठीक लग रहा है, एक चीज़ के लिए सहेजें: नाम। Should_create_person? एक व्यक्ति को क्या बनाना चाहिए? इसे एक सार्थक नाम दें, जैसे Creating_person_with_valid_data_succeeds
डेविड अर्नो

जवाबों:


18

यह एक वैध परीक्षण है (हालांकि बल्कि अति उत्साही) और मैं कभी-कभी निर्माण तर्क का परीक्षण करने के लिए भी करता हूं, हालांकि जैसा कि Laiv ने टिप्पणियों में उल्लेख किया है कि आपको खुद से क्यों पूछना चाहिए।

यदि आपका निर्माता इस तरह दिखता है:

public Person(Guid guid, DateTime dob)
{
  this.Guid = guid;
  this.Dob = dob;
}

क्या यह परीक्षण में बहुत सारे बिंदु हैं कि क्या यह फेंकता है? क्या मापदंडों को सही ढंग से सौंपा गया है, मैं समझ सकता हूं लेकिन आपका परीक्षण बल्कि ओवरकिल है।

हालाँकि, यदि आपका परीक्षण कुछ ऐसा करता है:

public Person(Guid guid, DateTime dob)
{
  if(guid == default(Guid)) throw new ArgumentException("Guid is invalid");
  if(dob == default(DateTime)) throw new ArgumentException("Dob is invalid");

  this.Guid = guid;
  this.Dob = dob;
}

तब आपका परीक्षण अधिक प्रासंगिक हो जाता है (जैसा कि आप वास्तव में कोड में कहीं अपवाद छोड़ रहे हैं)।

एक बात मैं कहूंगा, आम तौर पर आपके निर्माता में बहुत सारे तर्क होने के लिए यह बुरा अभ्यास है। मूल सत्यापन (जैसा कि मैं ऊपर कर रहा हूँ / डिफ़ॉल्ट चेक) ठीक है। लेकिन अगर आप डेटाबेस से जुड़ रहे हैं और किसी के डेटा को लोड कर रहे हैं, तो यह वह जगह है जहां कोड वास्तव में गंध करना शुरू कर देता है ...

इस वजह से, यदि आपका कंस्ट्रक्टर परीक्षण के लायक है (क्योंकि वहां बहुत तर्क चल रहे हैं) तो शायद कुछ और गलत है।

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

आपके उदाहरण में:

public Person(Id id, DateTime dateOfBirth) :
        base(id)
    {
        if (dateOfBirth == null)
            throw new ArgumentNullException("Date of Birth");
        elseif (dateOfBith < new DateTime(1900,01,01)
            throw new ArgumentException("Date of Birth");
        DateOfBirth = dateOfBirth;
    }

आप न केवल सत्यापन कर रहे हैं, बल्कि आप एक बेस कंस्ट्रक्टर भी कह रहे हैं। मेरे लिए यह इन परीक्षणों के लिए अधिक कारण प्रदान करता है क्योंकि उनके पास निर्माणकर्ता / सत्यापन तर्क है जो अब दो वर्गों में विभाजित हो गए हैं जो दृश्यता कम हो जाती है और अप्रत्याशित परिवर्तन का खतरा बढ़ जाता है।

TLDR

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


@Laith, कृपया मेरे प्रश्न का अद्यतन देखें
w0051977

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

"हालांकि, अगर आपका परीक्षण कुछ इस तरह करता है:" <क्या आपका मतलब नहीं है "अगर आपका निर्माता इस तरह से कुछ करता है" ?
कोडोस जॉनसन

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

12

पहले से ही यहां एक अच्छा जवाब है, लेकिन मुझे लगता है कि एक अतिरिक्त बात ध्यान देने योग्य है।

जब कर TDD "पुस्तक से", एक की जरूरत है जो निर्माता कहता है, पहले भी निर्माता को लागू किया जाता है एक परीक्षण पहले लिखने के लिए। यह परीक्षण वास्तव में आपके द्वारा प्रस्तुत किए गए की तरह लग सकता है, भले ही निर्माता के कार्यान्वयन के अंदर शून्य सत्यापन तर्क हो।

यह भी ध्यान दें कि टीडीडी के लिए, व्यक्ति को पहले की तरह एक और परीक्षण लिखना चाहिए

  Assert.Throws<ArgumentException>(() => new Person(Guid.NewGuid(), 
        new DateTime(1572, 01, 01));

DateTime(1900,01,01)कंस्ट्रक्टर के लिए चेक जोड़ने से पहले

टीडीडी संदर्भ में, दिखाया गया परीक्षण पूरी तरह से समझ में आता है।


अच्छा कोण मैं नहीं माना जाएगा!
Liath

1
यह मेरे लिए प्रदर्शित करता है कि टीडीडी का इतना कठोर रूप समय की बर्बादी है: कोड लिखे जाने के बाद परीक्षण का मूल्य होना चाहिए , या आप कोड की प्रत्येक पंक्ति को दो बार लिख रहे हैं, एक बार एक बार, और एक बार कोड के रूप में। मेरा तर्क है कि निर्माणकर्ता स्वयं तर्क का एक टुकड़ा नहीं है जिसे परीक्षण की आवश्यकता है; व्यवसाय नियम "1900 से पहले पैदा हुए लोगों को प्रतिनिधित्व योग्य नहीं होना चाहिए" टेस्ट करने योग्य है, और निर्माणकर्ता वह है जहां वह नियम लागू होता है, लेकिन एक खाली कंस्ट्रक्टर का परीक्षण कभी भी परियोजना में मूल्य कैसे जोड़ सकता है?
IMSoP

क्या यह वास्तव में पुस्तक द्वारा tdd है? मैं उदाहरण बनाऊंगा और तुरंत एक कोड में इसकी विधि को कॉल करूंगा। फिर मैं उस विधि के लिए परीक्षण लिखूंगा, और ऐसा करने से मुझे उस विधि के लिए उदाहरण भी बनाना होगा, इसलिए उस परीक्षण में निर्माता और विधि दोनों को शामिल किया जाएगा। जब तक निर्माणकर्ता में कुछ तर्क नहीं है, लेकिन वह हिस्सा लिआथ द्वारा कवर किया गया है।
राफेल żużyński

@ Rafał "użyński: TDD "पुस्तक" पहले परीक्षण लिखने के बारे में है । यह वास्तव में हमेशा एक असफल परीक्षा पहले लिखने का मतलब है (गणना के रूप में अच्छी तरह से विफलता के रूप में नहीं)। इसलिए आप सबसे पहले कंस्ट्रक्टर को बुलाते हुए एक परीक्षण लिखें , जब कोई कंस्ट्रक्टर न हो । फिर आप संकलन करने की कोशिश करते हैं (जो विफल रहता है), फिर आप एक खाली कंस्ट्रक्टर लागू करते हैं, संकलन करते हैं, परीक्षण चलाते हैं, परिणाम = हरा। फिर आप पहली असफल परीक्षा लिखते हैं और इसे चलाते हैं - परिणाम = लाल, फिर आप परीक्षण को "हरा" बनाने के लिए फिर से कार्यक्षमता जोड़ते हैं, और इसी तरह।
डॉक ब्राउन

बेशक। मेरा मतलब यह नहीं था कि मैं पहले कार्यान्वयन लिखता हूं, फिर परीक्षण करता हूं। मैं उस कोड के "उपयोग" को ऊपर के स्तर पर लिखता हूं, फिर उस कोड के लिए परीक्षण करता हूं, फिर मैं इसे लागू करता हूं। मैं आमतौर पर "आउटसाइड टीडीडी" कर रहा हूं।
राफेल żużyński
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.