ভালো ইউনিট টেস্ট লেখা ও টেস্ট কভারেজ
এই পাঠে যা শিখবেন
- একটি ভালো ইউনিট টেস্টের FIRST নীতি এবং প্রতিটির ব্যবহারিক গুরুত্ব
- Arrange-Act-Assert কাঠামো এবং এটি কেন টেস্ট পঠনযোগ্যতা বাড়ায়
- টেস্ট কভারেজের সঠিক গণনা-সূত্র
- একটি বাস্তব উদাহরণে দেখা — কীভাবে একটি "ফাঁপা" টেস্ট আর একটি প্রকৃত assertion-সহ টেস্ট একই কভারেজ শতাংশ দেখাতে পারে
১ · FIRST — একটি ভালো ইউনিট টেস্টের পাঁচটি গুণ
একটি ইউনিট টেস্ট শুধু "কাজ করলেই" যথেষ্ট নয় — বাস্তবে দলগুলো একটি ভালো ইউনিট টেস্টের গুণাবলি সংক্ষেপে বলার জন্য FIRST মনেমনিক ব্যবহার করে:
মিলিসেকেন্ডে চলে, যাতে ডেভেলপমেন্টের সময় বারবার চালানো যায় কোনো ঘর্ষণ ছাড়াই — M10/L44-এর "উল্টানো পিরামিড" সমস্যার সরাসরি বিপরীত গুণ।
অন্য কোনো টেস্টের এক্সিকিউশন বা ক্রমের উপর নির্ভর করে না — M10/L44-এর বিচ্ছিন্নতার থিমের সরাসরি পুনরাবৃত্তি।
প্রতিবার, যেকোনো পরিবেশে একই ফলাফল দেয় — সরাসরি এই কোর্সের CLAUDE.md-এর "unseeded randomness নয়" নিয়মের সমান্তরাল: real random বা real current-time ব্যবহারকারী টেস্ট এই নীতি ভঙ্গ করে।
টেস্ট নিজেই পাস/ফেল স্পষ্টভাবে রিপোর্ট করে (assertion-এর মাধ্যমে) — আউটপুট ম্যানুয়ালি চোখে দেখে যাচাই করার প্রয়োজন নেই।
কোডের প্রায় একই সময়ে লেখা — L45-এর TDD দর্শনের সরাসরি পুনরাবৃত্তি, টেস্ট পরে যোগ করা "যদি সময় থাকে" এমন কিছু নয়।
২ · Arrange-Act-Assert (AAA)
একটি ভালো ইউনিট টেস্টের গঠনও গুরুত্বপূর্ণ। প্রমিত Arrange-Act-Assert (AAA) কনভেনশন প্রতিটি টেস্টকে তিনটি স্পষ্ট অংশে ভাগ করে:
৩ · টেস্ট কভারেজ — সূত্র ও একটি গুরুত্বপূর্ণ সতর্কতা
টেস্ট কভারেজTest Coverageটেস্ট চালানোর সময় প্রোগ্রামের কতটুকু কোড আসলে এক্সিকিউট হয়েছে, তার একটি শতাংশ পরিমাপ। একটি প্রমিত মেট্রিক যা পরিমাপ করে কোডের কতটুকু অংশ টেস্ট চালানোর সময় আসলে এক্সিকিউট হয়েছে:
$$Coverage\% = \frac{\text{এক্সিকিউট হওয়া লাইন}}{\text{মোট এক্সিকিউটযোগ্য লাইন}} \times 100$$
উচ্চ কভারেজ শতাংশ ভালো টেস্টের নিশ্চয়তা দেয় না — একটি টেস্ট একটি লাইন এক্সিকিউট করতে পারে কোনো অর্থবহ কিছু যাচাই না করেই। কভারেজ টুল শুধু "কী চালানো হয়েছিল" পরিমাপ করে, "কী যাচাই করা হয়েছিল" নয় — এটি একটি বাস্তব, সাধারণ মিথ্যা-নিরাপত্তাবোধের উৎস।
নিচের কোড সেলে এই সতর্কতাটি বাস্তবে দেখানো হবে — একটি ছোট্ট ডিসকাউন্ট-গণনা ফাংশনের জন্য দুটো ভিন্ন টেস্ট লেখা হবে: একটি প্রকৃতপক্ষে সঠিক ফলাফল যাচাই করে (AAA কাঠামো মেনে), আরেকটি একই লাইন এক্সিকিউট করে কিন্তু অর্থবহ কোনো assertion ছাড়াই — এবং দেখানো হবে দুটোরই গণনাকৃত কভারেজ ঠিক একই।
# টেস্ট কভারেজ গণনা, এবং কেন উচ্চ কভারেজ "ভালো" টেস্টের নিশ্চয়তা দেয় না -- একটি বাস্তব উদাহরণ।
def calculate_coverage(total_lines, executed_lines_set):
return (len(executed_lines_set) / total_lines) * 100
def apply_discount(price, is_member):
# লাইন ১
if is_member:
# লাইন ২
discount = price * 0.10
else:
# লাইন ৩
discount = 0
# লাইন ৪
return price - discount
TOTAL_LINES = 4
# AAA-স্টাইল ভালো টেস্ট -- অর্থবহ assertion সহ
def test_apply_discount_for_member():
# Arrange
price = 200
is_member = True
# Act
result = apply_discount(price, is_member)
# Assert
assert result == 180
test_apply_discount_for_member()
executed_by_good_test = {1, 2, 4}
good_coverage = calculate_coverage(TOTAL_LINES, executed_by_good_test)
print(f"ভালো টেস্ট -- coverage: {good_coverage:.1f}% (assertion সহ, প্রকৃত আচরণ যাচাই করে)")
# ফাঁপা টেস্ট -- লাইন এক্সিকিউট করে কিন্তু কিছুই যাচাই করে না
def test_apply_discount_no_real_assertion():
result = apply_discount(200, True)
assert True # ফাঁপা assertion -- ফলাফলের সাথে কোনো সম্পর্ক নেই
test_apply_discount_no_real_assertion()
executed_by_hollow_test = {1, 2, 4}
hollow_coverage = calculate_coverage(TOTAL_LINES, executed_by_hollow_test)
print(f"ফাঁপা টেস্ট -- coverage: {hollow_coverage:.1f}% (একই লাইন এক্সিকিউট, কিন্তু কিছুই যাচাই হয়নি)")
print("\nদুটো টেস্টেরই coverage টুল অনুযায়ী রিপোর্ট একই -- কিন্তু শুধু প্রথমটি প্রকৃতপক্ষে সঠিকতা যাচাই করে।")
apply_discount(200, True) কল করে একই চারটি লাইন এক্সিকিউট করেছে, তাই
calculate_coverage উভয়ের জন্যই ৭৫.০% রিপোর্ট করে (৪ লাইনের মধ্যে ৩টি — শুধু else
শাখাটি অচেষ্টিত রয়ে গেছে)। কিন্তু test_apply_discount_no_real_assertion-এর
assert True কখনোই ব্যর্থ হতে পারে না, ফলাফল যাই হোক না কেন — এটি ইমপ্লিমেন্টেশনে যেকোনো বাগ
থাকলেও ধরবে না, অথচ কভারেজ রিপোর্টে এটি সম্পূর্ণ "স্বাস্থ্যকর" দেখাবে।
FIRST নীতি ও AAA কাঠামো একটি ইউনিট টেস্টকে দ্রুত, নির্ভরযোগ্য ও পঠনযোগ্য রাখে; টেস্ট কভারেজ একটি উপযোগী কিন্তু সীমাবদ্ধ মেট্রিক — এটি বলে দেয় কী চালানো হয়েছিল, কিন্তু কী প্রকৃতপক্ষে যাচাই করা হয়েছিল তা নয়। উচ্চ কভারেজ শতাংশকে কখনোই "ভালো টেস্টিং"-এর একমাত্র প্রমাণ হিসেবে ধরা উচিত নয় — প্রতিটি assertion আসলেই অর্থবহ কিনা, তা মানুষকেই যাচাই করতে হয়।
ভাবনার প্রশ্ন
প্রতিটি প্রশ্ন নিজে কিছুক্ষণ ভাবুন — তারপর "→ উত্তর" চাপুন।
প্র ০১
একটি টেস্ট যদি বর্তমান তারিখ/সময় (datetime.now()) ব্যবহার করে ফলাফল যাচাই করে, তাহলে FIRST-এর কোন নীতিটি ভাঙে, এবং কেন?
এটি Repeatable নীতি ভাঙে — টেস্টটি চালানোর সময়ের উপর নির্ভরশীল হয়ে যায়, ফলে আজ পাস করলেও আগামীকাল, বা এমনকি কয়েক মিলিসেকেন্ড পরে, ভিন্ন ফলাফল দিতে পারে (বিশেষ করে সময়-সংবেদনশীল লজিকে, যেমন "যদি আজ সোমবার হয়")। এটিই ঠিক এই কোর্সের CLAUDE.md-এর "no unseeded randomness" নিয়মের পেছনের যুক্তি — একটি টেস্ট যেকোনো পরিবেশে, যেকোনো সময়ে একই ফলাফল দেওয়া উচিত, নাহলে এটি নির্ভরযোগ্য নয়।
প্র ০২
উপরের কোড সেলে apply_discount-এর else শাখা (লাইন ৩) কোনো টেস্টেই এক্সিকিউট হয়নি। এটি বাস্তবে কী ঝুঁকি তৈরি করে?
এর মানে is_member=False কেসের জন্য কোনো টেস্টই কখনো যাচাই করেনি ফাংশনটি সঠিকভাবে কাজ করে
কিনা — যদি সেই শাখায় একটি বাগ থাকে (যেমন ভুলবশত discount = price * 0.10 লেখা হয়ে যায়
else-এও), তা কখনো ধরা পড়বে না, ৭৫% কভারেজ থাকা সত্ত্বেও। এটি ঠিক দেখায় কেন ৭৫% কভারেজ
মানে "৭৫% নিশ্চিত সঠিক" নয় — বাকি ২৫% (এখানে else শাখা) সম্পূর্ণ অপরীক্ষিত থেকে যায়।
প্র ০৩ Arrange-Act-Assert কাঠামোয় "Act" ধাপে সাধারণত ঠিক কতগুলো ফাংশন/মেথড কল করা উচিত, এবং কেন সেটি একটির বেশি না হওয়াই ভালো?
সাধারণত একটি মাত্র — যে জিনিসটি আসলে টেস্ট করা হচ্ছে ঠিক সেই একটি কল। যদি "Act" ধাপে একাধিক ফাংশন কল করা হয়, তাহলে টেস্টটি ব্যর্থ হলে বোঝা কঠিন হয়ে যায় ঠিক কোন কলটি সমস্যার উৎস — এটি Fast ও Self-validating নীতির সাথেও যুক্ত: একটি স্পষ্ট, একক-উদ্দেশ্যের টেস্ট ব্যর্থতার কারণ তাৎক্ষণিকভাবে স্পষ্ট করে, একাধিক কলের একটি জট নয়।
অনুশীলন
-
চিন্তা করুন: একটি টিম গর্বের সাথে ঘোষণা করে তাদের কোডবেসের টেস্ট কভারেজ ৯৮%। এই একটি সংখ্যা থেকে আপনি তাদের টেস্ট স্যুটের মান সম্পর্কে ঠিক কী জানতে পারেন, আর কী জানতে পারেন না?
আপনি জানতে পারেন কোডবেসের প্রায় সব লাইনই অন্তত একবার কোনো টেস্ট চালানোর সময় এক্সিকিউট হয়েছে। কিন্তু আপনি জানতে পারেন না সেই টেস্টগুলো প্রকৃতপক্ষে অর্থবহ assertion করে কিনা, এজ কেস (যেমন খালি ইনপুট, নেগেটিভ সংখ্যা) কভার করে কিনা, বা টেস্টগুলো FIRST নীতি মেনে চলে কিনা। ৯৮% কভারেজ একটি ভালো সংকেত হতে পারে, কিন্তু এটি একা কখনোই টেস্ট স্যুটের প্রকৃত মানের প্রমাণ নয় — এই পাঠের কোড সেলের ফাঁপা-টেস্ট উদাহরণটি ঠিক এই সীমাবদ্ধতাই দেখায়।
-
পরীক্ষা করুন: উপরের কোড সেলে
apply_discount-এরelseশাখা কভার করার জন্য একটি তৃতীয় AAA-স্টাইল টেস্ট যোগ করুন (is_member=Falseদিয়ে), এবং নতুনexecuted_linesসেট গণনা করে দেখুন কভারেজ কত বাড়ে।apply_discount(200, False)কল করলে লাইন ১, ৩ ও ৪ এক্সিকিউট হবে (লাইন ২ নয়, কারণif is_memberমিথ্যা)। আগের ভালো টেস্টের{1, 2, 4}-এর সাথে এটি একত্রিত করলে (সেট ইউনিয়ন) পুরো সেটটি হয়{1, 2, 3, 4}— অর্থাৎcalculate_coverage(4, {1, 2, 3, 4})এখন ১০০.০% দেবে, এবং এবার প্রতিটি শাখাই অন্তত একটি অর্থবহ assertion-সহ টেস্ট দিয়ে সত্যিই যাচাই করা হয়েছে।
আরও পড়ুন · ABCL TECH-এ আপনার পরবর্তী পদক্ষেপ
- কোর্সের সম্পূর্ণ সিলেবাস দেখুন ৫৮টি পাঠ পরের পাঠ — মকিং ও টেস্ট ডাবল — দেখাবে কীভাবে ধীর/অনির্ভরযোগ্য ডিপেন্ডেন্সি ছাড়াই একটি ইউনিটকে সম্পূর্ণ বিচ্ছিন্নভাবে টেস্ট করা যায়।
- টেস্ট-ড্রিভেন ডেভেলপমেন্ট (TDD) (L45) M10 · আগের পাঠ L45-এর Timely নীতি (টেস্ট কোডের সাথে সাথেই লেখা) এই পাঠের FIRST নীতির পঞ্চম, শেষ অংশ হিসেবেই এসেছে।
- কাপলিং ও কোহেশন (L14) M4 · সম্পর্কিত Independent/Isolated নীতি মেনে চলা টেস্ট লেখা প্রায়ই স্বাভাবিকভাবেই কম-কাপল্ড, বেশি টেস্টযোগ্য কোড ডিজাইনের দিকে ঠেলে দেয়।