रिफैक्टरिंग से पहले यूनिट टेस्ट कैसे लिखें?


55

मैंने एक समान लाइन के साथ प्रश्नों के कुछ उत्तर पढ़े हैं जैसे "रिफैक्टरिंग करते समय आप अपने यूनिट परीक्षणों को कैसे काम करते हैं?" मेरे मामले में परिदृश्य थोड़ा अलग है कि मुझे एक परियोजना की समीक्षा करने और कुछ मानकों के अनुरूप लाने के लिए दिया गया है, वर्तमान में परियोजना के लिए कोई परीक्षण नहीं है!

मैंने ऐसी कई चीजों की पहचान की है जो मुझे लगता है कि बेहतर किया जा सकता था जैसे कि सर्विस लेयर में DAO टाइप कोड को मिक्स न करना।

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

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

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

समान रूप से मैं इस तर्क को देख सकता हूं कि अगर मैं उन तरीकों के हस्ताक्षर बदल रहा हूं जिन्हें एक नया स्वरूप माना जा सकता है।

यहाँ एक संक्षिप्त उदाहरण है

MyDocumentService.java (वर्तमान)

public class MyDocumentService {
   ...
   public List<Document> findAllDocuments() {
      DataResultSet rs = documentDAO.findAllDocuments();
      List<Document> documents = new ArrayList<>();
      for(DataObject do: rs.getRows()) {
         //get row data create new document add it to 
         //documents list
      }

      return documents;
   }
}

MyDocumentService.java (refactored / जो कुछ भी बदल दिया गया है)

public class MyDocumentService {
   ...
   public List<Document> findAllDocuments() {
      //Code dealing with DataResultSet moved back up to DAO
      //DAO now returns a List<Document> instead of a DataResultSet
      return documentDAO.findAllDocuments();
   }
}

14
यह वास्तव में है पुनर्रचना आप करते हैं, या करने की योजना नया स्वरूप ? क्योंकि दो मामलों में उत्तर अलग हो सकता है।
16:30

4
मैं निश्चितता पर काम कर रहा हूँ "परिभाषा के साथ, परिभाषा के अनुसार, आप अपने सॉफ्टवेयर में क्या बदलाव नहीं करते हैं, आप इसे कैसे बदलते हैं।" इसलिए मेरा मानना ​​है कि इस मामले में यह
रिफलेक्टिंग है

21
एकीकरण परीक्षण न लिखें। आप जिस "रिफैक्टिंग" की योजना बना रहे हैं, वह इकाई परीक्षण के स्तर से ऊपर है। केवल यूनिट नई कक्षाओं (या पुराने लोगों को जिन्हें आप जानते हैं कि आप उन्हें रख रहे हैं) का परीक्षण करते हैं।
रुकना बंद करो मोनिका

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

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

जवाबों:


56

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

अब आपके पास कुछ परीक्षण हैं जो यह दावा करेंगे कि आप इस स्तर से नीचे जो भी करते हैं , आपका व्यवहार वैसा ही रहता है।

आप यह सवाल करने के लिए काफी सही हैं कि परीक्षण और कोड सिंक में कैसे रह सकते हैं। यदि किसी घटक के लिए आपका इंटरफ़ेस समान रहता है, तो आप इसके चारों ओर एक परीक्षण लिख सकते हैं और दोनों कार्यान्वयन के लिए समान शर्तों का दावा कर सकते हैं (जैसा कि आप नया कार्यान्वयन बनाते हैं)। यदि ऐसा नहीं होता है, तो आपको यह स्वीकार करना होगा कि एक निरर्थक घटक के लिए एक परीक्षण एक निरर्थक परीक्षण है।


1
विज़, आपको इकाई परीक्षण के बजाय एकीकरण या सिस्टम परीक्षण करने की संभावना है। आप शायद इसके लिए एक "यूनिट टेस्टिंग" टूल का उपयोग करेंगे, लेकिन आप प्रत्येक परीक्षण के साथ एक से अधिक कोड वाली यूनिट मारेंगे।
'23

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

