टीडीडी में री-डिजाइन के बाद जब विधि निजी हो जाती है, तो तरीकों के परीक्षणों के साथ क्या होता है?


29

मान लीजिए कि मैं पात्रों के साथ एक भूमिका खेल विकसित करना शुरू करता हूं जो अन्य पात्रों और उस तरह के सामान पर हमला करता है।

टीडीडी लागू करते हुए, मैं Character.receiveAttack(Int)विधि के अंदर तर्क का परीक्षण करने के लिए कुछ परीक्षण मामले बनाता हूं । कुछ इस तरह:

@Test
fun healthIsReducedWhenCharacterIsAttacked() {
    val c = Character(100) //arg is the health
    c.receiveAttack(50) //arg is the suffered attack damage
    assertThat(c.health, is(50));
}

कहो कि मेरे पास 10 तरीके हैं परीक्षण receiveAttackविधि। अब, मैं एक विधि जोड़ता हूं Character.attack(Character)(जिसे कॉल receiveAttackविधि), और कुछ टीडीडी चक्रों के परीक्षण के बाद, मैं एक निर्णय लेता हूं: Character.receiveAttack(Int)होना चाहिए private

पिछले 10 परीक्षण-मामलों के साथ क्या होता है? क्या मुझे उन्हें हटाना चाहिए? क्या मुझे विधि रखनी चाहिए public(मुझे ऐसा नहीं लगता)?

यह सवाल निजी तरीकों का परीक्षण करने के तरीके के बारे में नहीं है बल्कि टीडीडी को लागू करते समय फिर से डिजाइन करने के बाद उनसे कैसे निपटना है



10
यदि यह निजी है तो आप इसका परीक्षण नहीं करते हैं, यह इतना आसान है। निकालें और
रिफ्लेक्टर

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

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

2
@gnat लेकिन मैंने "कवरेज नहीं होने" के बारे में कभी कुछ नहीं कहा? "मुझे कम परीक्षणों की तुलना में अधिक परीक्षण पसंद हैं" के बारे में मेरी टिप्पणी को स्पष्ट करना चाहिए था। निश्चित रूप से आप जो बिल्कुल नहीं पा रहे हैं, निश्चित रूप से मैं उस कोड का भी परीक्षण करता हूं जिसे मैंने निकाला है। यह पूरी बात है।
14:99 पर user9993

जवाबों:


52

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

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

और अगर ऐसा नहीं होता है, तो इसमें कार्यक्षमता है receiveAttackऔर इसकी आवश्यकता नहीं है और अब अस्तित्व में नहीं होना चाहिए!

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


14
यह एक अच्छा जवाब है, "यदि आपका परीक्षण ढांचा निजी तरीकों का परीक्षण करना आसान बनाता है, और यदि आप निजी विधियों का परीक्षण करने का निर्णय लेते हैं, तो आप उन्हें रख सकते हैं।" निजी तरीके कार्यान्वयन विवरण हैं और कभी भी, कभी भी सीधे परीक्षण नहीं किए जाने चाहिए।
डेविड अर्नो

20
@ दाविद अरनो: मैं असहमत हूं, इस कारण से एक मॉड्यूल के आंतरिक परीक्षण कभी नहीं होना चाहिए। हालांकि, एक मॉड्यूल के आंतरिक महान जटिलता के हो सकते हैं और इसलिए प्रत्येक व्यक्ति की आंतरिक कार्यक्षमता के लिए यूनिट-परीक्षण मूल्यवान हो सकते हैं। यूनिट-परीक्षणों का उपयोग कार्यक्षमता के एक टुकड़े के आवेगों की जांच करने के लिए किया जाता है , अगर किसी निजी पद्धति में इन्वर्टर (पूर्व-स्थितियां / पोस्ट-स्थितियां) हैं, तो यूनिट-टेस्ट मूल्यवान हो सकता है।
मैथ्यू एम।

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

28
@DavidArno उस तर्क से, यदि आप एक निष्पादन योग्य (एक पुस्तकालय के बजाय) का निर्माण कर रहे हैं, तो आपके पास कोई इकाई परीक्षण नहीं होना चाहिए। - "फ़ंक्शन कॉल सार्वजनिक API का हिस्सा नहीं हैं! केवल कमांड लाइन तर्क हैं! यदि आपके प्रोग्राम का कोई आंतरिक फ़ंक्शन कमांड लाइन तर्क के माध्यम से पहुंच से बाहर है, तो इसे हटा दें क्योंकि यह कुछ भी नहीं करता है।" - जबकि निजी कार्य कक्षा के सार्वजनिक एपीआई का हिस्सा नहीं हैं, वे कक्षा के आंतरिक एपीआई का हिस्सा हैं। और जब आपको आवश्यक रूप से किसी वर्ग के आंतरिक एपीआई का परीक्षण करने की आवश्यकता नहीं है, तो आप निष्पादन योग्य के आंतरिक एपीआई के परीक्षण के लिए उसी तर्क का उपयोग कर सकते हैं।
आरएम

