পাঠ ৪৪ · ৫৮-এর মধ্যে · মডিউল ১০
Home / Courses / Software Engineering Principles & Git / টেস্টিং লেভেল

টেস্টিং লেভেল — ইউনিট, ইন্টিগ্রেশন, সিস্টেম, অ্যাকসেপ্টেন্স

Testing levels — unit, integration, system, acceptance
৮ মিনিট পড়া মধ্যম · Intermediate Python কোডসহ সম্পূর্ণ বাংলায়

এই পাঠে যা শিখবেন

  • টেস্টিং পিরামিডের চারটি স্তর, প্রতিটির স্কোপ, গতি ও উদ্দেশ্য
  • কেন ইউনিট টেস্ট পারে না এমন সমস্যা ইন্টিগ্রেশন টেস্ট ধরতে পারে
  • পিরামিডের প্রমিত, সুস্থ অনুপাত এবং "উল্টানো পিরামিড" অ্যান্টি-প্যাটার্ন কেন সমস্যাযুক্ত
  • একটি বাস্তব TestSuite ক্লাস যা pyramid_health_check() দিয়ে স্বাস্থ্যকর ও অস্বাস্থ্যকর টেস্ট-স্যুট শনাক্ত করে

১ · টেস্টিং পিরামিডের চারটি স্তর

টেস্টিং একটি একক, একচেটিয়া কাজ নয় — এটি বিভিন্ন স্কোপ ও গ্রানুলারিটি-তে করা হয়, ছোট/সবচেয়ে নির্দিষ্ট থেকে বড়/সবচেয়ে সামগ্রিক পর্যন্ত। এই স্তরগুলোর প্রমিত কাঠামোকে "টেস্টিং পিরামিড" বলা হয়, এবং এই পাঠ M10-এর বাকি অংশের (L45-L48) জন্য সেই মানচিত্র তৈরি করে।

অ্যাকসেপ্টেন্স সিস্টেম ইন্টিগ্রেশন ইউনিট — সবচেয়ে বেশি, সবচেয়ে দ্রুত
নিচের স্তর যত চওড়া, তত বেশি সংখ্যক ও দ্রুত টেস্ট থাকা উচিত — উপরে ওঠার সাথে সাথে টেস্টের সংখ্যা কমে, কিন্তু প্রতিটি টেস্টের স্কোপ ও এক্সিকিউশন সময় বাড়ে।
ইউনিট টেস্টিং
একটি একক, ছোট কোড ইউনিট (সাধারণত একটি ফাংশন/মেথড) বিচ্ছিন্নভাবে পরীক্ষা করে — দ্রুত, এবং ব্যর্থ হলে ঠিক কী ভাঙল তা নির্দিষ্টভাবে বলে দেয় (L47-এর মকিং এই বিচ্ছিন্নতা নিশ্চিত করে)।
ইন্টিগ্রেশন টেস্টিং
একাধিক ইউনিট/কম্পোনেন্ট একসাথে ঠিকভাবে কাজ করছে কিনা যাচাই করে (যেমন M6/L25-এর বিজনেস-লজিক লেয়ার ডেটা-অ্যাক্সেস লেয়ারের সাথে সঠিকভাবে ইন্টার‌অ্যাক্ট করছে কিনা) — এমন সমস্যা ধরে যা বিচ্ছিন্ন ইউনিট টেস্ট ধরতে পারে না।
সিস্টেম টেস্টিং
সম্পূর্ণ, পুরোপুরি ইন্টিগ্রেটেড সিস্টেমকে একটি সামগ্রিক ইউনিট হিসেবে পরীক্ষা করে, M3-এর রিকোয়ারমেন্ট পূরণ হচ্ছে কিনা যাচাই করে — বাস্তব ব্যবহারকারীর অভিজ্ঞতার কাছাকাছি।
অ্যাকসেপ্টেন্স টেস্টিং
M3/L13-এর ইউজার-স্টোরি অ্যাকসেপ্টেন্স ক্রাইটেরিয়ার সরাসরি প্রয়োগ — সিস্টেম ব্যবসা/ইউজারের প্রকৃত প্রত্যাশা পূরণ করছে কিনা, প্রায়ই "সম্পন্ন" ঘোষণা করার আগে চূড়ান্ত গেট।

২ · কেন এই আকৃতি — "অনেক ইউনিট, কম সিস্টেম"

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

একটি "উল্টানো পিরামিড" আসলে কী ক্ষতি করে