40

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

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

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


एकीकरण परीक्षणों के लिए +1। एप्लिकेशन के आधार पर, आप वास्तव में वेब ऐप पर अनुरोध भेजने के स्तर पर शुरू करने में सक्षम हो सकते हैं। एप्‍लिकेशन को वापस भेजने से केवल रीफैक्‍टेयरिंग के कारण परिवर्तन नहीं होना चाहिए, हालाँकि यदि यह HTML वापस भेज रहा है, तो यह निश्चित रूप से कम परीक्षण योग्य है।
jpmc26

मुझे वाक्यांश 'पिन-डाउन' टेस्ट पसंद हैं।
ब्रायन एग्न्यू

12

मेरा सुझाव है - यदि आप पहले से ही नहीं हैं - दोनों वर्किंग को प्रभावी रूप से लिगेसी कोड के साथ-साथ रिफैक्टरिंग - मौजूदा कोड के डिजाइन में सुधार

[..] यह समस्या मुझे प्रतीत होती है कि जब मैं रिफ्लेक्टर करता हूं तो वे परीक्षण टूट जाएंगे क्योंकि मैं बदल रहा हूं जहां कुछ निश्चित तर्क किए जाते हैं और परीक्षण पिछली संरचना को ध्यान में रखकर लिखे जाएंगे (मॉकड निर्भरता आदि) [ ..]

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

इसके अलावा, जैसा कि अन्य पहले ही लिख चुके हैं: बहुत विस्तृत परीक्षण न लिखें (कम से कम शुरुआत में नहीं)। अमूर्तता के उच्च स्तर पर रहने की कोशिश करें (इस प्रकार आपके परीक्षण शायद प्रतिगमन या एकीकरण परीक्षणों के रूप में बेहतर होंगे)।


1
इस। परीक्षण भयानक लगेंगे , लेकिन वे मौजूदा व्यवहार को कवर करेंगे। फिर, जैसा कि कोड फिर से सक्रिय हो जाता है, इसलिए परीक्षण, लॉक स्टेप में करें। तब तक दोहराएं जब तक आपके पास कुछ ऐसा न हो, जिस पर आपको गर्व हो। ++
रबरडक

1
मैं उन दोनों किताबों की सिफारिशों को पूरा करता हूं - मेरे पास हमेशा दोनों पास होते हैं जब मुझे टेस्टलेस कोड से निपटना पड़ता है।
टोबे स्पाइट

5

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

आइए अपने उदाहरण देखें:

public class MyDocumentService {
   ...
   public List<Document> findAllDocuments() {
      DataResultSet rs = documentDAO.findAllDocuments();
      List<Document> documents = new ArrayList<>();
      for(DataObject do: rs.getRows()) {
         //get row data create new document add it to 
         //documents list
      }

      return documents;
   }
}

आपका परीक्षण शायद कुछ इस तरह दिखता है:

DocumentDao documentDao = Mock.create(DocumentDao.class);
Mock.when(documentDao.findAllDocuments())
    .thenReturn(DataResultSet.create(...))
assertEquals(..., new MyDocumentService(documentDao).findAllDocuments());

डॉक्युमेंटडाउ का मज़ाक उड़ाने के बजाय, इसकी निर्भरता का मज़ाक उड़ाएँ:

DocumentDao documentDao = new DocumentDao(db);
Mock.when(db...)
    .thenReturn(...)
assertEquals(..., new MyDocumentService(documentDao).findAllDocuments());

अब, आप से तर्क स्थानांतरित कर सकते हैं MyDocumentServiceमें DocumentDaoबिना परीक्षण तोड़ने। परीक्षण दिखाएगा कि कार्यक्षमता समान है (अब तक आपने इसे परीक्षण किया है)।


