রিফ্যাক্টরিংয়ের আগে ইউনিট টেস্টগুলি কীভাবে লিখবেন?


55

আমি অনুরূপ লাইনে প্রশ্নের কয়েকটি উত্তর পড়েছি যেমন "রিফ্যাক্টরিংয়ের সময় আপনি কীভাবে আপনার ইউনিট পরীক্ষা চালিয়ে যাবেন?"। আমার ক্ষেত্রে দৃশ্যটি কিছুটা পৃথক যে আমাকে পর্যালোচনা করতে এবং আমাদের কিছু মানদণ্ডের সাথে সামঞ্জস্য করার জন্য একটি প্রকল্প দেওয়া হয়েছে, বর্তমানে প্রকল্পের জন্য কোনও পরীক্ষা নেই!

আমি অনেকগুলি জিনিস সনাক্ত করেছি যা আমি মনে করি যে আরও ভাল কাজ করা যেতে পারে যেমন কোনও সার্ভিস লেয়ারে ডিএও টাইপ কোডটি না মেশানো।

রিফ্যাক্টর করার আগে বিদ্যমান কোডের জন্য পরীক্ষা লিখতে ভাল ধারণা মনে হয়েছিল। আমার কাছে যে সমস্যাটি দেখা যাচ্ছে তা হ'ল আমি যখন রিফ্যাক্টর করি তখন সেই পরীক্ষাগুলি ভেঙে যায় কারণ আমি পরিবর্তন করছি যেখানে নির্দিষ্ট যুক্তিটি করা হয়েছে এবং পরীক্ষাগুলি পূর্ববর্তী কাঠামোটিকে সামনে রেখে লেখা হবে (বিদ্রূপযুক্ত নির্ভরতা ইত্যাদি))

আমার ক্ষেত্রে, এগিয়ে যাওয়ার সর্বোত্তম উপায় কী হবে? আমি রিফ্যাক্টর কোডের চারপাশে পরীক্ষাগুলি লিখতে প্ররোচিত করছি তবে আমি সচেতন রয়েছি এমন কিছু ঝুঁকি আছে যা আমি ভুলভাবে জিনিসগুলিকে রিফেক্টর করতে পারি যা পছন্দসই আচরণটি পরিবর্তন করতে পারে।

এটি শোধনাগার বা পুনরায় নকশাগুলি হোক না কেন আমি এই শর্তগুলি সংশোধন করার জন্য আমার সন্তুষ্টির জন্য খুশি, বর্তমানে আমি রিফ্যাক্টরিংয়ের জন্য নিম্নলিখিত সংজ্ঞাটিতে কাজ করছি "সংজ্ঞা অনুসারে, আপনি নিজের সফ্টওয়্যার যা করেন তা পরিবর্তন করেন না, আপনি এটি পরিবর্তন করে। সুতরাং আমি সফ্টওয়্যারটি কী করে তা পরিবর্তন করছি না যেখানে এটি / কোথায় তা এটি পরিবর্তন করে চলেছে।

সমানভাবে আমি এই যুক্তিটি দেখতে পাচ্ছি যে যদি আমি এমন পদ্ধতিগুলির স্বাক্ষর পরিবর্তন করি যা একটি পুনরায় ডিজাইন হিসাবে বিবেচনা করা যেতে পারে।

একটি সংক্ষিপ্ত উদাহরণ এখানে

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 (রিফ্যাক্টর / যাই হোক না কেন পুনরায় নকশাকৃত)

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
এটা সত্যিই হয় refactoring আপনাকে যা করতে, অথবা করার পরিকল্পনা রূপের ? কারণ উত্তর দুটি ক্ষেত্রে ভিন্ন হতে পারে।
হার্বি