7
@ आरएम, अगर मैं एक गैर-मॉड्यूलर फैशन में एक निष्पादन योग्य बनाने के लिए था, तो मुझे इंटर्नल के भंगुर परीक्षणों के बीच चयन करने के लिए मजबूर किया जाएगा, या निष्पादन योग्य और रनटाइम I / O का उपयोग करके केवल एकीकरण परीक्षणों का उपयोग करना होगा। इसलिए मेरे वास्तविक तर्क से, आपके स्ट्रोमैन संस्करण के बजाय, मैं इसे एक मॉड्यूलर फैशन (जैसे पुस्तकालयों के एक सेट के माध्यम से) में बनाऊंगा। उन मॉड्यूल के सार्वजनिक एपीआई को फिर गैर-भंगुर फैशन में परीक्षण किया जा सकता है।
डेविड अरनो 12

23

यदि विधि परीक्षण की आवश्यकता के लिए पर्याप्त जटिल है, तो इसे किसी वर्ग में सार्वजनिक होना चाहिए। तो आप इस से परिलक्षित करते हैं:

public class X {
  private int complexity(...) {
    ...
  }
  public void somethingElse() {
    int c = complexity(...);
  }
}

सेवा मेरे:

public class Complexity {
  public int calculate(...) {
    ...
  }
}

public class X {
  private Complexity complexity;
  public X(Complexity complexity) { // dependency injection happiness
    this.complexity = complexity;
  }

  public void something() {
    int c = complexity.calculate(...);
  }
}

X.complexity के लिए वर्तमान परीक्षा को ComplexityTest पर ले जाएँ। फिर जटिलता का मजाक उड़ाते हुए पाठ X.something।

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


आपका जवाब बहुत अधिक स्पष्ट रूप से बताता है कि ओपी के सवाल पर मैं अपनी टिप्पणी में समझाने की कोशिश कर रहा था। अच्छा जवाब।
user9993

3
आपके उत्तर के लिए धन्यवाद। दरअसल, getAttack विधि काफी सरल ( this.health = this.health - attackDamage) है। शायद इसे किसी अन्य वर्ग के लिए निकालें यह एक अतिव्यापी समाधान है, इस पल के लिए।
हेक्टर

1
यह निश्चित रूप से ओपी के लिए ओवरकिल है - वह स्टोर पर ड्राइव करना चाहता है, चंद्रमा पर नहीं उड़ना - लेकिन सामान्य मामले में एक अच्छा समाधान है।

यदि कार्य इतना सरल है कि शायद यह अति-साध्य है कि इसे पहले स्थान पर एक समारोह के रूप में परिभाषित किया गया है।
डेविड के

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

6

मान लें कि मेरे पास 10 विधियां प्राप्त करने की विधि है। अब, मैं एक विधि जोड़ता हूं Character.attack (चरित्र) (जो कॉल प्राप्त करता है। विधि), और कुछ TDD चक्रों के परीक्षण के बाद, मैं एक निर्णय लेता हूं: Character.receiveAttack (Int) निजी होना चाहिए।

यहां कुछ ध्यान में रखना है कि आप जो निर्णय कर रहे हैं वह एपीआई से एक विधि को हटाने के लिए है । पीछे की संगतता के शिष्टाचार का सुझाव होगा

  1. यदि आपको इसे हटाने की आवश्यकता नहीं है, तो इसे एपीआई में छोड़ दें
  2. यदि आपको इसे अभी तक हटाने की आवश्यकता नहीं है , तो इसे पदावनत के रूप में चिह्नित करें और यदि जीवन के अंत में संभव दस्तावेज होगा
  3. यदि आपको इसे हटाने की आवश्यकता है, तो आपके पास एक प्रमुख संस्करण परिवर्तन होगा

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

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


3
पदावनति हमेशा एक चिंता का विषय नहीं है। इस सवाल से: "चलो कहते हैं कि मैं विकसित करना शुरू कर देता हूं ..." यदि सॉफ्टवेयर अभी तक जारी नहीं किया गया है, तो अवमूल्यन एक गैर-मुद्दा है। इसके अलावा: "एक भूमिका खेल" का तात्पर्य यह एक पुन: प्रयोज्य पुस्तकालय नहीं है, लेकिन अंत उपयोगकर्ताओं के लिए बाइनरी सॉफ़्टवेयर है। जबकि कुछ एंड-यूज़र सॉफ़्टवेयर में सार्वजनिक API (जैसे MS Office) होता है, अधिकांश नहीं होता है। यहां तक ​​कि जिस सॉफ्टवेयर में एक सार्वजनिक एपीआई होता है, उसमें केवल प्लग इन, स्क्रिप्टिंग (एलयूए विस्तार के साथ खेल), या अन्य कार्यों के लिए इसका एक हिस्सा होता है। फिर भी, यह सामान्य मामले के लिए विचार लाने के लायक है जो ओपी वर्णन करता है।
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.