यदि आप DocumentService का परीक्षण कर रहे हैं और DAO का मज़ाक नहीं उड़ाते हैं, तो यह इकाई परीक्षण नहीं है। यह एकात्मक और एकीकरण परीक्षण के बीच कुछ है। ऐसा नहीं है?
Laiv

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

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

3

जैसा कि आप कहते हैं, यदि आप व्यवहार को बदलते हैं तो यह एक परिवर्तन है और रिफ्लेक्टर नहीं है। आप किस स्तर पर व्यवहार बदलते हैं इससे क्या फर्क पड़ता है।

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

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

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


3

यहाँ मेरा दृष्टिकोण। यह समय के संदर्भ में लागत है क्योंकि यह 4 चरणों में एक रिफलेक्टर-परीक्षण है।

मैं इस सवाल के उदाहरण में उजागर की तुलना में अधिक जटिलता के साथ घटकों में बेहतर सूट कर सकता हूं।

वैसे भी रणनीति किसी भी घटक उम्मीदवार के लिए एक इंटरफ़ेस (DAO, सेवाएँ, नियंत्रकों, ...) द्वारा सामान्यीकृत की जाती है।

1. इंटरफ़ेस

सभी सार्वजनिक विधियों को MyDocumentService से इकट्ठा करते हैं और उन सभी को एक इंटरफ़ेस में डालते हैं। उदाहरण के लिए। यदि यह पहले से मौजूद है, तो किसी नए को सेट करने के बजाय उस का उपयोग करें

public interface DocumentService {

   List<Document> getAllDocuments();

   //more methods here...
}

फिर हम इस नए इंटरफ़ेस को लागू करने के लिए MyDocumentService को बाध्य करते हैं

अब तक सब ठीक है। कोई बड़ा बदलाव नहीं किया गया था, हमने वर्तमान अनुबंध का सम्मान किया और व्यवहार अछूता नहीं रहा।

public class MyDocumentService implements DocumentService {

 @Override
 public List<Document> getAllDocuments(){
         //legacy code here as it is.
        // with no changes ...
  }
}

2. विरासत कोड की इकाई परीक्षण

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

अब, MyDocumentService का परीक्षण करने के बजाय हम परीक्षण किए जाने वाले अनुबंध के रूप में इंटरफ़ेस का उपयोग करने जा रहे हैं।

Im विवरण में नहीं जा रहा हूं, इसलिए मुझे क्षमा करें यदि मेरा कोड बहुत सरल या बहुत अज्ञेय लगता है

public class DocumentServiceTestSuite {

   @Mock
   MyDependencyA mockDepA;

   @Mock
   MyDependencyB mockDepB;

    //... More mocks

   DocumentService service;

  @Before
   public void initService(){
       service = MyDocumentService(mockDepA, mockDepB);
      //this is purposed way to inject 
      //dependencies. Replace it with one you like more.  
   }

   @Test
   public void getAllDocumentsOK(){
         // here I mock depA and depB
         // wanted behaivors...

         List<Document> result = service.getAllDocuments();

          Assert.assertX(result);
          Assert.assertY(result);
           //... As many you think appropiate
    } 
 }

यह चरण इस दृष्टिकोण में किसी भी अन्य की तुलना में अधिक समय लेता है। और यह सबसे महत्वपूर्ण है क्योंकि यह भविष्य की तुलनाओं के लिए संदर्भ बिंदु निर्धारित करेगा।

नोट: कोई बड़ा बदलाव नहीं किया गया और व्यवहारकर्ता अछूता नहीं रहा। मैं यहाँ SCM में एक टैग करने का सुझाव देता हूँ। टैग या शाखा कोई फर्क नहीं पड़ता। बस एक संस्करण है।

हम इसे रोलबैक, संस्करणों की तुलना के लिए चाहते हैं और पुराने कोड और नए के समानांतर निष्पादन के लिए हो सकते हैं।

3. परावर्तन