4
আমি সংজ্ঞা অনুসারে "রিফ্যাক্টরিং সহ, সংজ্ঞা অনুসারে, আপনার সফ্টওয়্যারটি যা করে তা আপনি পরিবর্তন করেন না, এটি কীভাবে পরিবর্তন করে তা আপনি পরিবর্তন করেন।" সুতরাং আমি বিশ্বাস করি এই ক্ষেত্রে এটি রিফ্যাক্টরিং, শব্দটি সম্পর্কে আমার উপলব্ধি সংশোধন করতে
নির্দ্বিধায়

21
না, ইন্টিগ্রেশন পরীক্ষা লিখুন। আপনি যে "রিফ্যাক্টরিং" পরিকল্পনা করছেন তা ইউনিট পরীক্ষার স্তরের উপরে। কেবলমাত্র ইউনিট পরীক্ষা করে নতুন ক্লাসগুলি (বা পুরানো যেগুলি আপনি জানেন যে আপনি সেগুলি রাখছেন)।
16:25

2
রিফ্যাক্টরিং সংজ্ঞাটি সম্মানের সাথে, আপনার সফ্টওয়্যারটি কী এটি পরিষ্কারভাবে সংজ্ঞা দেয়? অন্য কথায়, এটি ইতিমধ্যে স্বাধীন এপিআই সহ মডিউলগুলিতে "ফ্যাক্টরড"? যদি তা না হয় তবে আপনি সর্বোচ্চ (ব্যবহারকারী-মুখোমুখি) পর্যায়ে ব্যতীত আপনি এটিকে রিফ্যাক্টর করতে পারবেন না। মডিউল স্তরে, আপনি অনিবার্যভাবে এটি পুনরায় নকশা করাতে যাবেন। কোন ক্ষেত্রে ইউনিট থাকার আগে ইউনিট পরীক্ষা লেখার জন্য আপনার সময় নষ্ট করবেন না।
কেভিন ক্রামউইদে

4
পরীক্ষার সুরক্ষার জালটি কেবল পরীক্ষার জোরে প্রবেশ করতে সক্ষম হওয়ার জন্য আপনাকে খুব সম্ভবত রিফ্যাক্টরিং করতে হবে। আমি আপনাকে যে সর্বোত্তম পরামর্শ দিতে পারি তা হ'ল যদি আপনার আইডিই বা রিফ্যাক্টরিং সরঞ্জামটি এটি আপনার পক্ষে না করে তবে এটি হাতে হাতে করবেন না। যতক্ষণ না আপনি সিটিটি জোতাতে প্রবেশ করতে পারেন ততক্ষণ স্বয়ংক্রিয় রিফ্যাক্টরিংগুলি প্রয়োগ করতে থাকুন। আপনি অবশ্যই মাইকেল ফেদারের "লিগ্যাসি কোডের সাথে কার্যকরভাবে কাজ করা" এর একটি অনুলিপি তুলতে চাইছেন।
রাবারডাক

উত্তর:


56

আপনি এমন পরীক্ষাগুলি সন্ধান করছেন যা নিবন্ধগুলি পরীক্ষা করে । অর্থাত্ কিছু বিদ্যমান আচরণ ভঙ্গ করা। সেই আচরণটি কোন স্তরে একই থাকবে এবং এই আচরণটি চালানো ইন্টারফেসটি একই থাকবে এবং সেই সময়ে পরীক্ষাগুলি স্থাপন শুরু করব তা সনাক্ত করেই আমি শুরু করব।

আপনার এখন কয়েকটি পরীক্ষা রয়েছে যা দৃsert়ভাবে জানিয়ে দেবে যে আপনি এই স্তরের নীচে যা কিছু করেন না কেন আপনার আচরণ একই থাকে।

পরীক্ষাগুলি এবং কোড সিঙ্কে কীভাবে থাকতে পারে তা প্রশ্ন করা আপনি যথেষ্ট সঠিক। যদি কোনও উপাদানটির কাছে আপনার ইন্টারফেস একই থাকে, তবে আপনি এটির চারপাশে একটি পরীক্ষা লিখতে পারেন এবং উভয় বাস্তবায়নের জন্য একই শর্তটি জোর করতে পারেন (আপনি নতুন বাস্তবায়নটি তৈরি করার সময়)। যদি এটি না হয়, তবে আপনাকে স্বীকার করতে হবে যে রিন্ডন্ড্যান্ট উপাদানগুলির জন্য একটি পরীক্ষা একটি অপ্রয়োজনীয় পরীক্ষা।


