कोड में तार्किक गलतियों से कैसे बचें, जब TDD मदद नहीं करता है?


67

मैं हाल ही में एक छोटा सा कोड कोड लिख रहा था जो मानव-अनुकूल तरीके से इंगित करेगा कि कोई घटना कितनी पुरानी है। उदाहरण के लिए, यह इंगित कर सकता है कि घटना "तीन सप्ताह पहले" या "एक महीने पहले" या "कल" ​​हुई।

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

यहाँ कोड का प्रासंगिक टुकड़ा है:

now = datetime.datetime.utcnow()
today = now.date()
if event_date.date() == today:
    return "Today"

yesterday = today - datetime.timedelta(1)
if event_date.date() == yesterday:
    return "Yesterday"

delta = (now - event_date).days

if delta < 7:
    return _number_to_text(delta) + " days ago"

if delta < 30:
    weeks = math.floor(delta / 7)
    if weeks == 1:
        return "A week ago"

    return _number_to_text(weeks) + " weeks ago"

if delta < 365:
    ... # Handle months and years in similar manner.

परीक्षण आज, कल, चार दिन पहले, दो सप्ताह पहले, एक सप्ताह पहले, इत्यादि की घटना के मामले की जाँच कर रहे थे और कोड उसी के अनुसार बनाया गया था।

जो मैंने याद किया वह यह है कि एक घटना कल से एक दिन पहले हो सकती है, जबकि एक दिन पहले: उदाहरण के लिए छब्बीस घंटे पहले होने वाली घटना एक दिन पहले होगी, जबकि कल ठीक नहीं है, यदि अब 1 am अधिक है, तो यह एक बिंदु है कुछ है, लेकिन चूंकि deltaएक पूर्णांक है, यह सिर्फ एक होगा। इस मामले में, एप्लिकेशन "एक दिन पहले" प्रदर्शित करता है, जो स्पष्ट रूप से कोड में अप्रत्याशित और अखंडित है। इसे जोड़कर तय किया जा सकता है:

if delta == 1:
    return "A day ago"

कंप्यूटिंग के बाद बस delta

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

  • इस तरह के सरल स्रोत कोड में भी तार्किक गलती करना बहुत आसान है।
  • टेस्ट संचालित विकास में मदद नहीं मिली।

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

मैं पहली बार में इस बग को बनाने से कैसे बच सकता हूं?


38
इसके लिए एक परीक्षण मामला होने से? ऐसा लगता है कि आपने इसे बाद में कैसे खोजा, और टीडीडी के साथ मेष।
.urous

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

102
मेरे बाद दोहराएं: "टीडीडी सहित कोई भी चांदी की गोलियां नहीं हैं।" कोई प्रक्रिया नहीं है, नियमों का कोई सेट नहीं है, कोई एल्गोरिथ्म नहीं है जो आप रोबोट को सही कोड का उत्पादन करने के लिए अनुसरण कर सकते हैं। अगर वहाँ था, हम पूरी प्रक्रिया को स्वचालित कर सकते हैं और इसके साथ किया जा सकता है।
jpmc26

43
बधाई हो, आपने पुराने ज्ञान को फिर से खोज लिया है कि कोई भी परीक्षण बग की अनुपस्थिति को साबित नहीं कर सकता है। लेकिन यदि आप संभव इनपुट डोमेन का बेहतर कवरेज बनाने के लिए तकनीकों की तलाश कर रहे हैं, तो आपको डोमेन, किनारे के मामलों और उस डोमेन के समकक्ष वर्गों का गहन विश्लेषण करने की आवश्यकता है। टीडीडी शब्द का आविष्कार करने से पहले लंबे समय से ज्ञात सभी पुरानी, ​​प्रसिद्ध तकनीकें।
डॉक ब्राउन

80
मैं छींकने की कोशिश नहीं कर रहा हूं, लेकिन आपके प्रश्न को "मैं कैसे सोचूं कि चीजों के बारे में नहीं सोचा था?" के रूप में प्रकट किया जा सकता है। सुनिश्चित नहीं है कि टीडीडी के साथ क्या करना है।
जेरेड स्मिथ

जवाबों:


57