रिफ्लेक्टर एक नए घटक में लागू होने जा रहा है। हम मौजूदा कोड पर कोई बदलाव नहीं करेंगे। पहला कदम MyDocumentService की कॉपी और पेस्ट करना जितना आसान है और इसे CustomDocumentService (उदाहरण के लिए) का नाम बदलें ।

नई कक्षा DocumentService को लागू करती रहती है । तब जाओ और getAllDocuments () को फिर से सक्रिय करें(एक से शुरू करें। पिन-रिफ्लेक्टर)

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

मौजूदा कार्यान्वयन को तोड़ना / बदलना महत्वपूर्ण नहीं है। हम समानांतर में दोनों सेवाओं को निष्पादित करना चाहते हैं और फिर परिणामों की तुलना करते हैं।

public class CustomDocumentService implements DocumentService {

 @Override
 public List<Document> getAllDocuments(){
         //new code here ...
         //due to im refactoring service 
         //I do the less changes possible on its dependencies (DAO).
         //these changes will come later 
         //and they will have their own tests
  }
 }

4. अद्यतन कर रहा है DocumentServiceTestSuite

ठीक है, अब आसान हिस्सा है। नए घटक के परीक्षणों को जोड़ने के लिए।

public class DocumentServiceTestSuite {

   @Mock
   MyDependencyA mockDepA;

   @Mock
   MyDependencyB mockDepB;

   DocumentService service;
   DocumentService customService;

  @Before
   public void initService(){
       service = MyDocumentService(mockDepA, mockDepB);
        customService = CustomDocumentService(mockDepA, mockDepB);
       // this is purposed way to inject 
       //dependencies. Replace it with the one you like more
   }

   @Test
   public void getAllDocumentsOK(){
         // here I mock depA and depB
         // wanted behaivors...

         List<Document> oldResult = service.getAllDocuments();

          Assert.assertX(oldResult);
          Assert.assertY(oldResult);
           //... As many you think appropiate

          List<Document> newResult = customService.getAllDocuments();

          Assert.assertX(newResult);
          Assert.assertY(newResult);
           //... The very same made to oldResult

          //this is optional
Assert.assertEquals(oldResult,newResult);
    } 
 }

अब हमारे पास पुराने। और newResult दोनों स्वतंत्र रूप से मान्य हैं लेकिन हम एक दूसरे से तुलना भी कर सकते हैं। यह अंतिम सत्यापन वैकल्पिक है और यह परिणाम पर निर्भर है। यह तुलनीय नहीं हो सकता है।

इस तरह से दो संग्रहों की तुलना करने के लिए बहुत अधिक सघन नहीं हो सकता है, लेकिन किसी भी अन्य प्रकार की वस्तु (pojos, डेटा मॉडल एंटिटीज़, DTO, रैपर्स, देशी प्रकार ...) के लिए मान्य होगा।

टिप्पणियाँ

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

मैंने अपने उदाहरण एक सेवा द्वारा शुरू किए हैं और मैं डीएओ और इतने पर पालन करूंगा। निर्भरता के स्तर में गहराई तक जा रहे हैं। कमोबेश इसे ऊपर-नीचे की रणनीति के रूप में वर्णित किया जा सकता है । हालांकि मामूली बदलाव / रिफ्लेक्टर ( जैसे कि दौरे के उदाहरण में उजागर ), एक नीचे की ओर काम आसान होगा। क्योंकि बदलावों की गुंजाइश बहुत कम है।

अंत में, यह आप पर निर्भर है कि हटाए गए कोड को हटा दें और पुराने आश्रितों को नए पर पुनर्निर्देशित करें।

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

इतने काम के परिणाम में, आपके पास विरासत कोड का परीक्षण, सत्यापन और संस्करण है। और नया कोड, परीक्षण, मान्य और संस्करण के लिए तैयार है।


3

tl; डॉ । यूनिट परीक्षण न लिखें। अधिक उपयुक्त स्तर पर परीक्षण लिखें।