1
যেমন, আপনি সম্ভবত ইউনিট পরীক্ষার পরিবর্তে ইন্টিগ্রেশন বা সিস্টেম টেস্টিং করছেন। আপনি সম্ভবত এটির জন্য এখনও "ইউনিট টেস্টিং" সরঞ্জামটি ব্যবহার করবেন তবে আপনি প্রতিটি পরীক্ষার সাথে একাধিক ইউনিট কোডকে আঘাত করছেন।
Moz

হ্যাঁ. এটা খুব ঘটনা। আপনার রিগ্রেশন পরীক্ষাটি খুব উচ্চ স্তরের কিছু করতে পারে যেমন একটি সার্ভারের কাছে REST অনুরোধ করা এবং সম্ভবত পরবর্তী কোনও ডাটাবেস পরীক্ষা (যেমন অবশ্যই ইউনিট পরীক্ষা নয়!)
ব্রায়ান অ্যাগনিউ

40

প্রস্তাবিত অনুশীলনটি হ'ল "পিন-ডাউন টেস্টগুলি" লেখা দিয়ে শুরু করা যা কোডের বর্তমান আচরণের পরীক্ষা করে সম্ভবত বাগগুলি অন্তর্ভুক্ত করে, তবে আপনাকে প্রয়োজনীয় নথির লঙ্ঘন করে এমন প্রদত্ত আচরণটি বাগ কিনা কিনা তা বোঝার উন্মাদনায় নামার প্রয়োজন ছাড়াই, এমন কোনও কিছুর জন্য কাজ করা যা আপনি অবগত নন, বা প্রয়োজনীয়তার ক্ষেত্রে একটি অনাবন্ধিত পরিবর্তনের প্রতিনিধিত্ব করেন।

এই পিন-ডাউন পরীক্ষাগুলি উচ্চ স্তরে হওয়া, অর্থাৎ ইউনিট পরীক্ষাগুলির পরিবর্তে ইন্টিগ্রেশন হওয়ার পক্ষে সর্বাধিক বোধগম্যতা তৈরি করে, যাতে আপনি পুনরায় সংশোধন শুরু করার সময় তারা কাজ করতে থাকবে।

তবে কোডটিকে পরীক্ষণযোগ্য করে তোলার জন্য কিছু রিফ্যাক্টেরিংগুলির প্রয়োজন হতে পারে - "নিরাপদ" রিফ্যাক্টরিংগুলিতে লেগে থাকতে সাবধান হন। উদাহরণস্বরূপ, প্রায় সব ক্ষেত্রেই ব্যক্তিগত যে পদ্ধতিগুলি কোনও কিছু ভঙ্গ না করে সর্বজনীন করা যেতে পারে।


ইন্টিগ্রেশন পরীক্ষার জন্য +1। অ্যাপের উপর নির্ভর করে, আপনি সম্ভবত ওয়েব অ্যাপ্লিকেশনটিতে অনুরোধ প্রেরণের স্তরে শুরু করতে সক্ষম হতে পারেন। অ্যাপটি যা প্রেরণ করে তা কেবল রিফ্যাক্টরিংয়ের কারণে পরিবর্তিত হওয়া উচিত নয়, যদিও এটি যদি এইচটিএমএল ফেরত পাঠায় তবে এটি অবশ্যই কম পরীক্ষামূলক।
jpmc26

আমি 'পিন-ডাউন' পরীক্ষাগুলি পছন্দ করি।
ব্রায়ান

12

আমি প্রস্তাব দিচ্ছি - যদি আপনি ইতিমধ্যে না থাকেন - লিগ্যাসি কোডের সাথে কার্যকরভাবে কাজ করা পাশাপাশি রিফ্যাক্টরিং - বিদ্যমান কোডের নকশা উন্নত করা