ये वे प्रकार की त्रुटियां हैं जो आप आमतौर पर लाल / हरे / रिफलेक्टर के रिफैक्टर चरण में पाते हैं । उस कदम को मत भूलना! निम्नलिखित (अप्राप्त) की तरह एक रिफ्लेक्टर पर विचार करें:

def pluralize(num, unit):
    if num == 1:
        return unit
    else:
        return unit + "s"

def convert_to_unit(delta, unit):
    factor = 1
    if unit == "week":
        factor = 7 
    elif unit == "month":
        factor = 30
    elif unit == "year":
        factor = 365
    return delta // factor

def best_unit(delta):
    if delta < 7:
        return "day"
    elif delta < 30:
        return "week"
    elif delta < 365:
        return "month"
    else:
        return "year"

def human_friendly(event_date):
    date = event_date.date()
    today = now.date()
    yesterday = today - datetime.timedelta(1)
    if date == today:
        return "Today"
    elif date == yesterday:
        return "Yesterday"
    else:
        delta = (now - event_date).days
        unit = best_unit(delta)
        converted = convert_to_unit(delta, unit)
        pluralized = pluralize(converted, unit)
        return "{} {} ago".format(converted, pluralized)

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

अन्य अधिक सूक्ष्म परीक्षण के मामले भी इस तरह से एक परिष्कृत रूप को देखते समय अधिक आसानी से ध्यान में आते हैं। उदाहरण के लिए, best_unitयदि deltaनकारात्मक हो तो क्या करना चाहिए ?

दूसरे शब्दों में, refactoring सिर्फ इसे सुंदर बनाने के लिए नहीं है। यह मनुष्यों के लिए संकलक त्रुटि नहीं कर सकता है।


12
अगला कदम अंतर्राष्ट्रीयकरण करना है, और pluralizeकेवल अंग्रेजी शब्दों के सबसेट के लिए काम करना एक दायित्व होगा।
Deduplicator

@Deduplicator सुनिश्चित है, लेकिन फिर आप किन भाषाओं / संस्कृतियों पर निर्भर करते हैं, आप केवल किसी तालिका / संसाधन फ़ाइल से एक प्रारूप स्ट्रिंग को खींचने के लिए किसी संशोधन pluralizeका उपयोग करके numऔर unitकिसी प्रकार की कुंजी बनाने के साथ दूर हो सकते हैं । या आपको तर्क के पूर्ण पुनर्लेखन की आवश्यकता हो सकती है, क्योंकि आपको विभिन्न इकाइयों की आवश्यकता है ;-)
हल्क

4
एक समस्या इस रिफ्लेक्टराइजेशन के साथ भी बनी हुई है, जो यह है कि "कल" ​​सुबह के बहुत मूतने वाले घंटों (12:01 बजे के तुरंत बाद) में ज्यादा मायने नहीं रखता है। मानवीय हितैषी शब्दों में, 11:59 बजे जो कुछ हुआ, वह अचानक "आज" से "कल" ​​में नहीं बदल जाता, जब घड़ी मध्यरात्रि से बीती। इसके बजाय यह "1 मिनट पहले" से "2 मिनट पहले" में बदल जाता है। "आज" बहुत कुछ है, लेकिन मिनटों पहले हुआ था, और "कल" ​​रात उल्लू की समस्याओं से भरा हुआ है।
डेविड हैमेन

@David Hammen यह एक प्रयोज्य मुद्दा है और यह इस बात पर निर्भर करता है कि आपको कितना सटीक होना चाहिए। जब आप कम से कम घंटे के बारे में जानना चाहते हैं, तो मुझे नहीं लगता कि "कल" ​​अच्छा है। "24 घंटे पहले" बहुत स्पष्ट है और घंटों की संख्या पर जोर देने के लिए आमतौर पर इस्तेमाल की जाने वाली मानवीय अभिव्यक्ति है। कंप्यूटर जो "मानव हितैषी" बनने की कोशिश कर रहे हैं वे लगभग हमेशा इस गलत हो जाते हैं और इसे "कल" ​​के लिए सामान्यीकृत करते हैं जो बहुत अस्पष्ट है। लेकिन यह जानने के लिए आपको उपयोगकर्ताओं को साक्षात्कार करने की आवश्यकता होगी कि वे क्या सोचते हैं। कुछ चीजों के लिए आप वास्तव में सटीक तारीख और समय चाहते हैं, इसलिए "कल" ​​हमेशा गलत होता है।
ब्रैंडिन

