টিডিডি – টেস্ট ড্রিভেন ডেভেলাপমেন্ট

Test Driven Development (TDD)-এর বাংলা অর্থ হচ্ছে টেস্ট চালিত ডেভেলাপমেন্ট। এখানে টেস্ট বলতে সাধারণত সফটওয়্যার টেস্ট (ইউনিট টেস্ট এবং/অথবা ইন্টিগ্রেশন টেস্ট) এবং ডেভেলাপমেন্ট বলতে সফটওয়্যার ডেভেলাপমেন্ট বোঝানো হয়। এটা হচ্ছে সফটওয়্যার ডেভেলাপমেন্ট করার একটি পদ্ধতি। কিছু কিছু বিষয় আছে, যেগুলো নিয়ে সবাই কথা বলতে ভালোবাসে, সবাই হু-হু করে মাথা নাড়ায় যে এটি খুবই ভালো, কিন্তু কেউ আসলে বাস্তব জীবনে বা নিজের জীবনে সেটা অনুসরণ করে না, টিডিডি এমনই এক জিনিস। সারা পৃথিবীতে অনেক সফটওয়্যার ডেভেলাপার এটি নিয়ে কথা বলে, বিভিন্ন কনফারেন্সে আলোচনা হয়, ব্লগ পোস্ট হয়, কিন্তু খুবই কম সংখ্যক সফটওয়্যার ডেভেলাপার এই পদ্ধতি অনুসরণ করে। তাহলে এই বিষয় নিয়ে লিখছি কেন? কারণ দেশে-বিদেশে ইন্টারভিউতে এটি খুবই কমন একটি প্রশ্ন এবং তাই এই বিষয়ে সবার বেসিক ধারণা থাকা উচিত। তারপরে কেউ ব্যক্তিগতভাবে আগ্রহী হলে সে ইন্টারনেট ঘেঁটে টিডিডি সম্পর্কে আরো জেনে নিবে।

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

ধরা যাক, আমাকে কেউ রিকোয়ারমেন্ট দিলো যে, একটি প্রোগ্রাম লিখতে হবে, যেখানে শিক্ষার্থীদের নম্বর ইনপুট দিলে লেটার গ্রেড আউটপুট দেখাবে। তো একটু আলাপ-সালাপ করে আমি রিকোয়ারমেন্ট বুঝে নিলাম এরকম :

  • ৫০ নম্বরের নিচে পেলে ফেইল, অর্থাৎ F গ্রেড।
  • ৫০ থেকে ৫৯ এর মধ্যে নাম্বার পেলে B গ্রেড।
  • ৬০ থেকে ৬৯ এর মধ্যে নাম্বার পেলে B+ গ্রেড।
  • ৭০ থেকে ৭৯ এর মধ্যে নাম্বার পেলে A- গ্রেড।
  • ৮০ থেকে ৮৯ এর মধ্যে নাম্বার পেলে A গ্রেড।
  • ৯০ কিংবা তার বেশি পেলে A+ গ্রেড।

কেউ ল্যাঙ্গুয়েজ ঠিক না করে দিলে আমি পাইথন ব্যবহার করতেই পছন্দ করি, তাই আমার প্রিয় ল্যাঙ্গুয়েজ পাইথনে আমি শুরুতেই একটি ফাংশন লিখে ফেললাম।

def calculate_grade(marks):
    """ Takes marks as input and returns grade as output """
    return "F"

এখন আমি ফাংশনের জন্য টেস্ট কোড লিখে ফেললাম এরকম :

def test_calculate_grade():
    assert calculate_grade(0) == "F"
    assert calculate_grade(30) == "F"
    assert calculate_grade(49) == "F"

তারপরে টেস্ট রান করবো : pytest grade.py। আউটপুট আসবে সব টেস্ট পাশ।

1 passed in 0.01 seconds

কিন্তু সমস্যা হচ্ছে এভাবে কাজ করলে টিডিডি হবে না। টিডিডির ক্ষেত্রে প্রথমে আমার টেস্ট কোড লিখতে হবে, অর্থাৎ test_calculate_grade() ফাংশনটি লিখতে হবে, সেখানে এক বা একাধিক টেস্ট কেইস থাকতে হবে আর তারপরে calculate_grade() ফাংশনটি লেখা হবে। যেই ব্যাপারটি খুবই গুরুত্বপূর্ণ, সেটি হচ্ছে টেস্ট কেস পাশ করানোর জন্য নূন্যতম যতটুকু কোড লেখা দরকার, আমরা ততটুকু কোডই লিখবো। তাহলে আমি প্রথমে ওপরের test_calculate_grade() ফাংশনটি লিখবো, তারপরে নিচের কোড লিখবো।

def calculate_grade(marks):
    """ Takes marks as input and returns grade as output """
    pass