[..] আমার কাছে যে সমস্যাটি দেখা যাচ্ছে তা হ'ল আমি যখন রিফ্যাক্টর করি তখন সেই পরীক্ষাগুলি ভেঙে যায় কারণ আমি পরিবর্তন করছি যেখানে নির্দিষ্ট যুক্তিটি করা হয় এবং পরীক্ষাগুলি পূর্ববর্তী কাঠামোটিকে সামনে রেখে লেখা হবে (বিদ্রূপযুক্ত নির্ভরতা ইত্যাদি) [ ..]

আমি অগত্যা একটি সমস্যা হিসেবে এই দেখতে করবেন না: পরীক্ষার লিখুন, আপনার কোড গঠন পরিবর্তন, এবং তারপর পরীক্ষা গঠন সমন্বয় এছাড়াও । এটি আপনাকে নতুন প্রতিক্রিয়াটি পুরানোটির চেয়ে আসলে ভাল কিনা তা সরাসরি প্রতিক্রিয়া জানাবে, কারণ যদি এটি হয় তবে সমন্বিত পরীক্ষাগুলি লিখতে আরও সহজ হবে (এবং এইভাবে পরীক্ষাগুলি পরিবর্তন করা অপেক্ষাকৃত সহজবোধ্য হওয়া উচিত, সদ্য চালু হওয়ার ঝুঁকি হ্রাস করে বাগ পরীক্ষা পাস)।

এছাড়াও, যেমন অন্যরা ইতিমধ্যে লিখেছেন: খুব বিস্তারিত পরীক্ষা লিখবেন না (কমপক্ষে শুরুতে নয়)। বিমূর্ততার উচ্চ স্তরে থাকার চেষ্টা করুন (এইভাবে আপনার পরীক্ষাগুলি সম্ভবত রিগ্রেশন বা এমনকি সংহতকরণ পরীক্ষাগুলি হিসাবে আরও ভাল বৈশিষ্ট্যযুক্ত হবে)।


1
এই. পরীক্ষাগুলি দেখতে ভয়ঙ্কর দেখাবে , তবে তারা বিদ্যমান আচরণটি আবরণ করবে। তারপরে, কোডটি যেমন রিফ্যাক্টর হয়, তেমনি পরীক্ষাগুলিও লক স্টেপে করুন। আপনার গর্বিত কিছু না হওয়া পর্যন্ত পুনরাবৃত্তি করুন। ++
রাবারডাক

1
আমি এই বইয়ের দুটি সুপারিশই দ্বিতীয় - দ্বিতীয় টেস্টলেস কোডটি যখন মোকাবেলা করতে হয় তখন আমার সবসময়ই কাছে থাকে hand
টবি স্পিড

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ছাড়া পরীক্ষার অবিচ্ছিন্ন। পরীক্ষাগুলি দেখায় যে কার্যকারিতাটি একই (এখন পর্যন্ত আপনি এটি পরীক্ষা করেছেন)।


আপনি যদি ডকুমেন্ট সার্ভিস পরীক্ষা করে দেখেন এবং ডিএওকে উপহাস না করেন তবে এটি মোটেই ইউনিট পরীক্ষা নয়। এটি ইউনিটরি এবং ইন্টিগ্রেশন পরীক্ষার মধ্যে কিছু। তাই না?
লাইভ