आपकी कार्य करने की परिभाषा को देखते हुए:

आप अपने सॉफ्टवेयर में क्या बदलाव करते हैं, आप इसे कैसे बदलते हैं

वहाँ बहुत व्यापक स्पेक्ट्रम। एक छोर पर एक विशेष विधि में स्व-निहित परिवर्तन है, शायद एक अधिक कुशल एल्गोरिदम का उपयोग करना। दूसरे छोर पर दूसरी भाषा में पोर्टिंग है।

जिस भी स्तर पर रिफैक्टरिंग / रिडिजाइन किया जा रहा है, उस स्तर पर या उससे अधिक परीक्षण करने के लिए महत्वपूर्ण है।

स्वचालित परीक्षणों को अक्सर निम्नानुसार वर्गीकृत किया जाता है:

  • इकाई परीक्षण - व्यक्तिगत घटक (कक्षाएं, विधियाँ)

  • एकीकरण परीक्षण - घटकों के बीच बातचीत

  • सिस्टम परीक्षण - पूरा आवेदन

परीक्षण के स्तर को लिखें जो कि रिफैक्टिंग को अनिवार्य रूप से अछूता रख सकता है।

सोच:

क्या आवश्यक, सार्वजनिक रूप से दिखाई देने वाला व्यवहार आवेदन के पहले और बाद में दोनों होगा ? मैं उस चीज को कैसे परख सकता हूं जो अभी भी वही काम करती है?


2

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

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

यह विचार करने के लिए उपयोगी हो सकता है कि आप अपने परीक्षण लिखते हैं - क्या मैं आसानी से एक क्यूए, एक उत्पाद के मालिक या एक उपयोगकर्ता का वर्णन कर सकता हूं, यह परीक्षण वास्तव में क्या परीक्षण है? यदि ऐसा लगता है कि परीक्षण का वर्णन करना बहुत गूढ़ और तकनीकी होगा, तो शायद आप गलत स्तर पर परीक्षण कर रहे हैं। उन बिंदुओं / स्तरों पर परीक्षण करें जो 'समझ में' आते हैं, और हर स्तर पर परीक्षण के साथ अपने कोड को गम न करें।


हमेशा चढ़ाव के कारणों में दिलचस्पी!
टोपो मोर्टो

1

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

[TestMethod]
public void simple_addition()
{
    Assert.AreEqual(7, Eval("3 + 4"));
}

[TestMethod]
public void order_of_operations()
{
    Assert.AreEqual(52, Eval("2 + 5 * 10"));
}

[TestMethod]
public void absolute_value()
{
    Assert.AreEqual(9, Eval("abs(-9)"));
    Assert.AreEqual(5, Eval("abs(5)"));
    Assert.AreEqual(0, Eval("abs(0)"));
}

static object Eval(string expression)
{
    // This is the code under test.
    // I can refactor this as much as I want without changing the tests.
    var settings = new EvaluatorSettings();
    Evaluator.Settings = settings;
    Evaluator.Evaluate(expression);
    return Evaluator.LastResult;
}

परीक्षण अच्छे हैं, लेकिन परीक्षण के तहत कोड में खराब एपीआई है। मैं केवल अपने एडेप्टर परत को अद्यतन करके परीक्षण को बदले बिना इसे रीफ़ैक्टर कर सकता हूं:

static object Eval(string expression)
{
    // After refactoring...
    var settings = new EvaluatorSettings();
    var evaluator = new Evaluator(settings);
    return evaluator.Evaluate(expression);
}

यह उदाहरण डोंट रिपिट योरसेल्फ सिद्धांत के अनुसार एक बहुत स्पष्ट बात लगती है, लेकिन यह अन्य मामलों में इतना स्पष्ट नहीं हो सकता है। लाभ DRY से आगे जाता है - असली फायदा यह है कि परीक्षण के तहत कोड से परीक्षणों की डिकॉप्लिंग।

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

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.