কোড লিখা টেস্ট লিখার চেয়ে অনেক সহজ লাগে, এবং সেই এক-লাইন মেথড খুবি টেস্ট করা লাগবে কি? এটা যেন তुच্ছ! আপনি যে টেস্ট যোগ করবেন সেটি ডেভেলপমেন্ট টাইম ডাবল বা ট্রিপল করবে, এবং পরের বার কোড চেঞ্জ করলে আপনাকে টেস্টও চেঞ্জ করতে হবে। এটা যেন পুরোপুরি বর্জ্য, বিশেষ করে যখন আপনার এস্টিমেটে কম সময় বাকি থাকে।
তবে শীঘ্রই, আপনার কোড শুধু ২০% টেস্ট দিয়ে কভারড থাকে, এবং আপনার কোডে যেকোনো চেঞ্জ করা লাগে তাসের বাড়ির মাঝের লেয়ার বদলার চেষ্টা যেন পুরো জিনিসটা ধวัน না করে। কোথাও কিছু ভুল হয়েছে এবং যদিও আপনার নেওয়া ডিসিশনগুলো সময়মতো সঠিক মনে হত, আপনি তবুও একটি সম্পূর্ণ অনমেইনটেইনেবল কোডবেস পেয়ে গেলেন।
আপনি কীভাবে এখানে পৌঁছালেন? আপনি চেয়েছিলেন আপনার টেস্টগুলো একটি সেফটি নেট দেবে এবং আপনাকে কনফিডেন্টলি রিফ্যাক্টর করতে দেবে। সেগুলো আপনার কোড ভালো করার কথা ছিল! বদলে, আপনি ফিরে এলেন কম টেস্ট কভারেজের সাথে কোডে যা আপনি আর বুঝতে পারছেন না, এবং আপনার থাকা টেস্টগুলো কোড চেঞ্জ করা কঠিন করে দিয়েছে।
এটি একটি স্কিল ফেইলার নয়। এটি সেরা ডেভেলপারদেরও হয়। এটি একটি প্রসেস ফেইলার। আপনি কীভাবে নতুন ফিচার লিখেন সেটিতে কিছু চেঞ্জ নিয়ে, আপনি আপনার কোডকে রক্ষা করতে পারেন আপনার টেস্ট আপনাকে স্লো ডাউন করানো ছাড়া। আপনার টেস্ট আপনার কোডকে আরও বোঝার যোগ্য এবং ফ্লেক্সিবল করতে পারেন। আপনি কনফিডেন্সের সাথে আপনার কোড চেঞ্জ করতে পারবেন, জানвайки তার মাধ্যমে যেকোনো পাথ টেস্টেড।
আপনাকে ডিসিশন নেওয়ার প্রয়োজন নেই
আপনি যদি আপনার কীবোর্ডে বসে কোডের একটি টুকরো টেস্ট করা দরকার কিনা সেটি ডিসাইড করার চেষ্টা করেন, তবে আপনি ইতিমধ্যে ভুল পথে যাচ্ছেন। আপনাকে সবসময় "Test it!" ডিফল্ট রাখা উচিত। এমনকি যদি এটা টেস্ট করার জন্য খুব তুচ্ছ মনে হয়, টেস্ট লিখুন।
যদি কোডটি লিখা তুচ্ছ হয়, তবে টেস্ট করাও সহজ হওয়া উচিত। এবং জটিল কোড কখনো তত তুচ্ছ মনে হবে না যত তুচ্ছ এখন লাগছে ঠিক লিখার পর। আপনি কীভাবে জানবেন ছয় মাস পরেও এটি ততটুকু তুচ্ছ মনে হবে?
কিন্তু আপনি ওভারটেস্টও চান না
একজানা জায়েন্ট টেস্ট স্যুট নিজে একটি সমস্যা হতে পারে। ২০ মিনিট লাগে রান হওয়া টেস্টগুলো কোনো টেস্ট নেই এর মত, যেহেতু আপনি সেগুলো সবসময় রান করবেন না। (আপনি বলবেন আপনি করবেন, কিন্তু আমি জানি আপনি করবেন না)। আরও খারাপ, আপনার যদি খুব বেশি ব্রিটল টেস্ট থাকে, তবে রিফ্যাক্টরিং আরও পেইনফুল হয়ে যায় যা ছিল আগে, তাই আপনি সেটি করবেন না। আপনি মেথড পাবেন অধিকাংশ নভেল থেকে বড়।
কি এটি আমার আগের পয়েন্টের সাথে কন্ট্রাডিক্ট করে? আশ করি না। আপনার টেস্টগুলো সবসময় আপনার কোডের ইন্টারফেস-এ ফোকাস করা উচিত, ইমপ্লিমেন্টেশন-এ নয়। উদাহরণস্বরূপ:
class Cart
def initialize(item_params)
@line_items = Array(item_params).map {|item| LineItem.new(item[:name], item[:price])}
end
def total
@line_items.sum(&:price)
end
end
এখানে যেন লাগছে Cart ক্লাস এবং LineItem ক্লাস দুটোই টেস্ট করা দরকার। কিন্তু কি LineItem ক্লাস অন্য কही ব্যবহৃত হচ্ছে? যদি এটা শুধু Cart-এর একটি ইমপ্লিমেন্টেশন ডিটেইল হয়, এবং বাইরের দুনিয়ায় এক্সপোজড নয়, তবে এটিকে কতটুকু টেস্ট লাগে? কি এটাকে শুধু আপনার Cart ক্লাস থ্রু টেস্ট করা যাবে না?
রিফ্যাক্টরিং দিয়ে এক্সট্রাক্ট করা ক্লাসগুলোকে তাদের নিজস্ব টেস্ট স্যুট প্রয়োজন নেই। সেগুলো শুধু একটি ইমপ্লিমেন্টেশন ডিটেইল। শুধু তখন তাদের একস্ট্রা টেস্ট লাগে যখন সেগুলো নিজের ফোর্সে ব্যবহার হয়।
একটি পাবলিক ইন্টারফেসের বিপরীতে একটি গ্রেট টেস্ট স্যুট দিয়ে, আপনার ইমপ্লিমেন্টেশন চেঞ্জ করার ফ্লেক্সিবিলিটি থাকে আপনার সকল টেস্ট রি-রাইট করা ছাড়া। আপনি এটা খুব কম পরিশ্রমে করতে পারেন এমনকি একটি গড় টেস্ট স্যুট অবজেক্টের ইমপ্লিমেন্টেশনের বিপরীতে লিখার চেয়ে।
Test-Driven Development দিয়ে আপনার টেস্ট কস্ট এমর্টাইজ করুন
প্রথম সেকশনে, আপনি শিখেছেন যে আপনাকে সবকিছু টেস্ট করা উচিত। দ্বিতীয় সেকশনে, আপনি শিখেছেন যে আপনাকে শুধু পাবলিক ইন্টারফেস টেস্ট করা উচিত। এটি Test-Driven Development যা এই দুটি বিরোধী লক্ষ্যকে একসাথে আনে।
Test-Driven Development-এ, আপনার টেস্ট আপনার কোডের ডিজাইন এবং ইমপ্লিমেন্টেশন ড্রাইভ করে এই প্রসেস ফলো করে:
- একটি ফেইলিং টেস্ট লিখুন যা অনুমান করে যে আপনার দরকারি কোডটি ইতিমধ্যে আছে।
- টেস্ট পাস করার জন্য কোডের সিম্পলেস্ট ইমপ্লিমেন্টেশন লিখুন।
- ডুপ্লিকেশন রিমুভ করতে রিফ্যাক্টর করুন (অথবা কোডকে আরও এক্সপ্রেসিভ করুন)।
- আবার টেস্ট রান করুন (সেগুলো এখনও পাস করে কিনা দেখতে)।
- স্টেপ ১-এ ফিরে যান।
এই স্টেপগুলো ফলো করলে, আপনি সবকিছু টেস্ট করবেন (যেহেতু কোনো কোড ফেইলিং টেস্ট ছাড়া লিখা উচিত নয়), কিন্তু শুধু পাবলিক ইন্টারফেস টেস্ট করবেন (যেহেতু আপনি রিফ্যাক্টর করার পর নতুন টেস্ট লিখবেন না)।
এটা কখনো সেটা সহজ নয়। কিন্তু যাদের সাথে সবচেয়ে জটিল কোড টেস্ট-ড্রাইভ করা যায় সেগুলোও আছে।
TDD-র কিছু সাইড বেনিফিট:
- আপনি একটি আরও ফ্লেক্সিবল, টেস্টেড অবজেক্ট মডেল পাবেন (এটি যেন মূল বেনিফিট)।
- আপনার সিস্টেম বাই ডেফিনিশন টেস্টেবল, যা ভবিষ্যতের টেস্ট কম খরচিলা করে।
- আপনি আপনার টেস্টিং কস্ট ডেভেলপমেন্ট প্রসেসে ছড়িয়ে দেন, যা আপনার এস্টিমেট আরও এক্যুরেট করে।
- এটি আপনাকে ফ্লোতে রাখে, কারণ আপনাকে কখনো ডিসাইড করতে হয় না কী করবেন নেক্সট।
তাহলে আমি কীভাবে শুরু করব?
শুরু করা হার্ড পার্ট! একবার আপনি টেস্ট-ড্রাইভেন কোড লিখার রিদমে আসলে, বন্ধ করা কঠিন।
পরের বার আপনি একটি নতুন ফিচারে কাজ করবেন, উপরের TDD স্টেপগুলো ফলো করার চেষ্টা করুন। আপনি নিজেকে খুব কাছাকাছি ১০০% কোড কভারেজের সাথে পাবেন কমপক্ষে কাজ দিয়ে, আপনার একটি সলিড ফাউন্ডেশন থাকবে বিল্ড করার জন্য, এবং আপনাকে টোটাল কনফিডেন্স থাকবে যে আপনার টেস্ট স্যুট আপনাকে রক্ষা করবে যখন আপনাকে পরsaal বা আজই আপনার কোড চেঞ্জ করতে হবে।
যখন শেষ হয়ে যাবে, আমাকে ইমেইল পাঠান এবং বলুন কীভাবে গেল।
একটি সোজা টেস্টিং প্রসেস ফলো করে, আপনি কম সময় সিম্পল ডিসিশন নেওয়া এবং বাগ চেইস করার পিছনে বিতরবেন, এবং বেশি সময় সেই কোড লিখতে যেটি আপনার কাস্টমার এবং বিজনেসের প্রয়োজন সমাধান করে।