149

टेस्ट संचालित विकास में मदद नहीं मिली।

ऐसा लगता है कि यह मदद करता है, इसका सिर्फ इतना है कि आपके पास "एक दिन पहले" परिदृश्य के लिए एक परीक्षण नहीं था। संभवतः, आपने इस मामले के पाए जाने के बाद एक परीक्षण जोड़ा; यह अभी भी टीडीडी है, इसमें जब बग पाए जाते हैं तो आप बग का पता लगाने के लिए एक यूनिट-टेस्ट लिखते हैं, फिर इसे ठीक करते हैं।

यदि आप किसी व्यवहार के लिए परीक्षण लिखना भूल जाते हैं, तो TDD के पास आपकी मदद करने के लिए कुछ भी नहीं है; आप परीक्षण लिखना भूल जाते हैं और इसलिए कार्यान्वयन नहीं लिखते हैं।


2
कुछ भी हो सकता है कि यदि डेवलपर ने tdd का उपयोग नहीं किया है, तो वे अन्य मामलों को भी मिस करने की अधिक संभावना रखते हैं।
कालेब

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

15
टीडीडी केवल उतना ही अच्छा है जितना कि लिखे गए परीक्षण।
माइंडविन जू

एक और अवलोकन: इस मामले के लिए परीक्षण को जोड़ने से डिजाइन में सुधार होगा, जिससे हमें datetime.utcnow()फ़ंक्शन से बाहर निकालने के लिए मजबूर किया जा सकता है, और इसके बजाय now(प्रतिलिपि प्रस्तुत करने योग्य) तर्क के रूप में पारित करने के लिए।
टोबे स्पाइट

114

छब्बीस घंटे पहले होने वाली एक घटना एक दिन पहले की होगी

यदि कोई समस्या खराब परिभाषित की जाती है, तो टेस्ट बहुत मदद नहीं करेगा। आप जाहिर तौर पर कैलेंडर दिनों को घंटों में पूरा करने वाले दिनों के साथ मिला रहे हैं। यदि आप कैलेंडर दिनों से चिपके रहते हैं, तो 1 बजे, 26 घंटे पहले कल नहीं है । और यदि आप घंटों से चिपके रहते हैं, तो समय की परवाह किए बिना 26 घंटे पहले 1 दिन पहले राउंड करें।


45
यह बनाने के लिए एक महान बिंदु है। किसी आवश्यकता को मिस करने का मतलब यह नहीं है कि कार्यान्वयन के लिए आपकी प्रक्रिया विफल हो गई है। इसका मतलब सिर्फ इतना है कि आवश्यकता को अच्छी तरह से परिभाषित नहीं किया गया था। (या आपने बस एक मानवीय त्रुटि की है, जो समय-समय पर होगी)
कालेब

यह वह उत्तर है जो मैं बनाना चाहता था। मैं कल्पना को "इस घटना के दिन के रूप में प्रदर्शित करता हूं, तो यह निर्धारित करता हूं कि घंटों में डेल्टा मौजूद है। केवल डेल्टा का निर्धारण करने के लिए उपयोग की तारीखें" परीक्षण घंटे केवल एक दिन के भीतर उपयोगी है, यदि इससे परे आपका संकल्प दिन होने का है।
बाल्ड्रिक

1
मुझे यह उत्तर पसंद है क्योंकि यह वास्तविक समस्या को इंगित करता है: समय और तिथियां दो अलग-अलग मात्राएं हैं। वे संबंधित हैं लेकिन जब आप उनकी तुलना करना शुरू करते हैं, तो चीजें दक्षिण वास्तविक तेजी से चलती हैं। प्रोग्रामिंग में, तिथि और समय तर्क सही पाने के लिए सबसे कठिन चीजों में से कुछ है। मैं वास्तव में नापसंद करता हूं कि बहुत सारी तारीखें मूल रूप से तारीख को 0:00 बिंदु के रूप में संग्रहीत करती हैं। यह बहुत भ्रम पैदा करता है।
Pieter B

38

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

संबंधित पढ़ना: क्या बड़े पैमाने पर सॉफ़्टवेयर के लिए पूर्ण शून्य बग स्थिति तक पहुंचना संभव है?


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

35