এবারে টেস্ট রান করাবো (সেই আগের কমান্ড pytest grade.py)। আউটপুট আসবে এমন :

>       assert calculate_grade(0) == "F"
E       assert None == 'F'
E        +  where None = calculate_grade(0)
grade.py:9: AssertionError

তাহলে দেখা যাচ্ছে, প্রথমে আমার কোডে কিছু নেই, তাই 0 নম্বর পাওয়ার পরেও F রিটার্ন না করে None রিটার্ন করছে। তাহলে এই কেস পাশ করানোর জন্য এখন আমি ফাংশনটি আপডেট করবো :

def calculate_grade(marks):
    """ Takes marks as input and returns grade as output """
    return "F"

এবারে টেস্ট রান করলে তিনটি টেস্ট কেইসই পাশ করবে। এখন আমি B গ্রেডের জন্য দুইটি টেস্ট কেইস যুক্ত করবো।

def test_calculate_grade():
    assert calculate_grade(0) == "F"
    assert calculate_grade(30) == "F"
    assert calculate_grade(49) == "F"
    assert calculate_grade(50) == "B"
    assert calculate_grade(59) == "B"

এখন টেস্ট রান করলে আউটপুট আসবে এরকম :

>       assert calculate_grade(50) == "B"
E       assert 'F' == 'B'
E         - F
E         + B

grade.py:12: AssertionError

এখন এই টেস্ট কেস পাশ করাতে হবে। এজন্য আমরা এভাবে প্রোগ্রাম লিখবো :

 def calculate_grade(marks):
    """ Takes marks as input and returns grade as output """
    if marks < 50:
        return "F"
    else:
        return "B"

এখন টেস্ট রান করলে সবগুলো কেস পাশ করবে। এখন বাকী সব কেস যোগ করে ফেলবো।

 def test_calculate_grade():
    assert calculate_grade(0) == "F"
    assert calculate_grade(30) == "F"
    assert calculate_grade(49) == "F"
    assert calculate_grade(50) == "B"
    assert calculate_grade(59) == "B"
    assert calculate_grade(60) == "B+"
    assert calculate_grade(69) == "B+"
    assert calculate_grade(70) == "A-"
    assert calculate_grade(79) == "A-"
    assert calculate_grade(80) == "A"
    assert calculate_grade(89) == "A"
    assert calculate_grade(90) == "A+"
    assert calculate_grade(100) == "A+"

এখন একটা একটা করে টেস্টকেস পাশ করানোর জন্য আমি কোড লিখতে থাকবো, এবং প্রতিটি টেস্ট কেস পাশ করে কি না, সেটি দেখার জন্য প্রতিবার টেস্ট রান করাবো। একসময় আমার কোড সব টেস্ট কেস পাশ করবে এবং তখন সেটি দেখতে নিচের মতো হবে :

 def calculate_grade(marks):
    """ Takes marks as input and returns grade as output """
    if marks < 50:
        return "F"
    elif marks < 60:
        return "B"
    elif marks < 70:
        return "B+"
    elif marks < 80:
        return "A-"
    elif marks < 90:
        return "A"
    else:
        return "A+"

এভাবে দেখতে দেখতে আমার কোডটি সম্পূর্ণ তৈরি হয়ে গেল। টেস্ট ড্রিভেন ডেভেলাপমেন্টকে অনেক সময় টেস্ট ফার্স্ট ডেভেলাপমেন্টও বলা হয়, কারণ এখানে প্রথমে টেস্ট কেস লিখতে হয়। টিডিডি-এর মূল ধারণা নিচের সহজ ছবিটির মাধ্যমেও মনে রাখা যায়:

image_thumb3
Test Driven Development (collected from the Internet)

আশা করি টেস্ট ড্রিভেন ডেভেলামপেন্টের ধারণা অনেকেরই কাজে লাগবে চাকরির ইন্টারভিউতে এবং কেউ কেউ হয়ত তার প্রজেক্টে এর প্রয়োগ শুরু করে দিবে। এখন একটি বিষয়। কেউ যদি ৫৯.৫ বা এরকম নাম্বার পায়, তখন তার গ্রেড কী হবে? এটি রিকোয়ারমেন্টে বলা নাই। কিন্তু পরে জানা গেল যে ভগ্নাংশ হলে সেটিকে সিলিং (ceiling) করতে হবে, মানে ৫৯ এর চেয়ে বড় কিন্তু ৬০-এর চেয়ে ছোট সব সংখ্যাকে ৬০ ধরতে হবে। তাহলে এখন আমরা যদি নিচের টেস্টকেস যোগ করি, তাহলে টেস্ট ফেইল করবে:

assert calculate_grade(59.3) == "B"

এই টেস্ট কেসের মতো আরো টেস্ট কেস তৈরি করা এবং সেগুলো পাশ করানোর কাজটুকু পাঠককে দেওয়া হলো।