7
@ লাইভ, লোকেরা ইউনিট পরীক্ষার শব্দটি কীভাবে ব্যবহার করে তাতে প্রকৃতপক্ষে উল্লেখযোগ্য বৈচিত্র রয়েছে। কেউ কেউ কেবল কঠোরভাবে বিচ্ছিন্ন পরীক্ষা বোঝাতে এটি ব্যবহার করে। অন্যদের মধ্যে দ্রুত চলমান যে কোনও পরীক্ষা অন্তর্ভুক্ত থাকে। কিছু পরীক্ষার কাঠামোয় চলমান কিছু অন্তর্ভুক্ত করে। তবে শেষ পর্যন্ত, আপনি ইউনিট পরীক্ষার শব্দটি কীভাবে সংজ্ঞায়িত করতে চান তা বিবেচ্য নয়। প্রশ্নগুলি কী পরীক্ষাগুলি কার্যকর তা তাই আমরা ইউনিট পরীক্ষাটি ঠিক কীভাবে সংজ্ঞায়িত করি তা দ্বারা আমাদের বিভ্রান্ত হওয়া উচিত নয়।
উইনস্টন ইওয়ার্ট

দুর্দান্ত পয়েন্ট যা দেখায় যে দরকারীতা হ'ল সবচেয়ে গুরুত্বপূর্ণ। কেবলমাত্র ইউনিট পরীক্ষার জন্য অতিমাত্রায় তুচ্ছ আলগোরিদিমগুলির জন্য অমিতব্যয়ী ইউনিট পরীক্ষাগুলি কেবল সময়ের ব্যয় এবং মূল্যবান সংস্থানগুলির ব্যয় না হলে ভালয়ের চেয়ে আরও বেশি ক্ষতি করে। এটি প্রায় সব কিছুর ক্ষেত্রেই প্রয়োগ করা যেতে পারে এবং এমন একটি জিনিস যা আমি আমার ক্যারিয়ারের শুরুর দিকে জানতাম।
লি

3

যেমনটি আপনি বলেছেন, আপনি যদি আচরণটি পরিবর্তন করেন তবে এটি রূপান্তরকৃত এবং অশোধক নয় or আপনি কোন স্তরে আচরণ পরিবর্তন করেন তা হ'ল তারতম্য।

যদি সর্বোচ্চ স্তরে কোনও আনুষ্ঠানিক পরীক্ষা না হয় তবে ক্লায়েন্টদের (কলিং কোড বা মানব) আপনার কোডটি কাজ হিসাবে বিবেচিত হওয়ার জন্য পুনরায় নকশার পরে একই থাকার দরকার রয়েছে এমন প্রয়োজনীয়তার একটি সেট চেষ্টা করুন। এটি আপনার প্রয়োগ করা দরকার পরীক্ষার মামলার তালিকা।

পরীক্ষার কেসগুলির পরিবর্তনের প্রয়োজনে বাস্তবায়নের পরিবর্তন সম্পর্কে আপনার প্রশ্নের সমাধান করার জন্য আমি আপনাকে পরামর্শ দিচ্ছি যে আপনি ডেট্রয়েট (ধ্রুপদী) বনাম লন্ডন (মকবিদ) টিডিডি দেখুন। মার্টিন ফোলার তাঁর দুর্দান্ত নিবন্ধে এ সম্পর্কে কথা বলেছেন মকস স্টাবস নয় তবে অনেকের মতামত রয়েছে। আপনি যদি সর্বোচ্চ স্তরে শুরু করেন, যেখানে আপনার বাহ্যিকগুলি পরিবর্তন করতে পারে না এবং আপনার পথে কাজ করতে পারে তবে আপনি যদি এমন একটি স্তরে পৌঁছান না যে পর্যন্ত সত্যিকারের পরিবর্তন দরকার needs

কোনও পরীক্ষা ছাড়াই এটি কঠিন হতে চলেছে এবং আপনি দ্বৈত কোড পাথ (এবং পার্থক্য রেকর্ডিং) এর মাধ্যমে রান ক্লায়েন্টদের বিবেচনা করতে চাইতে পারেন যতক্ষণ না আপনি নিশ্চিত হয়ে উঠতে পারেন যে আপনার নতুন কোডটি যা করা দরকার ঠিক তা করছে।


3

এখানে আমার পদ্ধতির। সময়ের সাথে এটির ব্যয় হয়েছে কারণ এটি 4 টি পর্যায়ক্রমে রিফেক্টর-পরীক্ষা।