दो दृष्टिकोण हैं जिन्हें मैं सामान्य रूप से लेता हूं जो मुझे लगता है कि मदद कर सकते हैं।

सबसे पहले, मैं किनारे के मामलों की तलाश करता हूं। ये ऐसे स्थान हैं जहां व्यवहार बदलता है। आपके मामले में, सकारात्मक पूर्णांक दिनों के अनुक्रम के साथ कई बिंदुओं पर व्यवहार बदलता है। शून्य पर एक किनारे का मामला है, एक पर, सात बजे, आदि मैं फिर किनारे के मामलों में और चारों ओर परीक्षण मामले लिखूंगा। मैं -1 दिन, 0 दिन, 1 घंटे, 23 घंटे, 24 घंटे, 25 घंटे, 6 दिन, 7 दिन, 8 दिन इत्यादि मामलों की जांच करवाता हूँ।

दूसरी चीज जो मैं देखूंगा वह है व्यवहार का पैटर्न। सप्ताह के लिए अपने तर्क में, आपके पास एक सप्ताह के लिए विशेष हैंडलिंग है। संभवतः आपके प्रत्येक अन्य अंतराल में समान तर्क है जो नहीं दिखाया गया है। यह तर्क दिनों के लिए मौजूद नहीं है , हालांकि। मैं उस पर संदेह की दृष्टि से देखूंगा, जब तक कि मैं या तो यह स्पष्ट रूप से नहीं बता सकता कि मामला अलग क्यों है, या मैं तर्क को इसमें जोड़ता हूं।


9
यह टीडीडी का एक बहुत ही महत्वपूर्ण हिस्सा है जिसे अक्सर अनदेखा किया जाता है और मैंने शायद ही कभी लेखों और गाइडों के बारे में बात की हो - किनारे के मामलों और सीमा की स्थितियों का परीक्षण करना वास्तव में महत्वपूर्ण है क्योंकि मुझे पता है कि 90% बग्स का स्रोत है -अधिक त्रुटियां, ओवर एंड अंडरफ्लो, महीने का आखिरी दिन, साल का आखिरी महीना, लीप-
ईयर

2
@GoatInTheMachine - और 90% उन 90% कीड़े दिन के उजाले बचत समय संक्रमण के आसपास हैं ..... हाहा
कालेब

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

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

14

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

लेकिन ध्यान रखें, बग को ठीक करने के दौरान, आपके द्वारा पहले से ही किए गए परीक्षण के मामलों में कोई मौजूदा, कामकाजी व्यवहार टूट गया था। यह काफी महत्वपूर्ण है, एक बग को ठीक करना आसान है लेकिन दूसरे को पेश करना है।

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

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

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

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

ध्यान दें, हालांकि, मैंने जो लिखा है, उसमें से अधिकांश का TDD के साथ बहुत कम संबंध है। तुल्यता वर्गों को लिखने और सुनिश्चित करने के बारे में कि आपके अपने विनिर्देश उनके बारे में पर्याप्त विस्तृत हैं। यही वह प्रक्रिया है जिसके साथ आप तार्किक त्रुटियों को कम करते हैं। TDD सुनिश्चित करता है कि आपका कोड आपके मानसिक मॉडल के अनुरूप हो।

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


12

एक ही तरीका है जिसके बारे में मैं सोच सकता हूँ कि जिन मामलों पर मेरा विश्वास है कि वे कभी नहीं होंगे (जैसे मुझे विश्वास था कि एक दिन पहले कल जरूरी है), और फिर पिछले दस वर्षों से हर दूसरे से लूप के लिए जाँच करना। कोई जोरदार उल्लंघन, जो बहुत जटिल लगता है।

क्यों नहीं? यह एक बहुत अच्छा विचार लगता है!

कोड में अनुबंध (अभिकथन) जोड़ना इसकी शुद्धता को सुधारने का एक बहुत ठोस तरीका है। आम तौर पर हम उन्हें के रूप में जोड़ पूर्व शर्त समारोह प्रवेश पर और postconditions समारोह वापसी पर। उदाहरण के लिए, हम एक ऐसा अनुमान जोड़ सकते हैं कि सभी लौटाया गया मान या तो "ए [यूनिट] पहले" या "[संख्या] [यूनिट] एस पहले" था। जब एक अनुशासित तरीके से किया जाता है, तो यह अनुबंध द्वारा डिजाइन की ओर जाता है , और उच्च-आश्वासन कोड लिखने के सबसे सामान्य तरीकों में से एक है।

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

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

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