একটি টিম যদি বেশিরভাগ যাচাই সিস্টেম/অ্যাকসেপ্টেন্স টেস্টের উপর নির্ভর করে, তাহলে প্রতিটি টেস্ট রান অনেক ধীর হয়ে যায় (ঘণ্টার হিসেবে, মিনিটের নয়) — যা ঘন ঘন টেস্ট চালানো (দিনে বহুবার, L46-এর "Fast" নীতি) অব্যবহারিক করে তোলে। এছাড়া একটি সিস্টেম টেস্ট ব্যর্থ হলে, ঠিক কোন কম্পোনেন্টে সমস্যা তা বোঝা কঠিন হয়ে পড়ে — যেখানে একটি ব্যর্থ ইউনিট টেস্ট সরাসরি নির্দিষ্ট ফাংশনটির দিকে ইঙ্গিত করে।

নিচের কোড সেলে আমরা একটি বাস্তব TestSuite ক্লাস লিখব যা প্রতিটি স্তরের টেস্ট-সংখ্যা ট্র্যাক করে, এবং একটি pyramid_health_check() মেথড লিখব যা যাচাই করে সংখ্যাগুলো প্রত্যাশিত অনুপাত (unit > integration > system) অনুসরণ করছে কিনা।

Python
# একটি টেস্ট স্যুটের আকৃতি স্বাস্থ্যকর নাকি "উল্টানো পিরামিড" -- সেটা যাচাই করা।

class TestSuite:
    def __init__(self, name, unit_count, integration_count, system_count):
        self.name = name
        self.unit_count = unit_count
        self.integration_count = integration_count
        self.system_count = system_count

    def pyramid_health_check(self):
        problems = []
        if not (self.unit_count > self.integration_count):
            problems.append(
                f"unit_count ({self.unit_count}) > integration_count ({self.integration_count}) হওয়া উচিত ছিল"
            )
        if not (self.integration_count > self.system_count):
            problems.append(
                f"integration_count ({self.integration_count}) > system_count ({self.system_count}) হওয়া উচিত ছিল"
            )
        is_healthy = len(problems) == 0
        return is_healthy, problems

healthy_suite = TestSuite("healthy_suite", unit_count=120, integration_count=30, system_count=8)
inverted_suite = TestSuite("inverted_suite", unit_count=10, integration_count=15, system_count=25)

for suite in (healthy_suite, inverted_suite):
    is_healthy, problems = suite.pyramid_health_check()
    print(f"{suite.name}: unit={suite.unit_count}, integration={suite.integration_count}, system={suite.system_count}")
    if is_healthy:
        print("  -> স্বাস্থ্যকর পিরামিড আকৃতি\n")
    else:
        print("  -> অস্বাস্থ্যকর:")
        for p in problems:
            print("     -", p)
        print()

    
লক্ষ্য করুন inverted_suite-এ ইউনিট টেস্ট (১০টি) সিস্টেম টেস্ট (২৫টি)-এর চেয়েও কম — ঠিক পিরামিডের বিপরীত আকৃতি। pyramid_health_check() এই দুটো ভাঙা শর্তই সঠিকভাবে শনাক্ত করে এবং নির্দিষ্টভাবে বলে দেয় কোন তুলনাটি ব্যর্থ হয়েছে — শুধু "স্বাস্থ্যকর নয়" বলার বদলে ঠিক কোথায় সমস্যা তা দেখায়।
মূল কথা · Key takeaway

টেস্টিং পিরামিডের চারটি স্তর ভিন্ন স্কোপে ভিন্ন ধরনের সমস্যা ধরে — কোনোটি অন্যটির বিকল্প নয়, একটি সুস্থ টেস্ট স্যুটে সব কয়টিরই দরকার হয়, কিন্তু সঠিক অনুপাতে। M10-এর বাকি পাঠগুলো (L45-L48) মূলত পিরামিডের সবচেয়ে চওড়া, সবচেয়ে গুরুত্বপূর্ণ ভিত্তি — ইউনিট টেস্টিং-এর উপর গভীরভাবে ফোকাস করবে।

ভাবনার প্রশ্ন

প্রতিটি প্রশ্ন নিজে কিছুক্ষণ ভাবুন — তারপর "→ উত্তর" চাপুন।

প্র ০১ একটি ইউনিট টেস্ট একটি ফাংশনকে সম্পূর্ণ বিচ্ছিন্নভাবে পরীক্ষা করে পাস করলেও, প্রোডাকশনে সেই একই কোড ব্যর্থ হতে পারে কোন পরিস্থিতিতে?

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

প্র ০২ M3/L13-এর ইউজার-স্টোরি অ্যাকসেপ্টেন্স ক্রাইটেরিয়ার সাথে এই পাঠের অ্যাকসেপ্টেন্স টেস্টিং-এর সম্পর্ক কী?