আমি যা প্রকাশ করতে যাচ্ছি তাতে প্রশ্নের জটিলতার চেয়ে আরও জটিলতার সাথে উপাদানগুলিতে আরও ভাল স্যুট হতে পারে।

যাইহোক কৌশলটি কোনও ইন্টারফেস (ডিএও, পরিষেবাদি, নিয়ন্ত্রণকারী, ...) দ্বারা সাধারণকরণের জন্য কোনও উপাদান প্রার্থীর পক্ষে বৈধ।

1. ইন্টারফেস

মাই ডকুমেন্টস সার্ভিস থেকে সমস্ত পাবলিক পদ্ধতি সংগ্রহ করতে দেয় এবং সেগুলি একসাথে একটি ইন্টারফেসে রাখি। এই ক্ষেত্রে. এটি ইতিমধ্যে বিদ্যমান থাকলে, কোনও নতুন সেট করার পরিবর্তে সেটি ব্যবহার করুন

public interface DocumentService {

   List<Document> getAllDocuments();

   //more methods here...
}

তারপরে আমরা মাইডোকামেন্টস সার্ভিসকে এই নতুন ইন্টারফেসটি প্রয়োগ করতে বাধ্য করি

এ পর্যন্ত সব ঠিকই. কোনও বড় পরিবর্তন আনা হয়নি, আমরা বর্তমান চুক্তিকে সম্মান জানাই এবং আচরণগুলি অচেনা থেকে যায়।

public class MyDocumentService implements DocumentService {

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

2. উত্তরাধিকার কোড ইউনিট পরীক্ষা

এখানে আমাদের কঠোর পরিশ্রম আছে। একটি পরীক্ষা স্যুট সেট আপ করতে। আমাদের যতটা সম্ভব কেস সেট করা উচিত: সফল মামলা এবং ত্রুটির ক্ষেত্রেও। এগুলি সর্বশেষ ফলাফলের মানের জন্য রয়েছে।

এখন, মাইডোকামেন্টস সার্ভিস পরীক্ষা করার পরিবর্তে আমরা ইন্টারফেসটি চুক্তি হিসাবে পরীক্ষা করার জন্য ব্যবহার করতে যাচ্ছি।

আমি বিশদে যাচ্ছি না, তাই যদি আমার কোডটি খুব সাধারণ বা খুব অজ্ঞানময় মনে হয় তবে আমাকে ক্ষমা করুন

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
    } 
 }

এই পর্যায়ে এই পদ্ধতির অন্য যে কোনও সময়ের চেয়ে বেশি সময় নেয়। এবং এটি সর্বাধিক গুরুত্বপূর্ণ কারণ এটি ভবিষ্যতের তুলনার জন্য বিন্দুটি সেট করবে।

দ্রষ্টব্য: কোনও বড় পরিবর্তন করার কারণে এবং আচরণটি অচেনা থেকে যায়। আমি এসসিএম-এ এখানে একটি ট্যাগ করার পরামর্শ দিই। ট্যাগ বা শাখা কোনও বিষয় নয়। শুধু একটি সংস্করণ না।

আমরা এটি রোলব্যাকস, সংস্করণগুলির তুলনাগুলির জন্য চাই এবং এটি পুরানো কোড এবং নতুনটির সমান্তরাল মৃত্যুদন্ডের জন্য হতে পারে।

৩. রিফ্যাক্টরিং

রিফ্যাক্টর একটি নতুন উপাদান হিসাবে বাস্তবায়ন হতে চলেছে। আমরা বিদ্যমান কোডটিতে কোনও পরিবর্তন করব না। যেমন কপি ও পেস্ট করতে প্রথম পদক্ষেপ হিসেবে সহজ MyDocumentService এবং তা নামান্তর CustomDocumentService (উদাহরণস্বরূপ)।

নতুন ক্লাস ডকুমেন্ট সার্ভিস বাস্তবায়ন করতে থাকে । তারপরে যান এবং সমস্ত ডকুমেন্টস () এ রিফ্যাক্টরিজ করুন । (এক সাথে শুরু করা যাক। পিন-রিফ্যাক্টর)