2
इस तरह की समस्या का यह सही समाधान है। मान्य आउटपुट का सेट परिभाषित करना आसान है (आप बहुत ही सरलता से, कुछ ऐसा /(today)|(yesterday)|([2-6] days ago)|...कर सकते हैं ) एक नियमित अभिव्यक्ति दे सकते हैं और फिर आप प्रक्रिया को यादृच्छिक रूप से चयनित इनपुट के साथ चला सकते हैं जब तक कि आप एक ऐसा नहीं पाते हैं जो अपेक्षित आउटपुट के सेट में नहीं है। इस दृष्टिकोण को लेते हुए इस बग को पकड़ा जा सकता था, और यह महसूस करने की आवश्यकता नहीं होगी कि बग पहले से मौजूद हो सकता है।
जूल्स

@ जूल्स संपत्ति की जाँच / परीक्षण भी देखें । मैं आमतौर पर विकास के दौरान संपत्ति परीक्षण लिखता हूं, जितना संभव हो उतने अप्रत्याशित मामलों को कवर करने के लिए और मुझे सामान्य गुणों / आक्रमणकारियों के बारे में सोचने के लिए मजबूर करता है। मैं प्रतिगमन और इस तरह के लिए एक बंद परीक्षण (जो लेखक का मुद्दा का एक उदाहरण है) को बचाने
Warbo

1
यदि आप परीक्षणों में इतना लूपिंग करते हैं, यदि आपको बहुत लंबा समय लगेगा, जो इकाई परीक्षण के मुख्य लक्ष्यों में से एक को हरा देता है: परीक्षण तेजी से चलाएं !
सीजे डेनिस

5

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

def time_ago(delta, unit):
    delta_str = _number_to_text(delta) + " " + unit;
    if delta == 1:
        return delta_str + " ago"
    else:
        return delta_str = "s ago"

now = datetime.datetime.utcnow()
today = now.date()
if event_date.date() == today:
    return "Today"

yesterday = today - datetime.timedelta(1)
if event_date.date() == yesterday:
    return "Yesterday"

delta = (now - event_date).days

if delta < 7:
    return time_ago(delta, "day")

if delta < 30:
    weeks = math.floor(delta / 7)
    return time_ago(weeks, "week")

if delta < 365:
    months = math.floor(delta / 31)
    return time_ago(months, "month")

5

टेस्ट संचालित विकास में मदद नहीं मिली।

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

  • परीक्षण कार्यों के तहत फ़ंक्शन की पुष्टि करने के लिए परीक्षण न लिखें जैसा कि आपने इसे बनाया था। ऐसे परीक्षण लिखें जो जानबूझकर इसे तोड़ते हैं।

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

मजबूत सॉफ्टवेयर लिखने की मूल तकनीक, प्रभावी परीक्षण लिखने के तरीके को समझने की मुख्य तकनीक भी है:

किसी फ़ंक्शन के लिए पूर्व शर्त को समझें - मान्य स्टेट्स (यानी फ़ंक्शन की स्थिति के बारे में आप क्या धारणा बना रहे हैं) और मान्य इनपुट पैरामीटर रेंज - प्रत्येक डेटा प्रकार में संभावित मानों की एक सीमा होती है - जिनमें से एक सबसेट आपके कार्य को संभाला जाएगा।

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


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


1

यह सॉफ्टवेयर विकास के बारे में सबसे महत्वपूर्ण तथ्यों में से एक है: बग-मुक्त कोड लिखना बिल्कुल, बिल्कुल असंभव है।

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

यह कहना नहीं है कि आपको उम्मीद छोड़ देनी चाहिए। हालांकि पूरी तरह से सही कोड लिखना असंभव है, यह कोड लिखना बहुत संभव है जिसमें बहुत कम कीड़े हैं जो ऐसे दुर्लभ किनारे के मामलों में दिखाई देते हैं जो सॉफ्टवेयर का उपयोग करने के लिए बेहद व्यावहारिक है। सॉफ्टवेयर जो व्यवहार में छोटी गाड़ी के व्यवहार को प्रदर्शित नहीं करता है, उसे लिखना बहुत संभव है।

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

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

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


