ইন্টিগ্রেশন টেস্টিং স্ট্র্যাটেজি
এই পাঠে যা শিখবেন
- ইন্টিগ্রেশন টেস্টিং কী এবং ইউনিট টেস্টিং থেকে এটি কীভাবে আলাদা
- বিগ ব্যাং, টপ-ডাউন, বটম-আপ ও স্যান্ডউইচ স্ট্র্যাটেজির সংজ্ঞা, সুবিধা ও অসুবিধা
- স্টাব ও ড্রাইভার — এই দুটো "প্লেসহোল্ডার" ঠিক কী কাজ করে এবং কোন স্ট্র্যাটেজিতে কোনটা লাগে
- একটি ছোট মডিউল ডিপেন্ডেন্সি গ্রাফ থেকে সত্যিকারের ইন্টিগ্রেশন অর্ডার গণনা করে দেখা
১ · চারটি ইন্টিগ্রেশন স্ট্র্যাটেজি
ধরা যাক একটি ছোট সিস্টেমে তিনটি মডিউল আছে — একটি UI লেয়ার, যেটি একটি Service লেয়ারকে কল করে, আর Service লেয়ারটি একটি Database লেয়ারকে কল করে। প্রতিটি মডিউল আলাদাভাবে ইউনিট-টেস্ট করা হয়ে গেছে ধরে নিন — এখন প্রশ্ন হলো, এদের একসাথে জোড়া লাগানোর সময় কোন ক্রমে টেস্ট করবেন।
সব মডিউল একসাথে জোড়া লাগিয়ে পুরো সিস্টেম একবারে টেস্ট করা। দ্রুত মনে হলেও, কিছু ভুল হলে ঠিক কোন মডিউলের দোষ তা খুঁজে বের করা কঠিন — কোনো ইন্টারমিডিয়েট প্রতিক্রিয়া থাকে না।
সবচেয়ে উঁচু-স্তরের মডিউল (যেমন UI) থেকে শুরু করে ধীরে ধীরে নিচের দিকে নামা। যে নিচের মডিউল এখনও আসল হিসেবে ইন্টিগ্রেট হয়নি, তার জায়গায় একটি স্টাব (canned উত্তর দেওয়া placeholder) বসানো হয়।
সবচেয়ে নিচু-স্তরের মডিউল (যেমন Database) থেকে শুরু করে উপরের দিকে ওঠা। যে উপরের caller মডিউল এখনও আসল হিসেবে ইন্টিগ্রেট হয়নি, তার জায়গায় একটি ড্রাইভার (শুধু টেস্ট করার জন্য নিচের মডিউলকে কল করে এমন সাময়িক প্রোগ্রাম) বসানো হয়।
টপ-ডাউন ও বটম-আপ একসাথে — উপর ও নিচ থেকে একসাথে মাঝের দিকে এগিয়ে, মাঝখানে মিলিত হওয়া। বড় সিস্টেমে সময় বাঁচায়, কিন্তু স্টাব ও ড্রাইভার দুটোই লাগে।
মডিউলার আর্কিটেকচার ও কম্পোনেন্ট-ভিত্তিক ডিজাইনের ধারণা Software Engineering Principles & Git কোর্সে ধরে নেওয়া হয়েছে — এই পাঠ ধরে নেয় আপনি জানেন একটি সিস্টেম আলাদা আলাদা মডিউলে বিভক্ত থাকে।
২ · একটি ডিপেন্ডেন্সি গ্রাফ ও স্টাব/ড্রাইভার
নিচের ডায়াগ্রামে আমাদের ৩-মডিউল সিস্টেম দেখানো হয়েছে — UI নির্ভর করে Service-এর উপর, আর Service নির্ভর করে Database-এর উপর। টপ-ডাউনে UI দিয়ে শুরু হবে (Service-এর জায়গায় স্টাব লাগবে); বটম-আপে Database দিয়ে শুরু হবে (Service-কে ডাকার জন্য একটি ড্রাইভার লাগবে, কারণ আসল caller UI তখনও ইন্টিগ্রেট হয়নি)।
৩ · টপ-ডাউন বনাম বটম-আপ অর্ডার — সত্যিকারের গণনা
নিচের কোড সেলে উপরের ডিপেন্ডেন্সি গ্রাফটি একটি Python ডিকশনারি হিসেবে সংজ্ঞায়িত করা হয়েছে, তারপর প্রতিটি মডিউলের "লেভেল" (কতটা নিচু বা উঁচু স্তরে আছে) গণনা করে টপ-ডাউন ও বটম-আপ অর্ডার বের করা হয়েছে — এবং প্রতিটি ধাপে কোন মডিউলের জন্য স্টাব বা ড্রাইভার লাগবে তাও সত্যিকারভাবে গণনা করে দেখানো হয়েছে (হার্ডকোড করা কোনো বর্ণনা নয়)।
# মডিউল ডিপেন্ডেন্সি গ্রাফ: প্রতিটি মডিউল কোন কোন মডিউলকে কল করে (নির্ভর করে)
DEPENDS_ON = {
"UI": ["Service"],
"Service": ["Database"],
"Database": [],
}
# উল্টো দিক: প্রতিটি মডিউলকে কোন কোন মডিউল কল করে (কে caller)
CALLED_BY = {
module: [caller for caller, deps in DEPENDS_ON.items() if module in deps]
for module in DEPENDS_ON
}
def level_of(module):
# লেভেল 0 = সবচেয়ে নিচু স্তর (কোনো ডিপেন্ডেন্সি নেই), তার উপরে যত ডিপেন্ডেন্সি তত বেশি লেভেল
deps = DEPENDS_ON[module]
if not deps:
return 0
return 1 + max(level_of(d) for d in deps)
LEVELS = {module: level_of(module) for module in DEPENDS_ON}
print("প্রতিটি মডিউলের লেভেল:", LEVELS)
top_down_order = sorted(DEPENDS_ON, key=lambda m: -LEVELS[m])
bottom_up_order = sorted(DEPENDS_ON, key=lambda m: LEVELS[m])
print("\nটপ-ডাউন ইন্টিগ্রেশন অর্ডার:", top_down_order)
print("বটম-আপ ইন্টিগ্রেশন অর্ডার:", bottom_up_order)
def simulate(order, strategy):
print(f"\n--- {strategy} সিমুলেশন ---")
integrated = []
for module in order:
integrated.append(module)
if strategy == "টপ-ডাউন":
missing = [d for d in DEPENDS_ON[module] if d not in integrated]
if missing:
print(f"{module} ইন্টিগ্রেট করা হলো — এখনও আসল নয় এমন ডিপেন্ডেন্সি: {missing} → এদের জন্য STUB লাগবে")
else:
print(f"{module} ইন্টিগ্রেট করা হলো — সব ডিপেন্ডেন্সি ইতিমধ্যে আসল, কোনো স্টাব লাগবে না")
else: # বটম-আপ
missing = [c for c in CALLED_BY[module] if c not in integrated]
if missing:
print(f"{module} ইন্টিগ্রেট করা হলো — এখনও আসল নয় এমন caller: {missing} → এদের বদলে DRIVER লাগবে")
else:
print(f"{module} ইন্টিগ্রেট করা হলো — সব caller ইতিমধ্যে আসল, কোনো ড্রাইভার লাগবে না")
simulate(top_down_order, "টপ-ডাউন")
simulate(bottom_up_order, "বটম-আপ")
UI প্রথমে ইন্টিগ্রেট হয়, তখন Service এখনও আসল নয় বলে
তার জন্য স্টাব লাগে; পরে Service আসল হিসেবে যোগ হলে Database-এর জন্য স্টাব লাগে;
শেষে Database যোগ হলে আর কোনো স্টাব লাগে না। বটম-আপে ঠিক উল্টো ঘটনা ঘটে, কিন্তু স্টাবের বদলে
ড্রাইভার লাগে — কারণ এখানে সমস্যা হলো "কে এই নিচু-স্তরের মডিউলটাকে ডাকবে", ডিপেন্ডেন্সি নয়, caller অনুপস্থিত।
ইন্টিগ্রেশন স্ট্র্যাটেজি বেছে নেওয়া মানে "কোন ক্রমে মডিউল জোড়া লাগাবো, আর মাঝখানে ফাঁক পূরণ করার জন্য কী ব্যবহার করবো" তা সিদ্ধান্ত নেওয়া। টপ-ডাউন উঁচু থেকে নামে ও স্টাব ব্যবহার করে (early ব্যবহারকারী-মুখী ফিডব্যাক পাওয়া যায়), বটম-আপ নিচু থেকে ওঠে ও ড্রাইভার ব্যবহার করে (core লজিক আগে যাচাই হয়), স্যান্ডউইচ দুটোই একসাথে করে, আর বিগ ব্যাং কোনোটাই আলাদাভাবে করে না — যা ডিবাগিংকে কঠিন করে তোলে।
ভাবনার প্রশ্ন
প্রতিটি প্রশ্ন নিজে কিছুক্ষণ ভাবুন — তারপর "→ উত্তর" চাপুন।
প্র ০১ বিগ ব্যাং ইন্টিগ্রেশনে যদি টেস্ট ব্যর্থ হয়, কেন ঠিক কোন মডিউলের দোষ তা বের করা কঠিন হয়ে যায়?
কারণ সব মডিউল একসাথে জোড়া লাগানো হয়েছে, কোনো ইন্টারমিডিয়েট চেকপয়েন্ট নেই। একটি ব্যর্থতা যেকোনো মডিউলের ভিতরে, বা যেকোনো দুই মডিউলের মধ্যেকার ইন্টারফেসে হতে পারে — টপ-ডাউন বা বটম-আপে প্রতিটি ধাপে একটি মাত্র নতুন মডিউল যোগ হয় বলে সমস্যা হলে সেই নতুন মডিউলের দিকেই প্রথমে সন্দেহ যায়, যা ডিবাগিংকে অনেক সহজ করে দেয়।
প্র ০২ কোড সেলে টপ-ডাউন অর্ডারে "স্টাব" আর বটম-আপ অর্ডারে "ড্রাইভার" ব্যবহার হয় — এই দুটোর মধ্যে মূল পার্থক্য কী?
স্টাব হলো একটি নিচু-স্তরের মডিউলের জায়গায় বসানো placeholder, যা উপরের (এখন টেস্ট হওয়া) মডিউলকে একটি canned উত্তর দেয় যাতে সে তার কাজ চালিয়ে যেতে পারে — টপ-ডাউনে এটাই লাগে কারণ আপনি উপর থেকে নামছেন আর নিচেরটা এখনও তৈরি হয়নি। ড্রাইভার হলো উল্টো — একটি উঁচু-স্তরের caller-এর জায়গায় বসানো একটি সাময়িক প্রোগ্রাম যেটি শুধু টেস্ট করার জন্য নিচের (এখন টেস্ট হওয়া) মডিউলকে কল করে — বটম-আপে এটাই লাগে কারণ আসল caller তখনও ইন্টিগ্রেট হয়নি।
প্র ০৩ স্যান্ডউইচ/হাইব্রিড স্ট্র্যাটেজিতে কেন স্টাব ও ড্রাইভার — দুটোই লাগতে পারে?
কারণ স্যান্ডউইচ স্ট্র্যাটেজি একসাথে উপর থেকে নামে (টপ-ডাউন অংশ, যেখানে স্টাব লাগে) এবং নিচ থেকে ওঠে (বটম-আপ অংশ, যেখানে ড্রাইভার লাগে) — দুটো দিক মাঝখানে মিলিত না হওয়া পর্যন্ত প্রতিটি দিকেরই নিজস্ব প্লেসহোল্ডার দরকার হয়। এটি সময় বাঁচায় (দুই দিক থেকে সমান্তরালে কাজ করা যায়) কিন্তু বাস্তবায়ন জটিলতা বাড়ায়।
অনুশীলন
-
চিন্তা করুন: কোড সেলের ৩-মডিউল গ্রাফে যদি একটি চতুর্থ মডিউল
Cacheযোগ হয়, যেটি সরাসরিDatabase-এর উপর নির্ভর করে (Service-এর মতোই), তাহলেLEVELSডিকশনারিতেCache-এর লেভেল কত হবে বলে আপনার মনে হয়?Cache-এর একমাত্র ডিপেন্ডেন্সিDatabase, যার লেভেল0— তাইlevel_of("Cache")হবে1 + level_of("Database")=1, ঠিকService-এর মতোই একই লেভেলে। এর মানে টপ-ডাউন/বটম-আপ অর্ডারেCacheওServiceএকই ধাপে (যেকোনো ক্রমে) ইন্টিগ্রেট হতে পারে, দুজনেইDatabase-এর জন্য অপেক্ষা করবে। -
পরীক্ষা করুন: কোড সেলে
DEPENDS_ON-এ একটি নতুন এন্ট্রি"Cache": ["Database"]যোগ করুন এবংsimulateফাংশনটি আবার চালিয়ে দেখুন টপ-ডাউন ও বটম-আপ সিমুলেশনেCacheঠিক কোন ধাপে দেখা যায়।যেহেতু
Cache-এর লেভেলService-এর সমান,sorted()স্থিতিশীল (stable) sort হওয়ায় তারা মূল ডিকশনারিতে যে ক্রমে লেখা আছে সেই ক্রম বজায় রাখবে — যেমনServiceআগে থাকলে টপ-ডাউনেUI, Service, Cache, Databaseএমন একটি অর্ডার আসতে পারে। মূল কথা: একই লেভেলের একাধিক মডিউল থাকলে তাদের নিজেদের মধ্যে ক্রম নির্দিষ্ট নয় — শুধু এটুকু নিশ্চিত যেDatabaseসবার আগে (বটম-আপে) বা সবার পরে (টপ-ডাউনে) থাকবে।
আরও পড়ুন · ABCL TECH-এ আপনার পরবর্তী পদক্ষেপ
- কোর্সের সম্পূর্ণ সিলেবাস দেখুন ৫৭টি পাঠ টেস্ট ডিজাইন টেকনিক, ইউনিট টেস্টিং, টেস্ট ডাবলস, ইন্টিগ্রেশন টেস্টিং, কভারেজ, অটোমেশন, পারফরম্যান্স ও সিকিউরিটি টেস্টিং, অ্যাডভান্সড টেকনিক ও CI/CD ইন্টিগ্রেশন — বাকি পাঠগুলো শীঘ্রই যুক্ত হবে।
- Software Engineering Principles & Git কোর্স সহোদর কোর্স মডিউলার আর্কিটেকচার ও কম্পোনেন্ট-ভিত্তিক ডিজাইনের ভিত্তি সেই কোর্সেই তৈরি হয়েছে — এই কোর্স ইন্টিগ্রেশন টেস্টিং-এ গভীরে যায়।
- সব Courses দেখুন ABCL TECH C, C++, Python, Java, JavaScript, DSA, DBMS, Discrete Mathematics, System Design, Cybersecurity, Cloud Computing & DevOps, Computer Networks, Operating Systems, Computer Architecture, Programming Languages & Compiler Design, Software Engineering & Git, Theory of Computation, Engineering Economics, Full-Stack Web Frameworks, Mobile App Development, Ethics in Computing & AI Safety ও Software Testing & Quality Assurance — সব এক জায়গায়।