ডিএওর ইন্টারফেস / পদ্ধতিতে এটির জন্য কিছু পরিবর্তন প্রয়োজন হতে পারে। যদি তা হয় তবে বিদ্যমান কোডটি পরিবর্তন করবেন না। ডিএও ইন্টারফেসে আপনার নিজস্ব পদ্ধতি প্রয়োগ করুন। পুরনো কোড অ্যানোটেট্ অসমর্থিত এবং আপনি পরে কি সরিয়েছেন করা উচিত জানতে হবে।

বিদ্যমান বাস্তবায়ন ভঙ্গ / পরিবর্তন না করা গুরুত্বপূর্ণ। আমরা উভয় পরিষেবা সমান্তরালভাবে কার্যকর করতে এবং তারপরে ফলাফলগুলি তুলনা করতে চাই।

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. ডকুমেন্টসোর্সেসটেষ্টসুট আপডেট করা

ঠিক আছে, এখন সহজ অংশ। নতুন উপাদানগুলির পরীক্ষা যুক্ত করতে add

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);
    } 
 }

এখন আমাদের কাছে পুরানো রেজাল্ট এবং নতুন ফলাফল দুটিই স্বতন্ত্রভাবে বৈধ হয়েছে তবে আমরা একে অপরের সাথে তুলনাও করতে পারি। এই শেষ বৈধতা alচ্ছিক এবং এটি ফলাফলের উপর নির্ভরশীল। এটি তুলনাযোগ্য নাও হতে পারে।

এইভাবে দুটি সংগ্রহের তুলনা করার জন্য খুব বেশি সাডেস না তৈরি করতে পারে তবে অন্য যে কোনও ধরণের অবজেক্টের (পজোস, ডেটা মডেল সত্তা, ডিটিও, র্যাপার্স, নেটিভ টাইপস) এর জন্য এটি বৈধ হবে)

নোট

আমি ইউনিট টেস্টগুলি কীভাবে করব বা মক ল্যাবগুলি কীভাবে ব্যবহার করব তা বলার সাহস করব না। আপনার কীভাবে চুল্লিটি করতে হবে তা বলার মতো সাহসও আমি করি না। আমি যা করতে চেয়েছিলাম তা হ'ল একটি বৈশ্বিক কৌশল প্রস্তাব করা। কীভাবে এটি এগিয়ে নেওয়া যায় তা আপনার উপর নির্ভর করে। আপনি কোডটি ঠিক কীভাবে জানেন, এর জটিলতা এবং যদি এমন কৌশলটি চেষ্টা করার মতো হয়। সময় এবং সংস্থান সম্পর্কিত বিষয়গুলি এখানে গুরুত্বপূর্ণ। ভবিষ্যতে এই পরীক্ষাগুলি থেকে আপনি কী প্রত্যাশা করেন তাও গুরুত্বপূর্ণ।

আমি একটি উদাহরণ দিয়ে আমার উদাহরণগুলি শুরু করেছি এবং আমি ডিএও এবং এর সাথে অনুসরণ করব। নির্ভরতা স্তরের গভীরে চলে যাওয়া। কমবেশি এটি আপ-ডাউন কৌশল হিসাবে বর্ণনা করা যেতে পারে । তবে ছোটখাটো পরিবর্তন / পুনরুদ্ধারকারীদের ( ট্যুরের উদাহরণে যেমনটি উদ্ঘাটিত করা হয়েছে ) এর জন্য, নীচের অংশটি কাজটি আরও সহজ করে দেয়। কারণ পরিবর্তনের সুযোগ কম।

অবশেষে, অবহেলিত কোডটি সরিয়ে ফেলা এবং পুরানো নির্ভরতা নতুনকে পুনর্নির্দেশ করা আপনার পক্ষে।