1

आपने पहले इस मामले के बारे में नहीं सोचा था और इसलिए इसके लिए एक परीक्षण मामला नहीं था।

यह हर समय होता है और सामान्य होता है। यह हमेशा एक व्यापार बंद है कि आप सभी संभावित परीक्षण मामलों को बनाने में कितना प्रयास करते हैं। सभी परीक्षण मामलों पर विचार करने के लिए आप अनंत समय बिता सकते हैं।

एक विमान ऑटोपायलट के लिए आप एक साधारण उपकरण की तुलना में बहुत अधिक समय खर्च करेंगे।

यह अक्सर आपके इनपुट चर की मान्य सीमाओं के बारे में सोचने और इन सीमाओं का परीक्षण करने में मदद करता है।

इसके अलावा, अगर टेस्टर डेवलपर से अलग व्यक्ति है, तो अक्सर अधिक महत्वपूर्ण मामले पाए जाते हैं।


1

(और यह मानते हुए कि यह कोड में UTC के समान उपयोग के बावजूद, समय क्षेत्र के साथ करना है)

आपके कोड में एक और तार्किक गलती है जिसके लिए आपके पास अभी तक एक इकाई परीक्षण नहीं है :) - गैर-यूटीसी टाइमज़ोन में उपयोगकर्ताओं के लिए आपका तरीका गलत परिणाम देगा। आपको गणना करने से पहले उपयोगकर्ता के स्थानीय समयक्षेत्र में "अब" और घटना की तारीख दोनों को बदलने की आवश्यकता है।

उदाहरण: ऑस्ट्रेलिया में, एक घटना स्थानीय समयानुसार सुबह 9 बजे होती है। सुबह 11 बजे इसे "कल" ​​के रूप में प्रदर्शित किया जाएगा क्योंकि यूटीसी की तारीख बदल गई है।


0
  • किसी और को परीक्षण लिखने दें। इस तरह आपके कार्यान्वयन से अपरिचित कोई व्यक्ति उन दुर्लभ स्थितियों की जाँच कर सकता है जिनके बारे में आपने नहीं सोचा था।

  • यदि संभव हो, परीक्षण मामलों को संग्रह के रूप में इंजेक्ट करें। यह एक और परीक्षा को जोड़ना आसान बनाता है जैसे कि एक और लाइन जोड़ना yield return new TestCase(...)। यह अन्वेषण मामलों की दिशा में जा सकता है , परीक्षण मामलों के निर्माण को स्वचालित करता है: "आइए देखें कि एक सप्ताह पहले सभी सेकंड के लिए कोड क्या रिटर्न देता है"।


0

आप इस गलत धारणा के तहत दिखाई देते हैं कि यदि आपके सभी परीक्षण पास हो गए हैं, तो आपके पास कोई बग नहीं है। वास्तव में, यदि आपके सभी परीक्षण पास हो जाते हैं, तो सभी ज्ञात व्यवहार सही हैं। आप अभी भी नहीं जानते कि अज्ञात व्यवहार सही है या नहीं।

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

इसका मतलब यह नहीं है कि आपका कोड बग मुक्त है, बस यह पहले से बेहतर है, और एक बार फिर सभी ज्ञात व्यवहार सही है!

TDD का सही तरीके से उपयोग करने का मतलब यह नहीं है कि आप बग फ्री कोड लिखेंगे, इसका मतलब है कि आप कम बग लिखेंगे। तुम कहो:

आवश्यकताएँ अपेक्षाकृत स्पष्ट थीं

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


0

इस तरह के सरल स्रोत कोड में भी तार्किक गलती करना बहुत आसान है।

हाँ। टेस्ट संचालित विकास वह नहीं बदलता है। आप अभी भी वास्तविक कोड में बग बना सकते हैं, और परीक्षण कोड में भी।

टेस्ट संचालित विकास में मदद नहीं मिली।

ओह, लेकिन यह किया! सबसे पहले, जब आपने बग पर ध्यान दिया था तो आपके पास पहले से ही पूरा परीक्षण ढांचा था, और बस परीक्षण (और वास्तविक कोड) में बग को ठीक करना था। दूसरी बात, आप यह नहीं जानते हैं कि अगर आपने शुरुआत में टीडीडी नहीं किया होता तो आपके पास कितने और कीड़े होते।