M3/L13-এ অ্যাকসেপ্টেন্স ক্রাইটেরিয়া ছিল একটি ইউজার-স্টোরির জন্য টেস্টেবল শর্তের তালিকা — "কবে এই কাজটি 'সম্পন্ন' বলা যাবে।" অ্যাকসেপ্টেন্স টেস্টিং হলো সেই ক্রাইটেরিয়াগুলোকেই আসলে চালিয়ে যাচাই করার প্রক্রিয়া — প্রতিটি অ্যাকসেপ্টেন্স ক্রাইটেরিয়ন প্রায়ই সরাসরি একটি অ্যাকসেপ্টেন্স টেস্ট কেসে পরিণত হয়। এটি একটি সরাসরি, ব্যবহারিক সংযোগ — একটি বিমূর্ত রিকোয়ারমেন্ট-লেখার কৌশল থেকে একটি কংক্রিট টেস্টিং প্র্যাকটিসে।

প্র ০৩ উপরের কোড সেলে healthy_suite-এর জন্য unit=120, integration=30 কেন যথেষ্ট নয় শুধু "unit বেশি" বলার জন্য — কেন integration ও system-এর তুলনাও আলাদাভাবে দরকার?

কারণ পিরামিড আকৃতিটি একটি শৃঙ্খলাবদ্ধ ক্রম দাবি করে — শুধু unit > integration নয়, বরং integration > system-ও সমানভাবে সত্য হতে হবে। একটি স্যুট যেখানে unit=120, integration=5, system=15 — সেখানে unit > integration ঠিক আছে, কিন্তু integration < system ভেঙে গেছে (এখনও একটি আংশিক উল্টানো আকৃতি)। তাই pyramid_health_check()-এ দুটো শর্তই আলাদাভাবে যাচাই করা প্রয়োজন — একটি একক তুলনা পুরো পিরামিড আকৃতির নিশ্চয়তা দেয় না।

অনুশীলন

  1. চিন্তা করুন: একটি ছোট, তিন-পৃষ্ঠার পার্সোনাল ব্লগ ওয়েবসাইটের জন্য কি সবগুলো টেস্টিং লেভেল (ইউনিট, ইন্টিগ্রেশন, সিস্টেম, অ্যাকসেপ্টেন্স) সমান গুরুত্বপূর্ণ, নাকি প্রজেক্টের আকার/জটিলতার উপর ভিত্তি করে কিছু লেভেলে বেশি বিনিয়োগ করা যুক্তিসঙ্গত?

    একটি ছোট, কম-জটিলতার প্রজেক্টে পূর্ণাঙ্গ চার-স্তরের টেস্টিং পিরামিড হয়তো অতিরিক্ত ওভারহেড — কয়েকটি মূল ইউনিট টেস্ট এবং হয়তো একটি হালকা সিস্টেম/স্মোক টেস্ট যথেষ্ট হতে পারে। M13-এর প্রজেক্ট ম্যানেজমেন্ট থিমের সাথে সামঞ্জস্যপূর্ণভাবে বলা যায়: টেস্টিং বিনিয়োগও ঝুঁকি ও জটিলতার সাথে সমানুপাতিক হওয়া উচিত — একটি বড়, বহু-টিমের, ব্যবসা-সমালোচনামূলক সিস্টেমে চারটি স্তরেই ভারী বিনিয়োগ যুক্তিসঙ্গত, কিন্তু একটি ছোট ব্লগে তা অপ্রয়োজনীয় জটিলতা যোগ করতে পারে (M4/L15-এর YAGNI নীতির সাথেও সামঞ্জস্যপূর্ণ)।

  2. পরীক্ষা করুন: উপরের কোড সেলে একটি তৃতীয় TestSuite তৈরি করুন যেখানে unit=50, integration=50, system=10 (integration ঠিক unit-এর সমান, বেশি নয়)। pyramid_health_check() এটিকে স্বাস্থ্যকর বলবে, নাকি অস্বাস্থ্যকর?

    অস্বাস্থ্যকর বলবে — কারণ কোডে ব্যবহৃত শর্তটি কঠোরভাবে "বেশি" (>), "বেশি বা সমান" (>=) নয়। যখন unit_count (৫০) integration_count (৫০)-এর চেয়ে বেশি নয় (সমান), তখন not (self.unit_count > self.integration_count) সত্য হয়ে যায়, এবং একটি সমস্যা যোগ হয়। এটি প্রমাণ করে ফাংশনটি প্রকৃতপক্ষে কঠোর, নিখুঁত পিরামিড আকৃতিই দাবি করে — একটি সমতল বা প্রায়- সমতল অনুপাতকেও এটি "অস্বাস্থ্যকর" হিসেবে চিহ্নিত করে, শুধু সম্পূর্ণ উল্টানো অনুপাতকে নয়।

আরও পড়ুন · ABCL TECH-এ আপনার পরবর্তী পদক্ষেপ

আগের পাঠ
Git ট্যাগ, রিলিজ ও সিমান্টিক ভার্সনিং