অবচয় পরীক্ষাগুলিও সরান এবং কাজ শেষ হয়। আপনি যদি পুরানো সমাধানটিকে এর পরীক্ষাগুলির সাথে সংস্করণিত করেন তবে আপনি যে কোনও সময় একে অপরের সাথে চেক এবং তুলনা করতে পারেন।

এতগুলি কাজের ফলস্বরূপ, আপনার লিগ্যাসি কোড পরীক্ষা করা হয়েছে, যাচাইকৃত এবং সংস্করণ রয়েছে। এবং নতুন কোড, পরীক্ষিত, যাচাইকৃত এবং সংস্করণে প্রস্তুত।


3

tl; dr ইউনিট পরীক্ষা লিখবেন না। আরও উপযুক্ত পর্যায়ে পরীক্ষা লিখুন।


রিফ্যাক্টরিংয়ের আপনার কাজের সংজ্ঞা দেওয়া হয়েছে:

আপনার সফ্টওয়্যার যা করে তা আপনি পরিবর্তন করেন না, এটি কীভাবে তা পরিবর্তন করে

নেই খুব চওড়া বর্ণালী। এক প্রান্তে নির্দিষ্ট পদ্ধতিতে একটি স্ব-অন্তর্ভুক্ত পরিবর্তন সম্ভবত সম্ভবত আরও দক্ষ অ্যালগরিদম ব্যবহার করে। অন্য প্রান্তে অন্য ভাষায় পোর্ট করা হচ্ছে।

রিফ্যাক্টরিং / পুনরায় নকশার যে স্তরের কাজ সম্পাদন করা হচ্ছে, সেই স্তর বা উচ্চতর যেগুলি পরিচালনা করে সেগুলি পরীক্ষা করা গুরুত্বপূর্ণ।

স্বয়ংক্রিয় পরীক্ষাগুলি প্রায়শই স্তর অনুসারে শ্রেণিবদ্ধ করা হয়:

  • ইউনিট পরীক্ষা - পৃথক উপাদান (শ্রেণি, পদ্ধতি)

  • সংহতকরণ পরীক্ষা - উপাদানগুলির মধ্যে মিথস্ক্রিয়া

  • সিস্টেম পরীক্ষা - সম্পূর্ণ প্রয়োগ

পরীক্ষার স্তরটি লিখুন যা রিফ্যাক্টরিং মূলত অচ্ছুত সহ্য করতে পারে।

চিন্তা করুন:

রিফ্যাক্টরিংয়ের আগে এবং পরে অ্যাপ্লিকেশনটির কী কী অপরিহার্য, সর্বজনীন দৃশ্যমান আচরণ থাকবে ? আমি কীভাবে পরীক্ষা করতে পারি যে জিনিসটি এখনও একইরকম কাজ করে?


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);
}

এই উদাহরণটি নিজেকে পুনরাবৃত্তি করবেন না নীতি অনুসারে করা একটি সুস্পষ্ট স্পষ্ট জিনিস বলে মনে হয় তবে এটি অন্য ক্ষেত্রে এটি এতটা সুস্পষ্ট নাও হতে পারে। সুবিধাটি ডিআরওয়াই ছাড়িয়ে যায় - আসল সুবিধা হ'ল পরীক্ষার আওতাধীন কোড থেকে পরীক্ষাগুলির decoupling।

অবশ্যই, এই কৌশলটি সব পরিস্থিতিতেই উপযুক্ত নয়। উদাহরণস্বরূপ, POCOs / POJOs এর জন্য অ্যাডাপ্টারগুলি লেখার কোনও কারণ নেই কারণ তাদের সত্যিকারের এমন কোনও API নেই যা পরীক্ষার কোড থেকে স্বতন্ত্রভাবে পরিবর্তিত হতে পারে। এছাড়াও আপনি যদি অল্প সংখ্যক পরীক্ষাগুলি লিখছেন তবে তুলনামূলকভাবে বড় অ্যাডাপ্টার স্তরটি প্রচেষ্টা ব্যর্থ হতে পারে।

আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.