यह भी चिंताजनक है कि मैं यह नहीं देख सकता कि ऐसे कीड़े से कैसे बचा जा सकता है।

आप नहीं कर सकते। यहां तक ​​कि नासा ने बग से बचने का कोई तरीका नहीं पाया है; हम मनुष्य निश्चित रूप से कम या तो नहीं करते हैं।

कोड लिखने से पहले अधिक सोचने के अलावा,

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

एक ही तरीका है जिसके बारे में मैं सोच सकता हूं कि उन मामलों के लिए बहुत सारे दावे शामिल हैं जो मुझे लगता है कि ऐसा कभी नहीं होगा (जैसे कि मुझे विश्वास था कि एक दिन पहले जरूरी है कि कल हो), और फिर पिछले दस वर्षों से हर दूसरे के माध्यम से लूप के लिए जाँच करना। कोई जोरदार उल्लंघन, जो बहुत जटिल लगता है।

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

मिशन-क्रिटिकल कोड के लिए, आप वास्तव में वही कर सकते हैं जो आपने कहा था, लेकिन आपके रोजमर्रा के मानक कोड के लिए नहीं।

मैं पहली बार में इस बग को बनाने से कैसे बच सकता हूं?

तुम नहीं। आप अपने परीक्षणों पर भरोसा करते हैं ताकि अधिकांश प्रतिगमन मिल सकें; आप लाल-हरे-परावर्तक-चक्र में रहते हैं, वास्तविक कोडिंग से पहले / दौरान परीक्षण लिखते हैं, और (महत्वपूर्ण!) आप लाल-हरे स्विच (कम नहीं, कम नहीं) बनाने के लिए आवश्यक न्यूनतम राशि को लागू करते हैं। यह एक महान परीक्षण कवरेज के साथ समाप्त होगा, कम से कम एक सकारात्मक।

जब, यदि नहीं, तो आप एक बग ढूंढते हैं, आप उस बग को पुन: पेश करने के लिए एक परीक्षण लिखते हैं, और कहा जाता है कि बग को कम से कम काम के साथ बग को ठीक करने के लिए कहा जाए।


-2

आपने अभी-अभी पता लगाया है कि आप कितनी भी कोशिश कर लें, आप कभी भी अपने कोड में सभी संभावित कीड़े नहीं पकड़ पाएंगे।

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

इसका मतलब है कि आपको इन तकनीकों का उपयोग करके कम समय बिताना चाहिए, और विकास नेट के माध्यम से फिसलने वाली बगों को खोजने के लिए वैकल्पिक तरीकों पर काम करने में समय बचाने में खर्च करना चाहिए।

एकीकरण परीक्षण, या एक परीक्षण टीम, सिस्टम परीक्षण और लॉगिंग और उन लॉग का विश्लेषण जैसे विकल्प।

यदि आप सभी बग्स को नहीं पकड़ सकते हैं, तो आपके पास अतीत में फिसलने वाले कीड़ों के प्रभाव को कम करने के लिए एक रणनीति होनी चाहिए। यदि आपको इसे वैसे भी करना है, तो इसमें अधिक प्रयास करने से (व्यर्थ में) उन्हें पहले स्थान पर रोकने की कोशिश करने से अधिक समझ में आता है।

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


यह बेहद पराजय है। That in turn means you should spend less time using these techniques- लेकिन आपने अभी कहा कि यह कम कीड़े के साथ मदद करेगा ?!
J --M।

@ J @M which किस तकनीक का अधिक व्यावहारिक दृष्टिकोण है जो आपको अपने हिरन के लिए सबसे धमाकेदार लगता है। मैं ऐसे लोगों को जानता हूं जो इस बात पर गर्व करते हैं कि वे अपने कोड पर किए गए परीक्षण की तुलना में 10 गुना लेखन परीक्षण खर्च करते हैं, और वे अभी भी कीड़े हैं, इसलिए डोगमैटिक के बजाय समझदार हैं। परीक्षण तकनीकों के बारे में आवश्यक है। और एकीकरण परीक्षणों का उपयोग वैसे भी किया जाना है, इसलिए इकाई परीक्षण की तुलना में उनमें अधिक प्रयास करें।
gbjbaanb
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.