এই পর্বে যা শিখবেন
- একটি ইনপুট-ঘর থেকে valid ও invalid partition, প্রতিনিধি মান আর সীমার মান বের করা — হাতে আর কোডে
- তিন শর্তের একটি নিয়মের পূর্ণ decision table বানিয়ে don't-care দিয়ে ছোট করা
- State table থেকে বৈধ ও অবৈধ transition-এর test, আর 0-switch বনাম 1-switch coverage গোনা
- Pairwise suite কীভাবে ৮১টি মিলনকে ৯-এ নামায়, আর কোথায় এর সীমা
- Error guessing, exploratory আর property-based testing-এর জায়গা, আর ঝুঁকি ধরে কৌশল বেছে নেওয়া
১ · কেন test case নকশা করতে হয় — অসীম ইনপুট, সীমিত সময়
একটি কাল্পনিক বিশ্ববিদ্যালয়ের ফল-পোর্টালের কথা ভাবুন। একটি ঘরে শিক্ষক নম্বর লেখেন, আর system grade বসায়। নিয়মটিও কাল্পনিক: ০–৩৯ F, ৪০–৫৯ C, ৬০–৭৯ B, ৮০–১০০ A; এর বাইরে কিছু লিখলে system সেটি গ্রহণ করবে না। এমন একটি ঘর ঠিক আছে কি না যাচাই করতে কয়টি test লাগবে?
Integer ধরলেও বৈধ মানই ১০১টি। এর সাথে আছে ঋণাত্মক সংখ্যা, ১০০-র বেশি সংখ্যা, দশমিক, খালি ঘর, অক্ষর, বাংলা অঙ্ক “৪৫”। আর একটি পাতায় এমন ঘর থাকে দশটি — সব মিলনের সংখ্যা তখন কার্যত অসীম। সব ইনপুট চালিয়ে দেখাকে বলে exhaustive testing, আর বাস্তব software-এ এটি অসম্ভব। তাই প্রশ্ন বদলে যায়: সবচেয়ে কম test দিয়ে সবচেয়ে বেশি ভুল ধরার সম্ভাবনা কীভাবে? এর উত্তরই test design technique.
চালের বস্তা কেনার সময় দোকানি পুরো বস্তা ঢেলে দেখান না। অভিজ্ঞ ক্রেতা বস্তার ওপর, মাঝখান আর তলা থেকে এক মুঠো করে তোলেন — কারণ জানেন, ভেজাল বা ভাঙা চাল থাকলে সাধারণত কোথায় থাকে। Test নকশাও এমন বুদ্ধিমান নমুনা নেওয়া: কোন মানগুলো “একই রকম”, আর কোথায় ভুল লুকানোর সম্ভাবনা বেশি, তা আগে ভেবে নেওয়া।
একটি test case-এর অন্তত চারটি অংশ থাকে: পূর্বশর্ত (যেমন শিক্ষক লগইন করেছেন), ইনপুট (নম্বর 40), প্রত্যাশিত ফল (grade C), আর চালানোর পরের অবস্থা। প্রত্যাশিত ফল কোথা থেকে আসবে, সেটি ঠিক করার উৎসকে বলে test oracle — এখানে requirement-এর নিয়মটি। প্রত্যাশিত ফল আগে লিখতে হয়; চালানোর পরে যা এল তা দেখে “এটাই তো ঠিক” বলা আসলে যাচাই নয়।
এই পর্বের কৌশলগুলো প্রায় সবই black-box: কোডের ভেতর না দেখে, শুধু requirement আর আচরণ দেখে test বানানো। পর্ব ৪৫ Testing-এর মৌলিক-এ black-box আর white-box-এর পার্থক্য আর test-এর স্তর আছে। কোডের শাখা আর পথ ধরে test (statement, branch coverage, path testing) আসবে পর্ব ৫০ Coverage আর complexity-তে। আর এখানে বানানো test-গুলো কোডে কীভাবে লিখবেন — Arrange-Act-Assert, assertion, fixture — তা পর্ব ৪৬ Unit test-এ।
Test নকশার প্রতিটি কৌশল আসলে একটি মডেল: ইনপুটের দল (partition), সীমা (boundary), শর্তের মিলন (decision table), অবস্থার যন্ত্র (state machine), ব্যবহারের গল্প (use case), parameter-এর জোড়া (pairwise). মডেল থেকে test বের হয়, আর মডেলের সাপেক্ষেই coverage মাপা যায় — “সব partition ঢাকা হয়েছে”, “সব transition একবার চলেছে”।
এই পর্বের অনেক কৌশল প্রথম গুছিয়ে বই আকারে আসে Glenford Myers-এর The Art of Software Testing-এ (১৯৭৯) — equivalence partitioning, boundary value analysis, cause-effect graphing আর error guessing সেখানে আলোচিত। পরে Boris Beizer-এর Software Testing Techniques (২য় সংস্করণ ১৯৯০) domain testing আর state-ভিত্তিক test গভীরভাবে ব্যাখ্যা করে। আজ এগুলোর প্রচলিত সংজ্ঞা পাওয়া যায় ISTQB Foundation syllabus-এ (লেখার সময়ে সংস্করণ ৪.০, ২০২৩) আর ISO/IEC/IEEE 29119-4 মানে (প্রথম প্রকাশ ২০১৫, পরে হালনাগাদ)।
২ · Equivalence partitioning — একই আচরণের ইনপুট এক দলে
Equivalence partitioning-এর (EP) মূল অনুমান সহজ: যে ইনপুটগুলোর সাথে system একইভাবে আচরণ করার কথা, তাদের যেকোনো একটি দিয়ে test করলেই বাকিগুলোর প্রতিনিধিত্ব হয়। নম্বর 45 যদি C দেয়, 52-ও C দেবে বলে আশা করা যায় — দুটিই একই নিয়মের শাখায় পড়ে। তাই 40–59 একটি partition (equivalence class), আর এ থেকে একটি মান বেছে নিলেই চলে।
ধাপে ধাপে: grade-এর ঘর
- Valid partition: requirement যেখানে আলাদা আচরণ বলে, সেখানেই আলাদা দল। এখানে চারটি: F (0–39), C (40–59), B (60–79), A (80–100).
- Invalid partition: যা গ্রহণ করার কথা নয়। সংখ্যার দিকে দুটি: 0-র নিচে (−1, −50…) আর 100-র ওপরে (101, 500…)। ধরনের দিক থেকে আরও: খালি ঘর, অক্ষর (“abc”), দশমিক (45.5) — যদি requirement integer চায়।
- প্রতিনিধি: প্রতিটি partition থেকে একটি মান, সাধারণত মাঝামাঝি: −5, 19, 49, 69, 90, 105. সংখ্যার দিকের ৬টি partition-এ তাই ৬টি test.
EP-র coverage মাপা হয় এভাবে: যতগুলো partition চিহ্নিত, তার কয়টিতে অন্তত একটি test আছে। ৬টির মধ্যে ৬টি হলে ১০০% equivalence partition coverage. খেয়াল করুন, এটি partition চেনার মানের ওপর নির্ভর করে — একটি partition চোখ এড়ালে “১০০%” মিথ্যা আশ্বাস দেয়।
একাধিক ইনপুট — partition কীভাবে মেলাবেন
বাস্তব ফর্মে ঘর অনেক। ধরুন ফল-পোর্টালে দুটি ঘর: নম্বর (৬টি partition) আর ছাত্রের roll (২টি: বৈধ ৭ অঙ্ক, অবৈধ)। তিনটি পথ আছে:
- প্রতিটি partition অন্তত একবার (each-choice): দুই ঘরের partition একসাথে মিলিয়ে test গোনা হয় বড়টির সমান — এখানে ৬টি test-এই দুই ঘরের ৮টি partition ঢাকা যায়।
- সব মিলন: ৬ × ২ = ১২টি test. ঘর বাড়লে গুণফল দ্রুত ফুলে ওঠে — এই সমস্যার মাঝামাঝি সমাধান অংশ ৭-এর pairwise.
- অবৈধ মান একবারে একটি: এটি নিয়ম, পছন্দ নয়। একটি test-এ নম্বর 105 আর roll “abc” দুটোই দিলে system প্রথমটিতেই থেমে error দিতে পারে; দ্বিতীয় ঘরের যাচাই আদৌ আছে কি না জানা যায় না। একে বলে error masking. তাই বৈধ মানগুলো একসাথে মেলানো চলে, কিন্তু প্রতিটি invalid partition-এর আলাদা test, বাকি সব ঘর বৈধ রেখে।
Partition বানান requirement থেকে, কোড থেকে নয়। কোড দেখে partition বানালে কোডের ভুলও partition-এ ঢুকে যায় — কোডে যদি 40-এর বদলে 41 থেকে C শুরু হয়, কোড-দেখা partition-ও তাই বলবে, আর test পাস করবে। Requirement অস্পষ্ট হলে (40 কি C, নাকি F?) সেটিই প্রথম “bug” — প্রশ্ন তুলে স্পষ্ট করুন (পর্ব ২১ মাপা যায় এমন requirement).
৩ · Boundary value analysis — ভুল লুকায় কিনারায়
প্রোগ্রামাররা সবচেয়ে বেশি ভুল করেন সীমায়: < না <=, লুপ ০ থেকে না ১ থেকে, শেষ উপাদান বাদ পড়ল কি না। এই ধরনের ভুলের নাম off-by-one error. ধরুন নিয়ম “৪০ বা বেশি হলে C”, কিন্তু কোডে লেখা if marks > 40. EP-র প্রতিনিধি 49 দিয়ে test করলে C আসবে, test পাস — অথচ ঠিক 40 পাওয়া ছাত্রটি F পাবেন। Boundary value analysis (BVA) এই কারণেই আছে: partition-এর কিনারার মান আলাদা করে test করা।
On point আর off point
প্রতিটি সীমায় দুটি বিশেষ বিন্দু। On point হলো সীমার মানটি নিজেই, অর্থাৎ ঠিক সীমার ওপর পড়া বিন্দু — যেমন C-র নিচের সীমায় 40. Off point হলো সীমার অন্য পাশে সবচেয়ে কাছের মান — এখানে 39, যা F-এ পড়ে। Integer-এ “সবচেয়ে কাছে” মানে ১ কম বা বেশি; টাকার মতো দুই দশমিকের সংখ্যায় ০.০১; দৈর্ঘ্যের সীমায় একটি অক্ষর। এই ক্ষুদ্রতম ধাপটি requirement-এর নির্ভুলতা (precision) থেকে আসে, তাই আগে জেনে নিন ঘরটি integer, না দশমিক।
নম্বরের সংখ্যারেখা — partition, সীমা আর সীমার পাশের মান
Number line for marks: invalid and valid partitions, 2-value boundary pairs, and a zoom on the F/C boundary showing 3-value points
২-মান বনাম ৩-মান BVA (ISTQB)
ISTQB-এর Foundation syllabus (সংস্করণ ৪.০) দুটি রূপ আলাদা করে বলে। ২-মান BVA-তে প্রতিটি সীমার মান আর পাশের partition-এর সবচেয়ে কাছের মান — অর্থাৎ on point আর off point. ৩-মান BVA-তে প্রতিটি সীমার মান আর তার দুই পাশের প্রতিবেশী। এক রেঞ্জের ঘর, বয়স 18–60:
- ২-মান: 17, 18, 60, 61 — ৪টি test.
- ৩-মান: 17, 18, 19, 59, 60, 61 — ৬টি test.
এখানে (আর এই পর্বের সব হিসাবে) ৩-মানের প্রতিবেশী নেওয়া হয়েছে valid partition-এর কিনারা ধরে। কেউ invalid partition-এর কিনারাকেও (17, 61) আলাদা সীমার মান ধরলে 16 আর 62 যোগ হয়ে ৮টি হয় — উৎসভেদে দুই রকম হিসাবই দেখা যায়।
৩-মান কেন? ধরুন একটি ঘরে মাত্র একটি সীমা: “বয়স ৬০ বা কম হলে ছাড়” (age <= 60), কিন্তু কোডে ভুল করে age == 60. ২-মানের 60 আর 61 দুটিতেই ফল ঠিক আসে (60-এ ছাড়, 61-এ নয়) — ভুলটি ধরা পড়ে না। ৩-মানের 59 ছাড় পায় না, ভুল ধরা পড়ে। অর্থাৎ ৩-মান বাড়তি খরচে কিছু বাড়তি ধরনের ভুল ধরে; কোনটি নেবেন তা ঝুঁকির ওপর নির্ভর করে (অংশ ৯)। পুরোনো বইয়ে “min, min+1, nominal, max−1, max” ধাঁচের আরেকটি রূপও পাওয়া যায় (যেমন robustness testing-এ min−1 আর max+1 যোগ) — স্কুলভেদে নাম আর মানের তালিকা আলাদা, তাই পরীক্ষায় প্রশ্ন কোন রূপ চাইছে খেয়াল করুন।
এবার পুরো grade-এর ঘরটি ধাপে ধাপে ভাঙি। “পরে” চাপুন:
| ধাপ | |
| পাওয়া গেল | |
| test-এর সংখ্যা |
- ইনপুটের জগৎ। Requirement পড়ে লিখুন: ঘরটি integer, বৈধ 0–100, আর চারটি grade-এর রেঞ্জ। এখনো কোনো test নয় — আগে জানা দরকার কোন মানগুলো “একই রকম”।
- Partition. চারটি valid আর দুই পাশে দুটি invalid — সংখ্যার দিকে মোট ৬টি। এর বাইরে ধরনের দিক থেকে invalid: খালি ঘর, অক্ষর, দশমিক। এগুলোর প্রতিটির আলাদা test, একবারে একটি অবৈধ মান।
- প্রতিনিধি। প্রতিটি partition-এর মাঝামাঝি একটি মান: −5, 19, 49, 69, 90, 105 — ৬টি test-এ ১০০% partition coverage. কিন্তু “> 40” ধাঁচের ভুল এরা ধরবে না।
- সীমার মান। প্রতিটি valid partition-এর দুই কিনারা: 0, 39, 40, 59, 60, 79, 80, 100 — ৮টি। পাশাপাশি দুটি partition-এর সীমা (39 আর 40) একে অপরের off point.
- ২-মান BVA. প্রতিটি কিনারা আর অন্য পাশের সবচেয়ে কাছের মান। ভেতরের সীমায় দুটি মান আগে থেকেই আছে; শুধু দুই প্রান্তে −1 আর 101 যোগ হয়। মোট ১০টি।
- ৩-মান BVA. প্রতিটি কিনারার মানের দুই পাশের প্রতিবেশীও: যেমন 39-এর জন্য 38, 39, 40 আর 40-এর জন্য 39, 40, 41. পুনরাবৃত্তি বাদে মোট ১৮টি।
- চূড়ান্ত suite. এখানে বেছে নেওয়া হলো EP-র ৬ + ২-মানের ১০ = ১৬টি সংখ্যার test, সাথে খালি, “abc” আর 45.5 — মোট ১৯টি। ১০১টি বৈধ মানের তুলনায় কম, কিন্তু প্রতিটি সীমা আর প্রতিটি দল ঢাকা। ঝুঁকি বেশি হলে ২-মানের বদলে ৩-মান নিন: ৬ + ১৮ + ৩ = ২৭টি।
এই হিসাবটি কোডে করলে ভুল কম হয়, আর নতুন ঘরের জন্য আবার ব্যবহার করা যায়। নিচের প্রোগ্রামটি প্রতিটি ঘরের valid partition-এর তালিকা নেয়, invalid দুই পাশ নিজেই যোগ করে, তারপর প্রতিনিধি, ২-মান আর ৩-মান BVA ছাপে:
// Equivalence classes + 2-value / 3-value boundary values (integer inputs).
// Each field: a list of VALID partitions [lo, hi, label]; outside = invalid.
function design(name, parts) {
const lo = parts[0][0], hi = parts[parts.length - 1][1];
const classes = [["INVALID low", -Infinity, lo - 1], ...parts.map(p => [p[2], p[0], p[1]]),
["INVALID high", hi + 1, Infinity]];
console.log(`== ${name}`);
for (const [label, a, b] of classes) {
// one representative per class: middle of the class (or a value near the edge)
const rep = a === -Infinity ? b - 4 : b === Infinity ? a + 4 : Math.floor((a + b) / 2);
console.log(` ${label.padEnd(12)} ${String(a).padStart(9)} .. ${String(b).padEnd(9)} rep=${rep}`);
}
const bounds = []; // every edge value of every valid partition
for (const [a, b] of parts) bounds.push(a, b);
const two = new Set(), three = new Set();
for (const v of bounds) {
const isLow = parts.some(p => p[0] === v);
two.add(v); two.add(isLow ? v - 1 : v + 1); // the value + its nearest neighbour in the next class
three.add(v - 1); three.add(v); three.add(v + 1); // the value + both neighbours
}
const sort = s => [...s].sort((x, y) => x - y);
console.log(` EP tests : ${classes.length}`);
console.log(` 2-value : ${sort(two).join(", ")} (${two.size})`);
console.log(` 3-value : ${sort(three).join(", ")} (${three.size})`);
}
design("age (single range 18-60)", [[18, 60, "valid"]]);
design("marks (grades)", [[0, 39, "F"], [40, 59, "C"], [60, 79, "B"], [80, 100, "A"]]);আউটপুট:
== age (single range 18-60)
INVALID low -Infinity .. 17 rep=13
valid 18 .. 60 rep=39
INVALID high 61 .. Infinity rep=65
EP tests : 3
2-value : 17, 18, 60, 61 (4)
3-value : 17, 18, 19, 59, 60, 61 (6)
== marks (grades)
INVALID low -Infinity .. -1 rep=-5
F 0 .. 39 rep=19
C 40 .. 59 rep=49
B 60 .. 79 rep=69
A 80 .. 100 rep=90
INVALID high 101 .. Infinity rep=105
EP tests : 6
2-value : -1, 0, 39, 40, 59, 60, 79, 80, 100, 101 (10)
3-value : -1, 0, 1, 38, 39, 40, 41, 58, 59, 60, 61, 78, 79, 80, 81, 99, 100, 101 (18)
বয়সের এক রেঞ্জের ঘরে EP ৩টি, ২-মান ৪টি, ৩-মান ৬টি। Grade-এর ঘরে ৬, ১০ আর ১৮ — stepper-এর সাথে হুবহু মিল। two set-এ প্রতিটি নিচের কিনারার জন্য v - 1, ওপরের কিনারার জন্য v + 1 যোগ হয়; পাশাপাশি partition-এর কিনারায় সেই মান আগেই set-এ আছে বলে দ্বিগুণ হয় না। প্রতিনিধি হিসেবে অসীম দিকের class-এ সীমা থেকে ৪ দূরের মান নেওয়া হয়েছে (−5, 105) — এটি নিছক পছন্দ, যেকোনো ভেতরের মান চলে। প্রোগ্রামটি শুধু integer ধরে; দশমিক ঘরে ১-এর বদলে ক্ষুদ্রতম ধাপ (যেমন 0.01) লাগবে।
সীমা সবসময় requirement-এ লেখা থাকে না। Integer-এর সর্বোচ্চ মান (32-bit-এ 2,147,483,647), string-এর সর্বোচ্চ দৈর্ঘ্য, তালিকা খালি কি না, ফেব্রুয়ারির ২৯ তারিখ, রাত ১১:৫৯ থেকে ১২:০০ — এগুলো লুকানো সীমা। Requirement-এর সীমার পাশাপাশি data-র ধরন আর প্রযুক্তির সীমাও তালিকায় রাখুন।
৪ · Decision table — শর্তের সব মিলন
EP আর BVA একটি একটি ঘর দেখে। কিন্তু অনেক নিয়মে ফল নির্ভর করে কয়েকটি শর্ত একসাথে কেমন, তার ওপর। এমন জায়গায় decision table সবচেয়ে কাজের। একটি কাল্পনিক শহরের বাস-ভাড়ার ছাড় নিয়ম:
অন্যথায় বৈধ ছাত্র-কার্ড থাকলে ৩০%, আর ভিড় কম সময়ে (off-peak) যাত্রা হলে আরও ১০%.
টেবিলের চারটি অংশ। শর্ত (condition): C1 যাত্রী ৬০+, C2 ছাত্র-কার্ড, C3 off-peak. কাজ (action): A1 ছাড়ের হার। প্রতিটি rule একটি কলাম: শর্তগুলোর একটি মিলন আর তার কাজ। n-টি Y/N শর্তে সব মিলন 2nটি — এখানে 23 = ৮টি। Rule বেশি হলে কিছু শর্ত ফলে কোনো প্রভাব ফেলে না, আর সেখানে বসে don't-care চিহ্ন “−”।
| ধাপ | |
| টেবিল | |
| rule-এর সংখ্যা |
- শর্ত আর কাজ। Requirement-এর প্রতিটি “যদি” একটি শর্ত, প্রতিটি “তাহলে” একটি কাজ। তিনটি Y/N শর্ত আর একটি কাজ (ছাড়ের হার) পাওয়া গেল। শর্ত যত স্বাধীন আর স্পষ্ট, টেবিল তত পরিষ্কার।
- Rule গোনা। প্রতিটি শর্তের দুটি মান, তাই 23 = ৮টি rule. চারটি শর্ত হলে ১৬, দশটি হলে ১০২৪ — এজন্য বড় নিয়মকে ছোট ছোট টেবিলে ভাঙা হয়।
- Y/N বসানো। একটি সহজ কৌশল: প্রথম সারিতে অর্ধেক Y অর্ধেক N (৪+৪), দ্বিতীয় সারিতে ২+২ করে, তৃতীয়তে ১+১ করে। এতে কোনো মিলন বাদ বা দ্বিগুণ হয় না।
- কাজ বসানো। প্রতিটি কলামে requirement থেকে প্রত্যাশিত ফল: ৬০+ হলে সব কলামে 50; অন্যথায় 30 + 10 = 40, 30, 10, 0. এই সারিটিই test oracle.
- ছোট করা। R1–R4-এ C1 = Y আর ফল একই (50), C2 আর C3 যা-ই হোক। তাই চারটি কলাম মিশে একটি: “Y − −”। বাকি চারটির ফল আলাদা, সেগুলো থাকে। ৮টি থেকে ৫টি rule.
- Test case. প্রতিটি rule থেকে একটি test; “−”-এর জায়গায় যেকোনো মান বসানো চলে। কিন্তু খেয়াল করুন: “−” একটি দাবি যে ওই শর্তের প্রভাব নেই। কোডে সত্যিই নেই কি না, ঝুঁকি থাকলে একটি বাড়তি test-এ দেখুন — যেমন ৬০+ যাত্রী যাঁর ছাত্র-কার্ডও আছে, off-peak-এ।
একই কাজ কোডে। প্রোগ্রামটি নিয়ম থেকে ৮টি rule বানায়, তারপর বারবার এমন দুটি rule মেশায় যাদের কাজ একই আর ঠিক একটি শর্তে পার্থক্য — সেই শর্তে বসে “−”:
from itertools import product
CONDS = ["C1 senior (60+)", "C2 student card", "C3 off-peak"]
def discount(senior, student, offpeak): # the fictional rule (spec)
if senior:
return 50
return (30 if student else 0) + (10 if offpeak else 0)
def show(title, rules):
print(title)
for i, c in enumerate(CONDS):
print(f" {c:16}" + "".join(f"{r[0][i]:>4}" for r in rules))
print(f" {'A1 discount %':16}" + "".join(f"{r[1]:>4}" for r in rules))
def merge_once(rules): # join 2 rules: same action, differ in exactly 1 Y/N
for a, b in product(rules, rules):
d = [i for i in range(3) if a[0][i] != b[0][i]]
if a[1] == b[1] and len(d) == 1 and "-" not in (a[0][d[0]], b[0][d[0]]):
new = (a[0][:d[0]] + "-" + a[0][d[0] + 1:], a[1])
return [r for r in rules if r not in (a, b)] + [new]
return None
full = [("".join("Y" if v else "N" for v in vals), discount(*vals))
for vals in product([True, False], repeat=3)]
show(f"Full table: 2^3 = {len(full)} rules", full)
rules = full
while (nxt := merge_once(rules)) is not None:
rules = nxt
rules.sort(key=lambda r: [{"Y": 0, "-": 0, "N": 1}[c] for c in r[0]])
show(f"Collapsed: {len(rules)} rules", rules)
print("Test cases (one per collapsed rule; '-' filled with N):")
for k, (cond, act) in enumerate(rules, 1):
v = [c == "Y" for c in cond]
print(f" TC{k}: senior={v[0]!s:5} student={v[1]!s:5} offpeak={v[2]!s:5} -> {act}%")আউটপুট:
Full table: 2^3 = 8 rules
C1 senior (60+) Y Y Y Y N N N N
C2 student card Y Y N N Y Y N N
C3 off-peak Y N Y N Y N Y N
A1 discount % 50 50 50 50 40 30 10 0
Collapsed: 5 rules
C1 senior (60+) Y N N N N
C2 student card - Y Y N N
C3 off-peak - Y N Y N
A1 discount % 50 40 30 10 0
Test cases (one per collapsed rule; '-' filled with N):
TC1: senior=True student=False offpeak=False -> 50%
TC2: senior=False student=True offpeak=True -> 40%
TC3: senior=False student=True offpeak=False -> 30%
TC4: senior=False student=False offpeak=True -> 10%
TC5: senior=False student=False offpeak=False -> 0%
পূর্ণ টেবিলে ৮টি rule, ছোট করার পরে ৫টি — stepper-এর সাথে মিল। মেশানোর ধাপগুলো ছিল: YYY আর YYN মিশে YY−, YNY আর YNN মিশে YN−, তারপর YY− আর YN− মিশে Y−−. “−” আছে এমন অবস্থানে আর মেশানো হয় না, তাই অ-সমান কিছু ভুল করে মিশে যায় না। শেষে প্রতিটি rule থেকে একটি test, “−”-এর জায়গায় N বসিয়ে। প্রত্যাশিত ফল এসেছে discount() থেকে, যা এখানে requirement-এর কোড রূপ — বাস্তবে oracle থাকবে requirement নথিতে, আর পরীক্ষা হবে আসল ভাড়া-system-এর বিরুদ্ধে।
শর্তগুলো সবসময় Y/N হয় না। “যাত্রার দূরত্ব: ১০ কিমির কম / ১০–৩০ / ৩০-এর বেশি” এমন তিন মানের শর্তে টেবিলটি extended entry — মোট rule তখন মানগুলোর গুণফল (২ × ২ × ৩ = ১২)। আর একটি শর্তের ভেতরের রেঞ্জ ঠিক করতে আবার EP আর BVA লাগে — কৌশলগুলো একসাথে কাজ করে। Requirement-এর ভাষা থেকে শর্ত আর ফলের সম্পর্ক আগে একটি ছবিতে আঁকার পদ্ধতিও আছে, cause-effect graph; তা থেকে decision table বের করা হয়।
৫ · State transition testing — অবস্থা আর ঘটনা
কিছু system-এর ফল শুধু এখনকার ইনপুটে নয়, আগে কী ঘটেছে তার ওপরও নির্ভর করে। একই “বাতিল” বোতাম টিকিট পরিশোধের আগে এক কাজ করে, পরে আরেক কাজ, আর যাত্রা শেষে কিছুই না। এমন জিনিসকে state আর transition দিয়ে মডেল করা হয় — UML state machine diagram পর্ব ২৩ Requirement modeling-এ আর পর্ব ৩০ UML behaviour diagram-এ দেখেছেন। State transition testing সেই মডেল থেকেই test বের করে।
একটি কাল্পনিক অনলাইন বাস-টিকিটের জীবনচক্র: আসন সংরক্ষণের পরে ১৫ মিনিটের মধ্যে টাকা না দিলে বাতিল; পরিশোধের পরে যাত্রার আগে বাতিল বা আসন বদল করা যায়; বাসে উঠলে টিকিট ব্যবহৃত।
বাস-টিকিটের state machine — প্রতিটি transition-এ test-এর নম্বর
Ticket state machine: Reserved, Paid, Cancelled, Used; transitions T1–T6 numbered for test design
State table — ছবির ফাঁকগুলো খুঁজে পাওয়া
ছবিতে শুধু বৈধ পথ আঁকা থাকে। কিন্তু ব্যবহারকারী ভুল সময়ে ভুল বোতামও চাপেন — দুবার pay, ব্যবহৃত টিকিট বাতিল। State table-এ প্রতিটি state-এর সারি আর প্রতিটি event-এর কলাম থাকে, তাই প্রতিটি মিলন চোখে পড়ে:
| State \ Event | pay | cancel | timeout | change | board |
|---|---|---|---|---|---|
| Reserved | T1 → Paid | T2 → Cancelled | T3 → Cancelled | ✗ | ✗ |
| Paid | ✗ | T4 → Cancelled | ✗ | T6 → Paid | T5 → Used |
| Used | ✗ | ✗ | ✗ | ✗ | ✗ |
| Cancelled | ✗ | ✗ | ✗ | ✗ | ✗ |
৪টি state × ৫টি event = ২০টি ঘর: ৬টি বৈধ transition আর ১৪টি invalid transition (✗)। প্রতিটি ✗-এর জন্য requirement-কে জিজ্ঞেস করতে হয়: এখানে কী হওয়ার কথা — চুপচাপ উপেক্ষা, error বার্তা, নাকি log? “Paid অবস্থায় আবার pay” যদি দ্বিতীয়বার টাকা কেটে নেয়, সেটি গুরুতর bug — আর ছবি দেখে এই প্রশ্ন সহজে ওঠে না, টেবিলে ওঠে।
Coverage: 0-switch আর 1-switch
- সব state: প্রতিটি state অন্তত একবার পৌঁছানো — সবচেয়ে দুর্বল মাপ।
- 0-switch coverage (সব বৈধ transition): T1–T6 প্রতিটি অন্তত একবার। প্রতিটি test শুরু হয় Reserved থেকে, তাই sequence হিসেবে লাগে: (ক) pay, change, board → T1, T6, T5; (খ) pay, cancel → T1, T4; (গ) cancel → T2; (ঘ) timeout → T3. ৪টি sequence-এ ৬টি transition. কম সম্ভব নয়: T2, T3 আর T1 প্রতিটি শুরুর transition, আর T4, T5 দুটিই শেষ state-এ নিয়ে যায়।
- 1-switch coverage (পরপর দুটি transition-এর প্রতিটি বৈধ জোড়া): Paid-এ ঢোকে T1 আর T6, Paid থেকে বের হয় T4, T5, T6 — তাই জোড়া ২ × ৩ = ৬টি: T1T4, T1T5, T1T6, T6T4, T6T5, T6T6. এটি switch coverage-এর ধারণা (Chow, ১৯৭৮-এর কাজ থেকে আসা)। N-switch মানে N+1টি পরপর transition-এর ক্রম।
- অবৈধ transition: ১৪টির প্রতিটি আলাদা test — প্রত্যাশিত ফল “প্রত্যাখ্যাত, state অপরিবর্তিত”।
এবার state table ধরে একটি event-এর ক্রম চালান — বৈধ আর অবৈধ দুটোই আছে:
| event | |
| আগের state | |
| ফল | |
| নতুন state | |
| 0-switch ঢাকা | |
| 1-switch ঢাকা |
- পরিশোধ ছাড়া বাসে ওঠা। Reserved-এর সারিতে board-এর ঘর ✗. প্রত্যাশিত ফল: প্রত্যাখ্যান আর বার্তা “আগে পরিশোধ করুন”; state Reserved-ই থাকে। এটি একটি invalid transition test — ১৪টির একটি।
- pay. Reserved + pay = T1, নতুন state Paid. প্রথম বৈধ transition ঢাকা হলো (১/৬)।
- আসন বদল। Paid + change = T6, যা Paid-এই ফেরে। T1-এর পরে T6 — প্রথম 1-switch জোড়া T1T6 ঢাকা হলো।
- দ্বিতীয়বার pay. Paid + pay = ✗. প্রত্যাশিত: টাকা আর কাটা হবে না, state Paid. Coverage বাড়ে না, কিন্তু এটিই হয়তো সবচেয়ে দামি test — ভুল হলে যাত্রীর টাকা দুবার কাটে।
- বাসে ওঠা। Paid + board = T5, নতুন state Used. 0-switch ৩/৬; T6-এর পরে T5, তাই T6T5 জোড়াও ঢাকা হলো।
- ব্যবহৃত টিকিট বাতিল। Used-এর সারিতে সব ✗. প্রত্যাখ্যান, ফেরত নেই। এক sequence-এ তিনটি বৈধ transition আর তিনটি অবৈধ পরীক্ষা হলো; বাকি T2, T3, T4 আর চারটি 1-switch জোড়ার জন্য আরও sequence লাগবে।
State machine বড় হলে transition-এর জোড়া দ্রুত বাড়ে (state explosion). তখন 0-switch সাধারণ নিয়ম, আর 1-switch বা তার বেশি শুধু ঝুঁকিপূর্ণ অংশে — যেমন টাকা কাটার আশপাশে। Guard শর্তও ভুলবেন না: T4-এর [যাত্রার আগে] শর্তে আবার BVA লাগে — যাত্রার ঠিক এক মিনিট আগে আর এক মিনিট পরে বাতিল।
৬ · Use case আর scenario testing — গল্প ধরে test
EP থেকে state transition পর্যন্ত কৌশলগুলো ছোট অংশ দেখে। কিন্তু ব্যবহারকারী একটি লক্ষ্য নিয়ে ধাপে ধাপে কাজ করেন, আর অনেক bug লুকিয়ে থাকে ধাপের সংযোগে। Scenario testing-এ use case-এর প্রতিটি পথ এক বা একাধিক test হয়। Use case লেখা আর acceptance criteria পর্ব ২২ Use case আর user story-এ বিস্তারিত আছে; এখানে শুধু test-এ রূপান্তর।
কাল্পনিক ফল-পোর্টালের use case “ছাত্র ফল দেখেন”:
- Main success scenario: ১. ছাত্র roll আর registration নম্বর দেন; ২. system মিলিয়ে দেখে; ৩. ছাত্র পরীক্ষা বাছেন; ৪. system grade-সহ ফল দেখায়।
- Extension 2a: নম্বর মেলেনি — বার্তা, আবার চেষ্টা; ৩ বার ভুলে ১০ মিনিট অপেক্ষা।
- Extension 3a: ফল এখনো প্রকাশ হয়নি — “প্রকাশের তারিখ” দেখায়।
- Extension 4a: ছাত্রের বকেয়া ফি আছে — ফল আটকে বার্তা দেখায়।
ন্যূনতম test: main scenario-র জন্য ১টি আর প্রতিটি extension-এর জন্য ১টি — মোট ৪টি। এরপর ভেতরের কৌশল যোগ হয়: 2a-তে “৩ বার” একটি সীমা, তাই ২য় আর ৩য় ভুলের test (BVA); “১০ মিনিট” আরেকটি সীমা। আর user story-র Given-When-Then আকারের acceptance criteria প্রায় সরাসরি scenario test হয়ে যায় — পর্ব ৪৮ TDD আর BDD-তে এগুলো চালানো যায় এমন test হিসেবে দেখবেন।
বিয়েবাড়ির আয়োজনে আলাদা করে রান্না, ডেকোরেশন, সাউন্ড সিস্টেম সব ঠিক থাকলেও বর আসার মুহূর্তে বিদ্যুৎ চলে গেলে জেনারেটর কে চালাবেন, তা কেউ ঠিক না করলে পুরো অনুষ্ঠান থমকে যায়। Scenario test এই “পুরো অনুষ্ঠানের মহড়া” — অংশগুলো আলাদাভাবে ঠিক থাকলেও একসাথে ঠিক চলে কি না।
৭ · Pairwise আর combinatorial testing — ৮১ থেকে ৯
ফল-পোর্টালটি কোন পরিবেশে চলবে: device (phone, tablet, laptop), network (2G, 4G, wifi), ভাষা (বাংলা, ইংরেজি, মিশ্র), ভূমিকা (ছাত্র, শিক্ষক, admin). ৪টি parameter, প্রতিটির ৩টি মান: সব মিলন 34 = ৮১টি। প্রতিটি হাতে চালাতে ১০ মিনিট লাগলে সাড়ে ১৩ ঘণ্টা।
Pairwise testing-এর যুক্তি: বাস্তবে অনেক ভুল ঘটে একটি parameter-এর একটি মানে, বা দুটি parameter-এর একটি নির্দিষ্ট জোড়ায় (যেমন “phone + 2G”-তে ফল লোড হয় না)। তাই লক্ষ্য হলো প্রতিটি parameter-জোড়ার প্রতিটি মান-জোড়া অন্তত একটি test-এ আসা। হিসাব: parameter-জোড়া C(4,2) = ৬টি, প্রতিটিতে 3 × 3 = ৯টি মান-জোড়া — মোট ৫৪টি জোড়া। একটি test একসাথে ৬টি জোড়া ঢাকে, তাই অন্তত ৫৪ ÷ ৬ = ৯টি test লাগবে। আর ৯টিতেই সম্ভব — নিচের প্রোগ্রাম দেখাবে:
// Greedy all-pairs (pairwise) suite: 4 parameters x 3 values (fictional result portal).
const P = { device: ["phone", "tablet", "laptop"], net: ["2G", "4G", "wifi"],
lang: ["bn", "en", "mixed"], role: ["student", "teacher", "admin"] };
const names = Object.keys(P);
let all = [[]]; // every full combination
for (const n of names) all = all.flatMap(c => P[n].map(v => [...c, v]));
const pairsOf = t => { // the 6 pairs one test covers
const out = [];
for (let i = 0; i < t.length; i++)
for (let j = i + 1; j < t.length; j++) out.push(`${i}=${t[i]}&${j}=${t[j]}`);
return out;
};
const need = new Set(all.flatMap(pairsOf)); // all pairs that must appear
const uncovered = new Set(need), suite = [];
while (uncovered.size > 0) { // pick the combo that covers most new pairs
let best = null, bestGain = -1;
for (const t of all) {
const gain = pairsOf(t).filter(p => uncovered.has(p)).length;
if (gain > bestGain) { best = t; bestGain = gain; }
}
pairsOf(best).forEach(p => uncovered.delete(p));
suite.push(best);
console.log(`T${suite.length}: ${best.join(" / ").padEnd(30)} +${bestGain} new pairs`);
}
const covered = new Set(suite.flatMap(pairsOf));
console.log(`full combinations : ${all.length}`);
console.log(`pairs to cover : ${need.size}`);
console.log(`pairwise suite : ${suite.length} tests`);
console.log(`every pair covered: ${[...need].every(p => covered.has(p))}`);আউটপুট:
T1: phone / 2G / bn / student +6 new pairs
T2: phone / 4G / en / teacher +6 new pairs
T3: phone / wifi / mixed / admin +6 new pairs
T4: tablet / 2G / en / admin +6 new pairs
T5: tablet / 4G / mixed / student +6 new pairs
T6: tablet / wifi / bn / teacher +6 new pairs
T7: laptop / 2G / mixed / teacher +6 new pairs
T8: laptop / 4G / bn / admin +6 new pairs
T9: laptop / wifi / en / student +6 new pairs
full combinations : 81
pairs to cover : 54
pairwise suite : 9 tests
every pair covered: true
Greedy নিয়ম: প্রতিবার ৮১টি মিলনের মধ্যে সেটি নেওয়া হয় যেটি সবচেয়ে বেশি নতুন জোড়া ঢাকে (সমান হলে তালিকার আগেরটি)। এখানে প্রতিটি পছন্দই পুরো ৬টি নতুন জোড়া দিয়েছে, তাই ৯টিতে ৫৪টি জোড়া — তাত্ত্বিক ন্যূনতম। শেষ লাইনটি আলাদাভাবে যাচাই করে যে সব জোড়া সত্যিই ঢাকা। Greedy সবসময় ন্যূনতম দেয় না; parameter বা মান বাড়লে বা অসমান হলে কয়েকটি test বেশি লাগতে পারে। এই ৯টি সারির গঠনটি একটি orthogonal array — L9(34) নামে পরিচিত।
Orthogonal array-র ধারণা পরিসংখ্যানের পরীক্ষা-নকশা থেকে আসে (Genichi Taguchi-র কাজে শিল্পে জনপ্রিয়); software test-এ এর ব্যবহার নিয়ে ১৯৮০-এর দশক থেকে লেখা আছে, আর ১৯৯০-এর দশকে AETG-এর মতো greedy পদ্ধতি নিয়ে গবেষণা হয়। আজ বেশ কিছু open-source আর গবেষণা প্রতিষ্ঠানের তৈরি pairwise-generator tool আছে; এদের অনেকেই নিষেধ (constraint) মানতে পারে — যেমন “laptop-এ 2G থাকবে না” — আর তিনটি বা তার বেশি parameter-এর মিলনও (combinatorial testing, t-way) বানাতে পারে। কোন tool কোন বৈশিষ্ট্য দেয়, তা সংস্করণভেদে আলাদা; ব্যবহারের আগে নথি দেখে নিন।
Pairwise জাদু নয়। (১) তিনটি parameter-এর একটি নির্দিষ্ট মিলনে ঘটা ভুল ধরা না-ও পড়তে পারে। (২) মান বাছাই খারাপ হলে (যেমন network-এর মান থেকে “offline” বাদ) কোনো সংখ্যা সাহায্য করে না — মানগুলো নিজেই EP আর BVA দিয়ে বাছতে হয়। (৩) প্রতিটি সারির প্রত্যাশিত ফল আলাদা করে ঠিক করতে হয়। তাই pairwise হলো পরিবেশ আর configuration-এর জন্য, যেখানে মিলন অনেক কিন্তু প্রতিটির ঝুঁকি মাঝারি।
৮ · অভিজ্ঞতা-নির্ভর কৌশল আর property-based testing-এর প্রথম স্বাদ
Error guessing আর checklist
অভিজ্ঞ tester জানেন কোথায় ভুল থাকে। Error guessing সেই জ্ঞানকে সচেতনভাবে ব্যবহার করে: সম্ভাব্য ভুলের তালিকা বানিয়ে প্রতিটির জন্য test. দলের অভিজ্ঞতা এক জায়গায় লিখে রাখলে হয় checklist-based testing. ফর্ম আর ইনপুট-ঘরের জন্য একটি নমুনা checklist:
Exploratory testing
Exploratory testing-এ test-এর নকশা, চালানো আর শেখা একসাথে চলে: আগে থেকে ধাপ লেখা থাকে না, প্রতিটি পর্যবেক্ষণ পরের পদক্ষেপ ঠিক করে। এটি “এলোমেলো ক্লিক” নয়। একটি নির্দিষ্ট লক্ষ্য থাকে — test charter, যেমন “৯০ মিনিট: ধীর network-এ ফল প্রকাশের দিনের ভিড় অনুকরণ করে দেখা, ফল-পাতা কোথায় ভাঙে” — আর শেষে লিখিত নোট: কী দেখলাম, কী bug, কোন প্রশ্ন বাকি। শব্দটি ১৯৮০-এর দশকে Cem Kaner-এর লেখায় আসে বলে জানা যায়; সময় বেঁধে charter-ভিত্তিক session-এর পদ্ধতি (session-based test management) Jonathan আর James Bach ২০০০ সালের দিকে বর্ণনা করেন। Scripted test যা জানা, তা নিশ্চিত করে; exploratory test যা জানা নেই, তা খোঁজে — দুটি একে অপরের পরিপূরক।
Property-based testing — একটি নিয়ম, অনেক এলোমেলো ইনপুট
এতক্ষণ প্রতিটি test-এ একটি নির্দিষ্ট ইনপুট আর একটি নির্দিষ্ট প্রত্যাশিত ফল ছিল। Property-based testing উল্টো দিক থেকে ভাবে: এমন একটি property লিখুন যা যেকোনো বৈধ ইনপুটে সত্য হওয়ার কথা, তারপর যন্ত্রকে শত শত এলোমেলো ইনপুট বানাতে দিন। Sort-এর ক্ষেত্রে দুটি property: ফলাফল ছোট থেকে বড় ক্রমে, আর ফলাফলে ঠিক ইনপুটের উপাদানগুলোই আছে (একই সংখ্যায়)। ভাড়ার ছাড়ের ক্ষেত্রে: ছাড়ের পরের ভাড়া কখনো শূন্যের কম বা মূল ভাড়ার বেশি নয়।
ব্যর্থ ইনপুট পাওয়া গেলে tool সেটিকে ধাপে ধাপে ছোট করে — উপাদান বাদ দিয়ে, মান কমিয়ে — যতক্ষণ test ব্যর্থ থাকে। একে বলে shrinking; এতে ৫০টি উপাদানের জট পাকানো ইনপুটের বদলে সবচেয়ে ছোট উদাহরণ হাতে আসে। নিচে কোনো library ছাড়া একটি খেলনা রূপ। ইনপুট আসে একটি নির্দিষ্ট seed-এর PRNG থেকে, তাই প্রতিবার একই ক্রম, আর ফল পুনরুৎপাদনযোগ্য:
import random
from collections import Counter
def my_sort(xs): # hand-written insertion sort (has a bug)
out = []
for x in xs:
i = 0
while i < len(out) - 1 and out[i] < x: # off-by-one: should be len(out)
i += 1
out.insert(i, x)
return out
def holds(xs): # the properties (invariants) of any sort
ys = my_sort(xs)
in_order = all(ys[k] <= ys[k + 1] for k in range(len(ys) - 1))
return in_order and Counter(ys) == Counter(xs) # same items, same counts
def shrink(xs): # keep a smaller input while it still fails
while True:
smaller = [xs[:k] + xs[k + 1:] for k in range(len(xs))] # drop one
smaller += [xs[:k] + [v // 2] + xs[k + 1:] for k, v in enumerate(xs) if v]
bad = next((s for s in smaller if not holds(s)), None)
if bad is None:
return xs
xs = bad
rng = random.Random(47) # fixed seed: same inputs every run
for trial in range(1, 101):
xs = [rng.randint(0, 100) for _ in range(rng.randint(0, 8))]
if not holds(xs):
print(f"trial {trial}: FAILED on {xs}")
print(f" my_sort gives {my_sort(xs)}")
small = shrink(xs)
print(f" shrunk to {small} -> {my_sort(small)}")
break
else:
print("100 random inputs passed")আউটপুট:
trial 1: FAILED on [8, 55, 70, 58, 73]
my_sort gives [55, 58, 70, 73, 8]
shrunk to [0, 1] -> [1, 0]
প্রথম এলোমেলো তালিকাতেই property ভাঙল: 8 শেষে চলে গেছে। কারণ লুপের শর্তে len(out) - 1 — শেষ উপাদানের সাথে তুলনাই হয় না, ফলে বড় উপাদান কখনো শেষ ঘরে বসে না, আর ছোট উপাদান ভুল জায়গায় ঢোকে। এটিই সীমায় off-by-one. Shrinking পাঁচ উপাদানের তালিকাকে নামাল [0, 1]-এ — দুটি উপাদান, যার উত্তর [1, 0]. এত ছোট উদাহরণ দেখে ভুলটি এক নজরে বোঝা যায়: দ্বিতীয় উপাদান কখনো প্রথমটির পরে বসছে না। Counter দিয়ে দ্বিতীয় property-ও যাচাই হচ্ছে, যাতে কেউ “সব মুছে খালি তালিকা ফেরত” দিয়ে প্রথম property ফাঁকি দিতে না পারে।
এই ধারণা জনপ্রিয় করে Koen Claessen আর John Hughes-এর Haskell library QuickCheck (ICFP ২০০০-এর গবেষণাপত্র)। Python-এ এর পরিচিত উত্তরসূরি Hypothesis (David MacIver-এর শুরু করা, ২০১০-এর দশকে)। আসল tool-গুলো ইনপুটের ধরন বুঝে বানায়, শুরুতেই সীমার কাছের মান (0, খালি তালিকা, খুব বড় সংখ্যা) বেশি চেষ্টা করে, ব্যর্থ উদাহরণ মনে রাখে, আর shrinking অনেক বুদ্ধিমান। বৈশিষ্ট্যগুলো সংস্করণভেদে আলাদা; এখানকার কোড শুধু মূল ভাবনা দেখায়। Property খোঁজা প্রায়ই কঠিন — পর্ব ৩১ Interface আর contract-এর precondition আর postcondition এখানে ভালো সূত্র দেয়।
৯ · কোন কৌশল কখন — ঝুঁকি ধরে বেছে নেওয়া
কোনো একটি কৌশল সব জায়গায় সেরা নয়। কৌশল বাছাইয়ের প্রশ্ন দুটি: এই অংশের আচরণ কী ধরনের (রেঞ্জ, শর্তের মিলন, অবস্থা, ধাপের ক্রম, অনেক configuration), আর ভুল হলে ক্ষতি কত (risk). ঝুঁকি দেখে test-এর গভীরতা ঠিক করাকে বলে risk-based testing (ঝুঁকির হিসাব আর মাপ পর্ব ৬২ Risk আর quality management-এ)।
| আচরণের ধরন | মূল কৌশল | ঝুঁকি বেশি হলে যোগ করুন | উদাহরণ (কাল্পনিক) |
|---|---|---|---|
| সংখ্যা, তারিখ বা দৈর্ঘ্যের রেঞ্জ | EP + ২-মান BVA | ৩-মান BVA, লুকানো সীমা | নম্বর → grade |
| কয়েকটি শর্তের মিলনে ফল | Decision table (ছোট করা) | পূর্ণ টেবিল, don't-care যাচাই | বাস-ভাড়ার ছাড় |
| অবস্থা আর ঘটনা | State table, 0-switch | 1-switch, সব invalid transition | টিকিটের জীবনচক্র |
| ধাপে ধাপে ব্যবহারকারীর লক্ষ্য | Use case: প্রতিটি পথ | প্রতিটি extension-এ BVA | ফল দেখা |
| অনেক parameter বা configuration | Pairwise | ৩-way combinatorial | device × network × ভাষা × ভূমিকা |
| নিয়ম আছে, ইনপুটের জগৎ বিশাল | Property-based | বেশি run, ধরন-ভিত্তিক generator | sort, ভাড়ার হিসাব |
| নতুন বা অস্পষ্ট অংশ | Exploratory, error guessing | checklist, পুনরাবৃত্ত session | নতুন ফল-প্রকাশের পাতা |
একটি বাস্তব চিত্র: ফল-পোর্টালে grade বসানো আর টাকা কাটা সবচেয়ে ঝুঁকিপূর্ণ (ভুল হলে ছাত্রের ভবিষ্যৎ বা টাকা) — সেখানে ৩-মান BVA, পূর্ণ decision table, 1-switch. প্রোফাইলের ছবি বদলানোর মতো কম ঝুঁকির অংশে EP আর একটি exploratory session যথেষ্ট। এই বাছাইয়ের সিদ্ধান্ত test plan-এ লিখে রাখুন, যাতে পরে বোঝা যায় কেন কোথায় কতটা test (requirement থেকে test পর্যন্ত traceability পর্ব ২৪ Requirement management-এ)।
এক রেঞ্জ [a, b] (integer): EP ৩টি, ২-মান BVA ৪টি (a−1, a, b, b+1), ৩-মান BVA ৬টি (a−1, a, a+1, b−1, b, b+1). n-টি Y/N শর্ত: পূর্ণ decision table 2n rule. k-টি parameter, প্রতিটির v-টি মান: সব মিলন vk, pairwise-এ অন্তত v2টি test. State machine: 0-switch-এ test-এর লক্ষ্য = বৈধ transition-এর সংখ্যা; 1-switch-এ প্রতিটি state-এ (ঢোকার transition × বের হওয়ার transition)-এর যোগফল।
আরও গভীরে: ISO/IEC/IEEE 29119-4 (test techniques) এই পর্বের প্রায় সব কৌশলের আনুষ্ঠানিক সংজ্ঞা আর coverage-এর মাপ দেয়। Domain testing-এ on/off point-এর গভীর রূপ পাবেন Beizer-এর বইয়ে আর Kaner, Padmanabhan ও Hoffman-এর The Domain Testing Workbook-এ (২০১৩)। Combinatorial testing নিয়ে NIST-এর গবেষকদের (D. Richard Kuhn প্রমুখ) প্রকাশনা পড়ার মতো — কত-way মিলনে বাস্তব ভুল ঘটে, সে বিষয়ে তাঁদের বিশ্লেষণ আছে। Property-based testing-এর মূল উৎস Claessen ও Hughes-এর QuickCheck গবেষণাপত্র (২০০০)।
পরীক্ষা ও ইন্টারভিউতে:
- EP আর BVA-র পার্থক্য? — EP প্রতিটি দল থেকে একটি প্রতিনিধি নেয়; BVA দলের কিনারার মান আর পাশের মান নেয়, কারণ off-by-one ভুল সেখানেই।
- ১–১০০ রেঞ্জে ২-মান আর ৩-মান BVA? — ২-মান: 0, 1, 100, 101 (৪টি); ৩-মান: 0, 1, 2, 99, 100, 101 (৬টি)।
- ৪টি Y/N শর্তে decision table-এ কয়টি rule? — 24 = ১৬; don't-care দিয়ে কমতে পারে, যদি কিছু শর্ত কিছু rule-এ ফল না বদলায়।
- 0-switch আর 1-switch coverage? — 0-switch: প্রতিটি বৈধ transition অন্তত একবার; 1-switch: পরপর দুটি transition-এর প্রতিটি বৈধ ক্রম অন্তত একবার।
- একটি test-এ কেন দুটি invalid মান নয়? — Error masking: প্রথম ভুলে system থেমে গেলে দ্বিতীয়টির যাচাই আছে কি না জানা যায় না।
১০ · AI-এর যুগে test নকশা — AI-কে কৌশল ধরিয়ে দেওয়া, আর তার কাজ যাচাই
AI-কে “এই function-এর test লিখে দাও” বললে সাধারণত কয়েকটি সুন্দর দেখতে, সহজ পথের test আসে: 50 → C, 95 → A. সীমা থাকে না, invalid partition থাকে না, আর প্রায়ই প্রত্যাশিত ফল নেওয়া হয় কোড চালিয়ে — ফলে test কোডের ভুলকেই “সঠিক” বলে পাকা করে। AI আরও যা করে: ISTQB বা কোনো মানের নামে এমন সংজ্ঞা বা “নিয়ম” বলে যা সেখানে নেই; pairwise tool-এর অস্তিত্বহীন option বানায়; decision table-এ rule গুনতে ভুল করে; state diagram-এ UML-এর বাইরের চিহ্ন বসায়। AI-কে কৌশলের নাম আর requirement সরাসরি দিন, আর তার উত্তর হাতে গুনে মিলিয়ে নিন:
নিচের prompt-এ requirement আলাদা, কৌশল নির্দিষ্ট, আর প্রত্যাশিত ফল requirement থেকে নিতে বলা হয়েছে — কোড থেকে নয়:
AI-এর test নকশা হাতে পেলে — যাচাইয়ের চেকলিস্ট
ভাবনার প্রশ্ন
প্রতিটি প্রশ্ন নিয়ে নিজে কিছুক্ষণ ভাবুন — তারপর “→ উত্তর” চাপুন।
প্র ০১Dijkstra বলেছিলেন, test দিয়ে bug-এর উপস্থিতি দেখানো যায়, অনুপস্থিতি নয়। তাহলে test নকশার এত কৌশলের মূল্য কী?
Edsger Dijkstra-র এই কথাটি সাধারণত ১৯৬৯ সালের NATO software engineering সম্মেলনের (রোম) প্রতিবেদন আর তাঁর ১৯৭০-এর “Notes on Structured Programming”-এর সাথে যুক্ত করে উদ্ধৃত হয়। যুক্তিটি অকাট্য: ১০১টি বৈধ নম্বরের একটি ঘরেও আমাদের ১৬টি সংখ্যার test-এর মধ্যে বৈধ মান মাত্র ১২টি; বাকি ৮৯টি বৈধ মানে কী হয়, test সরাসরি বলে না। আর বাস্তব system-এর ইনপুটের জগৎ এর চেয়ে অনেক গুণ বড়। Dijkstra-র সমাধান ছিল প্রমাণযোগ্যভাবে সঠিক প্রোগ্রাম গঠন — formal reasoning.
তবু test নকশার কৌশলগুলো অর্থহীন নয়, কারণ এরা এলোমেলো নমুনা নয়, অনুমানভিত্তিক নমুনা। EP-র অনুমান: একই partition-এর মানগুলোর সাথে কোড একই পথে চলে। যদি এই অনুমান সত্য হয়, তাহলে প্রতিনিধি মানটি সত্যিই বাকিদের হয়ে কথা বলে। BVA-র অনুমান: ভুল সবচেয়ে বেশি থাকে যেখানে কোডের শর্ত বদলায়। এই অনুমানগুলো দশকের অভিজ্ঞতা থেকে এসেছে, আর এদের ব্যর্থতাও চেনা — কোডে যদি লুকানো একটি বিশেষ ক্ষেত্র থাকে (যেমন “marks == 73 হলে আলাদা কিছু”), কোনো black-box কৌশল সেটি নিশ্চিতভাবে ধরবে না। এজন্যই white-box coverage (পর্ব ৫০) আর code review (পর্ব ৪৪) পাশাপাশি লাগে।
দ্বিতীয় মূল্য: কৌশল test-কে পুনরুৎপাদনযোগ্য আর আলোচনাযোগ্য করে। “আমি অনেক test করেছি” বলা আর “প্রতিটি partition, প্রতিটি সীমার ২-মান, আর সব বৈধ transition ঢাকা” বলা এক নয়। দ্বিতীয়টি অন্য কেউ যাচাই করতে পারেন, আর কোথায় ফাঁক রইল তাও বলা যায়। তৃতীয়ত, ঝুঁকির সাথে খরচ মেলানো যায় — যেখানে ভুলের দাম বেশি, সেখানে ৩-মান আর 1-switch.
আর যেখানে প্রমাণ সত্যিই দরকার — বিমান, রেলের signal, চিকিৎসা যন্ত্র — সেখানে test-এর সাথে formal method, static analysis আর কঠোর review মেলানো হয়। অর্থাৎ Dijkstra-র কথা test বাদ দেওয়ার যুক্তি নয়, test-কে সঠিক জায়গায় বসানোর যুক্তি।
মূল উপলব্ধি: Test আত্মবিশ্বাস বাড়ায়, প্রমাণ দেয় না। কৌশল সেই আত্মবিশ্বাসকে অনুমাননির্ভর, মাপা যায় এমন আর অন্যের কাছে যাচাইযোগ্য করে — আর তার সীমাও খোলাখুলি বলে।
প্র ০২বাস্তবের বেশ কিছু বিখ্যাত বিভ্রাট ঘটেছে এমন সীমায়, যা requirement-এ লেখাই ছিল না। এ থেকে boundary value analysis নিয়ে কী শেখার আছে?
কয়েকটি প্রকাশ্যে আলোচিত উদাহরণ (বিবরণ প্রকাশিত প্রতিবেদন অনুযায়ী, সংক্ষেপে)। ২০০৮ সালের ৩১ ডিসেম্বর একটি পরিচিত কোম্পানির একটি মডেলের portable music player একসাথে অনেক জায়গায় আটকে যায়; পরে জানা যায়, অধিবর্ষের ৩৬৬তম দিনে তারিখ হিসাবের একটি লুপ থেকে বের হওয়ার শর্ত পূরণ হচ্ছিল না। ২০১৪ সালে একটি ভিডিও সাইট জানায়, একটি জনপ্রিয় ভিডিওর দর্শক-সংখ্যা 32-bit signed integer-এর সর্বোচ্চ মান (2,147,483,647) ছুঁতে চলায় তারা counter 64-bit-এ নিয়েছে। ২০১৫ সালে মার্কিন বিমান নিয়ন্ত্রক FAA একটি নির্দেশ জারি করে যে একটি আধুনিক যাত্রীবিমানের জেনারেটর নিয়ন্ত্রণ software টানা প্রায় ২৪৮ দিন চালু থাকলে একটি counter overflow-এ বিদ্যুৎ চলে যেতে পারে; সমাধান হিসেবে নির্দিষ্ট সময় পরপর বন্ধ-চালু করার নির্দেশ দেওয়া হয়।
তিনটিতেই সাধারণ সূত্র: সীমাটি ব্যবসার নিয়মে নয়, প্রযুক্তির ভেতরে — তারিখের ক্যালেন্ডার, integer-এর আকার, চালু থাকার সময়। Requirement-এ “দর্শক-সংখ্যা দেখাও” লেখা থাকে, “২১০ কোটি পেরোলেও দেখাও” লেখা থাকে না। তাই শুধু requirement-এর সংখ্যা থেকে BVA করলে এই সীমাগুলো চোখ এড়ায়।
শিক্ষাগুলো: (১) প্রতিটি ঘরের data-র ধরন জেনে তার সীমাও partition-এর তালিকায় রাখুন — integer-এর সর্বোচ্চ, string-এর দৈর্ঘ্য, তারিখের অধিবর্ষ, সময়-অঞ্চলের পরিবর্তন। (২) সময়ও একটি ইনপুট — “অনেক দিন চালু থাকলে কী হয়” এর test সাধারণত ঘড়ি নকল করে (test double পর্ব ৪৯-এ) চালানো হয়, কারণ ২৪৮ দিন অপেক্ষা সম্ভব নয়। (৩) এই সীমাগুলো error guessing checklist-এ স্থায়ীভাবে লিখে রাখুন, যাতে দলের স্মৃতিতে না থেকে তালিকায় থাকে। (৪) Property-based testing-এর generator-গুলো প্রায়ই এমন চরম মান বেশি চেষ্টা করে।
লক্ষ করুন, এই ঘটনাগুলোর কোনোটিই “প্রোগ্রামার অযোগ্য ছিলেন” গল্প নয়। বড় দলেও সীমাটি কারও তালিকায় ছিল না, কারণ সবাই ব্যবসার নিয়মের সীমা ভাবছিলেন।
মূল উপলব্ধি: BVA-র সীমা দুই উৎস থেকে আসে — requirement আর প্রযুক্তি। দ্বিতীয়টি কেউ লিখে দেয় না; data-র ধরন, সময় আর আকার নিয়ে নিজে প্রশ্ন করে তালিকায় আনতে হয়।
প্র ০৩Pairwise testing ধরে নেয় বেশিরভাগ ভুল দুটি parameter-এর মিলনে ঘটে। এই অনুমানের প্রমাণ কতটুকু, আর কোথায় এটি বিপজ্জনক?
সবচেয়ে বেশি উদ্ধৃত প্রমাণ আসে NIST-এর গবেষকদের কাজ থেকে — D. Richard Kuhn, Dolores Wallace ও সহকর্মীদের ২০০০-এর দশকের প্রথম দিকের প্রকাশনা (যেমন IEEE Transactions on Software Engineering, ২০০৪)। তাঁরা কয়েকটি ক্ষেত্রের (চিকিৎসা যন্ত্র, browser, server, database ইত্যাদি) প্রকাশিত বা জমা bug report বিশ্লেষণ করে দেখান, অধ্যয়ন করা system-গুলোতে বেশিরভাগ ভুল একটি বা দুটি parameter-এর মানে ঘটেছে, আর মিলনের মাত্রা বাড়লে ভুলের অনুপাত দ্রুত কমে — বিশ্লেষণ করা তথ্যে প্রায় সব ভুল ছয় বা তার কম parameter-এর মিলনে ধরা পড়েছে। এটি শক্তিশালী ইঙ্গিত, কিন্তু সার্বজনীন সূত্র নয়: নমুনা নির্দিষ্ট কিছু system-এর, আর কোন ক্ষেত্রে অনুপাত কত, তা ভিন্ন ছিল।
বিপদ কোথায়? প্রথমত, তিন-মিলনের ভুল: “phone + 2G + admin”-এ ঘটা ভুল ৯টি pairwise test-এর কোনোটিতে না-ও আসতে পারে, কারণ এই তিনটি একসাথে কোনো সারিতে নেই। ঝুঁকিপূর্ণ জায়গায় তাই 3-way suite বানানো হয় — আকার বাড়ে, কিন্তু 34-এর চেয়ে অনেক কম থাকে। দ্বিতীয়ত, মান বাছাই: pairwise শুধু যে মানগুলো দেওয়া হয়েছে তাদের মেলায়। Network-এর তালিকায় “offline” না থাকলে কোনো সংখ্যা সেটি খুঁজে দেবে না। তৃতীয়ত, নিষেধ: বাস্তবে কিছু মিলন অসম্ভব বা অর্থহীন; সেগুলো বাদ দিতে গেলে suite-এর গঠন ভাঙে, তাই tool-এর constraint ক্ষমতা লাগে। চতুর্থত, ধারাবাহিক আচরণ: যে ভুল ঘটে কাজের ক্রমে (আগে X তারপর Y), সেটি configuration-এর মিলন দিয়ে ধরা যায় না — সেখানে state transition বা scenario লাগে।
তাই pairwise-কে দেখুন একটি অর্থনৈতিক হাতিয়ার হিসেবে: ৮১-র বদলে ৯টি test-এ প্রতিটি মান আর প্রতিটি জোড়া অন্তত একবার। এটি “সব ঠিক আছে”-র প্রমাণ নয়, বরং সীমিত সময়ে প্রথম ও দ্বিতীয় স্তরের মিলনের ঝুঁকি দ্রুত কমানো। আর যেখানে ব্যর্থতার দাম অনেক, সেখানে মাত্রা বাড়ান।
মূল উপলব্ধি: Pairwise-এর ভিত্তি অভিজ্ঞতালব্ধ — কিছু system-এর বিশ্লেষণ, সার্বজনীন নিয়ম নয়। মান বাছাই, নিষেধ আর ঝুঁকি অনুযায়ী মাত্রা (2-way, 3-way) ঠিক করাই আসল দক্ষতা।
প্র ০৪Exploratory testing কি শুধু অভিজ্ঞ tester-এর “মন যা চায় তাই চাপা”? আগে থেকে লেখা script-এর test থাকলে এর দরকার কী?
Script-এর test একটি প্রশ্নের উত্তর দেয়: “আমরা যা জানি তা কি এখনো ঠিক আছে?” প্রতিটি ধাপ আর প্রত্যাশিত ফল আগে লেখা, তাই যে কেউ চালাতে পারেন, আর automation-এ রূপ দেওয়া সহজ। কিন্তু script লেখা হয় requirement আর লেখকের কল্পনা থেকে; যে ভুল কেউ কল্পনা করেননি, script সেটি খোঁজে না। একই script বারবার চালালে এক সময় নতুন bug আর পাওয়া যায় না — কৃষিকাজে একই কীটনাশকে কীট সহনশীল হওয়ার উপমায় একে কেউ কেউ “pesticide paradox” বলেন (Beizer-এর বইয়ে এই নাম আছে বলে জানা যায়)।
Exploratory testing প্রশ্নটা বদলায়: “আমরা কী জানি না?” Cem Kaner ১৯৮০-এর দশকে শব্দটি চালু করেন বলে জানা যায়, আর পরে James Bach সহ অনেকে একে শৃঙ্খলায় আনেন। মূল কথা: শেখা, নকশা আর চালানো একসাথে। Tester একটি অস্বাভাবিক আচরণ দেখলে সাথে সাথে সেটির আশপাশে আরও খোঁজেন — যা script পারে না, কারণ script-এর পরের ধাপ আগেই ঠিক।
এলোমেলো হওয়া ঠেকায় কাঠামো। Session-based test management-এ (Jonathan ও James Bach, ২০০০ সালের দিকে) প্রতিটি session-এর একটি charter থাকে — কোন অংশ, কোন ঝুঁকি, কত সময় (প্রায়ই ৬০–১২০ মিনিট) — আর শেষে লিখিত নোট আর সংক্ষিপ্ত আলোচনা। কোন অংশ কতবার explore হলো, তার হিসাবও রাখা যায়। Tester-এর মাথায় এই পর্বের কৌশলগুলোই কাজ করে: একটি সীমা চোখে পড়লে BVA, একটি অবস্থার বদল দেখলে state-এর প্রশ্ন।
দুটির সম্পর্ক তাই পরিপূরক। নতুন feature-এ আগে exploratory session ভুলের ধরন চেনায়; যা পাওয়া গেল তার স্থায়ী regression test script বা automation হয়ে যায়। আর পুরোনো, স্থিতিশীল অংশে script পাহারা দেয়, exploratory সময় যায় নতুন ঝুঁকিতে। ISTQB-ও exploratory-কে অভিজ্ঞতা-নির্ভর কৌশলের মধ্যে রাখে, black-box কৌশলের বিকল্প হিসেবে নয়।
মূল উপলব্ধি: Script জানা জিনিস পাহারা দেয়, exploration অজানা খোঁজে। Charter, সময়সীমা আর নোট থাকলে exploratory testing শৃঙ্খলিত আর মাপা যায় এমন কাজ।
প্র ০৫Property-based testing যদি হাজার ইনপুট নিজেই বানায়, তাহলে কি হাতে বাছা উদাহরণের test আর লাগবে না?
QuickCheck (Koen Claessen ও John Hughes, ICFP ২০০০) দেখিয়েছিল, একটি property লিখে যন্ত্রকে ইনপুট বানাতে দিলে মানুষের কল্পনার বাইরের ক্ষেত্র ধরা পড়ে। পরে Erlang-সহ নানা ভাষায় বাণিজ্যিক আর open-source সংস্করণ আসে, আর Python-এ Hypothesis বহুল ব্যবহৃত হয়। প্রকাশিত অভিজ্ঞতায় দেখা যায়, telecom বা গাড়ির software-এর মতো জটিল ক্ষেত্রে property-ভিত্তিক test এমন ভুল খুঁজেছে যা বছরের পর বছর উদাহরণ-ভিত্তিক test পার হয়ে গিয়েছিল — John Hughes এ নিয়ে একাধিক বক্তৃতা ও লেখায় উদাহরণ দিয়েছেন।
তবু উদাহরণের test বাদ যায় না, তিনটি কারণে। প্রথমত, property সাধারণত আংশিক নির্দিষ্টকরণ। “ছাড়ের পরের ভাড়া ০ থেকে মূল ভাড়ার মধ্যে” — সত্য, কিন্তু ছাত্রের ৩০% আসলে ৩০% কি না, এটি বলে না; ২৯% দিলেও property পাস। নির্দিষ্ট উত্তরের জন্য উদাহরণ লাগে (decision table-এর ৫টি test). দ্বিতীয়ত, উদাহরণ নথি: “40 → C” পড়লেই নিয়ম বোঝা যায়; property পড়ে বোঝা কঠিন। তৃতীয়ত, এলোমেলো ইনপুট ঠিক সীমার মানে পড়বে, এমন নিশ্চয়তা নেই — ভালো tool চরম মান বেশি চেষ্টা করে, কিন্তু requirement-এর নির্দিষ্ট সীমা (39/40) সে জানে না।
কাজের property খোঁজার কয়েকটি পরিচিত ধাঁচ: round-trip (encode করে decode করলে আগেরটি ফেরে), invariant (sort-এ ক্রম আর উপাদান একই), সহজ মডেলের সাথে তুলনা (নিজের দ্রুত sort-এর ফল ভাষার নিজস্ব sort-এর সাথে মেলে), idempotence (দুবার চালালে একবারের সমান), আর metamorphic সম্পর্ক (ইনপুট এভাবে বদলালে ফল ওভাবে বদলাবে)। আর অবশ্যই seed লিখে রাখা — ব্যর্থতা পুনরুৎপাদন না করা গেলে debug অসম্ভব; এজন্যই উপরের কোডে random.Random(47).
মূল উপলব্ধি: Property বলে “সবসময় কী সত্য”, উদাহরণ বলে “ঠিক এখানে কী হবে”। দুটি একসাথে শক্তিশালী: property অজানা কোণ খোঁজে, উদাহরণ নির্দিষ্ট নিয়ম আর সীমা পাহারা দেয়।
অনুশীলন
কাগজ-কলম নিন। প্রথমটিতে হিসাব, দ্বিতীয়টিতে নকশা, তৃতীয়টিতে আউটপুট অনুমান, চতুর্থটিতে সমালোচনা।
-
কয়টি test লাগবে (numerical): একটি কাল্পনিক বাস-কোম্পানির ভাড়া নিয়ম যাত্রীর বয়সের (integer, বৈধ 0–120) ওপর: 0–4 বিনা ভাড়া, 5–11 অর্ধেক, 12–59 পূর্ণ, 60–120 প্রবীণ। (ক) সংখ্যার দিকে EP-তে কয়টি test? (খ) ২-মান BVA-তে কয়টি, কোন মান? (গ) ৩-মান BVA-তে কয়টি? (ঘ) ভাড়ার নিয়মে চারটি Y/N শর্ত থাকলে পূর্ণ decision table-এ কয়টি rule? (ঙ) টিকিটের state machine: Reserved থেকে T1 pay → Paid, T2 cancel → Cancelled, T3 timeout → Cancelled; Paid থেকে T4 cancel → Cancelled, T5 board → Used, T6 change → Paid. 1-switch জোড়া কয়টি, আর 0-switch-এ ন্যূনতম কয়টি sequence (প্রতিটি Reserved থেকে শুরু)?
(ক) ৬টি: অবৈধ নিচে, চারটি বৈধ, অবৈধ ওপরে — যেমন −5, 2, 8, 35, 90, 125.
(খ) ১০টি: −1, 0, 4, 5, 11, 12, 59, 60, 120, 121. পাশাপাশি দুটি partition-এর সীমায় (4/5, 11/12, 59/60) দুটি মান একে অপরের off point, তাই নতুন মান লাগে শুধু দুই প্রান্তে (−1, 121).
(গ) ১৮টি: প্রতিটি সীমার মান (0, 4, 5, 11, 12, 59, 60, 120) আর তার দুই প্রতিবেশী, পুনরাবৃত্তি বাদে: −1, 0, 1, 3, 4, 5, 6, 10, 11, 12, 13, 58, 59, 60, 61, 119, 120, 121.
(ঘ) ১৬টি: 24. Don't-care দিয়ে পরে কমতে পারে।
(ঙ) Paid-এ ঢোকে T1, T6; বের হয় T4, T5, T6 → 1-switch জোড়া ২ × ৩ = ৬টি (T1T4, T1T5, T1T6, T6T4, T6T5, T6T6). অন্য কোনো state-এ ঢোকা আর বের হওয়া দুটোই নেই। 0-switch-এ ন্যূনতম ৪টি sequence: T1, T2, T3 প্রতিটি শুরুর transition বলে আলাদা sequence চায়, আর T1-এর পরে T4 আর T5 দুটিই শেষ state-এ নিয়ে যায়, তাই T1-এর দুটি sequence লাগে — যেমন pay-change-board, pay-cancel, cancel, timeout.
-
Test নকশা করুন: একটি কাল্পনিক বৃত্তির নিয়ম: GPA (দুই দশমিক, বৈধ 0.00–5.00) 4.50 বা বেশি, মাসিক পারিবারিক আয় (পূর্ণ টাকা, 0 বা বেশি) 20,000 বা কম, আর উপস্থিতি (integer শতাংশ, 0–100) 75 বা বেশি — তিনটিই হলে বৃত্তি, নইলে নয়। প্রতিটি ঘরের partition আর ২-মান boundary, ছোট করা decision table, আর চূড়ান্ত test-এর তালিকা বানান। Requirement-এ কোনো ফাঁক থাকলে বলুন।
ঘর ধরে partition আর ২-মান boundary:
- GPA: অবৈধ (<0) │ অযোগ্য 0.00–4.49 │ যোগ্য 4.50–5.00 │ অবৈধ (>5.00). ক্ষুদ্রতম ধাপ 0.01, তাই boundary: −0.01, 0.00, 4.49, 4.50, 5.00, 5.01.
- আয়: অবৈধ (<0) │ যোগ্য 0–20000 │ অযোগ্য 20001 থেকে। Boundary: −1, 0, 20000, 20001. ওপরের কোনো সীমা লেখা নেই — এটি requirement-এর ফাঁক; system-এর সর্বোচ্চ গ্রহণযোগ্য মান জিজ্ঞেস করুন (লুকানো সীমা)।
- উপস্থিতি: অবৈধ (<0) │ অযোগ্য 0–74 │ যোগ্য 75–100 │ অবৈধ (>100). Boundary: −1, 0, 74, 75, 100, 101.
Decision table: তিনটি শর্তের AND, পূর্ণ টেবিলে ৮টি rule, শুধু YYY → বৃত্তি। ছোট করলে ৪টি: (Y,Y,Y) → হ্যাঁ; (N,−,−) → না; (Y,N,−) → না; (Y,Y,N) → না।
চূড়ান্ত তালিকা (একটি যুক্তিসংগত রূপ): বৃত্তির rule-টি সব on point একসাথে দিয়ে: GPA 4.50, আয় 20000, উপস্থিতি 75 → হ্যাঁ। প্রতিটি “না” rule-এ একটি শর্ত ঠিক off point-এ, বাকি দুটি যোগ্য: 4.49 → না; আয় 20001 → না; উপস্থিতি 74 → না। এতে decision table আর ভেতরের সীমা একসাথে ঢাকা। এরপর বাকি boundary (0.00, 5.00, 0, 100) আর প্রতিটি অবৈধ মান (−0.01, 5.01, −1, 101, −1 টাকা) আলাদা test-এ, প্রতিবার বাকি ঘর বৈধ রেখে — error masking এড়াতে। আরও ফাঁক: GPA কি সবসময় দুই দশমিকে আসে (4.5 লিখলে?), খালি ঘর হলে কী, আর আয়ের প্রমাণপত্রের নিয়ম কী।
-
আউটপুট অনুমান করুন: অংশ ৩-এর JavaScript প্রোগ্রামের শেষ দুই লাইনের বদলে শুধু এই লাইনটি চালালে কী ছাপা হবে?
design("pages", [[1, 5, "S"], [6, 10, "L"]]);— প্রতিটি class-এর প্রতিনিধি, EP tests, 2-value আর 3-value-এর লাইনগুলো লিখুন।আসল আউটপুট (চালিয়ে যাচাই করা):
== pages INVALID low -Infinity .. 0 rep=-4 S 1 .. 5 rep=3 L 6 .. 10 rep=8 INVALID high 11 .. Infinity rep=15 EP tests : 4 2-value : 0, 1, 5, 6, 10, 11 (6) 3-value : 0, 1, 2, 4, 5, 6, 7, 9, 10, 11 (10)নিচের অসীম class-এর প্রতিনিধি 0 − 4 = −4, ওপরেরটি 11 + 4 = 15; S আর L-এর মাঝামাঝি floor((1+5)/2) = 3 আর floor((6+10)/2) = 8. সীমার মান 1, 5, 6, 10 — ২-মানে নিচের দুই প্রান্তে 0 আর ওপরে 11 যোগ হয়ে ৬টি; ৩-মানে প্রতিটির দুই প্রতিবেশী নিয়ে 0, 1, 2, 4, 5, 6, 7, 9, 10, 11 = ১০টি (3 আর 8 আসে না, কারণ এরা কোনো সীমার প্রতিবেশী নয়)।
-
সমালোচনা করুন: একজন সহকর্মী AI দিয়ে grade-নিয়মের (০–৩৯ F, ৪০–৫৯ C, ৬০–৭৯ B, ৮০–১০০ A, এর বাইরে প্রত্যাখ্যান) জন্য চারটি test বানিয়ে বললেন: “10 → F, 50 → C, 75 → B, 95 → A. চারটি grade-ই ঢাকা, তাই ১০০% coverage.” আর প্রত্যাশিত ফলগুলো তিনি নিয়েছেন function চালিয়ে যা এসেছে তা থেকে। এই suite-এর দুর্বলতা কী, আর কীভাবে ঠিক করবেন?
- Coverage-এর দাবি ভুল: চারটি valid partition ঢাকা, কিন্তু দুটি invalid partition (−1, 101) আর ধরনের অবৈধ মান (খালি, “abc”) বাদ — সংখ্যার দিকের ৬টির মধ্যে ৪টি, অর্থাৎ প্রায় ৬৭%.
- কোনো সীমা নেই:
> 40বা< 80-এর মতো off-by-one ভুল একটিও ধরবে না। অন্তত ২-মান BVA-র ১০টি মান যোগ করুন: −1, 0, 39, 40, 59, 60, 79, 80, 100, 101. - Oracle ভুল জায়গা থেকে: function চালিয়ে প্রত্যাশিত ফল নিলে test কোডের আয়না — কোডে ভুল থাকলে test-ও সেই ভুলকে “ঠিক” বলবে। প্রত্যাশিত ফল requirement-এর নিয়ম থেকে লিখুন।
- যাচাইয়ের উপায়: কোডে একটি সীমা ইচ্ছা করে বদলে দেখুন (40-কে 41) — নতুন suite-এর অন্তত একটি test ব্যর্থ হওয়া উচিত; পুরোনো চারটি পাস করবে। এটিই mutation testing-এর মূল ভাবনা (পর্ব ৫০)।
নিজেকে যাচাই করুন
ভুল হলে কারণসহ ব্যাখ্যা পাবেন, আবার চেষ্টাও করতে পারবেন।
ক্রমএকটি ইনপুট-ঘরের test নকশার ধাপগুলো সঠিক ক্রমে সাজান (▲▼ চেপে সরান)
- Requirement পড়ে ঘরের ধরন, বৈধ রেঞ্জ আর ভিন্ন আচরণের নিয়ম লেখা
- Valid আর invalid partition চিহ্নিত করা
- প্রতিটি partition-এর প্রতিনিধি আর প্রতিটি সীমার boundary value বের করা
- প্রতিটি test-এর প্রত্যাশিত ফল requirement থেকে লেখা
- Test চালিয়ে আসল ফল প্রত্যাশিত ফলের সাথে মেলানো
একটি ঘরে integer 1 থেকে 50 পর্যন্ত বৈধ। ISTQB-এর ৩-মান boundary value analysis-এ কয়টি test মান লাগবে?
- ৪টি
- ৫টি
- ৬টি
- ৫০টি
Requirement: “x ≤ 10 হলে গ্রহণ”, কিন্তু কোডে ভুল করে লেখা x == 10. কোন test মান এই ভুল ধরবে, যা ২-মান BVA-র (10, 11) তালিকায় নেই?
- 10
- 9
- 11
- 100
একটি নিয়মে চারটি স্বাধীন Y/N শর্ত আছে। কোনো don't-care ব্যবহার না করলে পূর্ণ decision table-এ কয়টি rule (কলাম) থাকবে?
- ৪টি
- ৮টি
- ১২টি
- ১৬টি
State transition testing-এ “1-switch coverage” বলতে কী বোঝায়?
- পরপর দুটি transition-এর প্রতিটি বৈধ ক্রম অন্তত একবার চালানো
- প্রতিটি বৈধ transition অন্তত একবার চালানো
- প্রতিটি state অন্তত একবার পৌঁছানো
- শুধু অবৈধ transition-গুলো একবার করে চালানো
৪টি parameter, প্রতিটির ৩টি মান। প্রতিটি parameter-জোড়ার প্রতিটি মান-জোড়া অন্তত একবার ঢাকতে ন্যূনতম কয়টি test লাগবে, আর সব মিলন কয়টি?
- ৬টি; সব মিলন ৮১
- ৯টি; সব মিলন ৮১
- ১২টি; সব মিলন ৬৪
- ২৭টি; সব মিলন ২৭
একটি ফর্মে নম্বর আর roll দুটি ঘর। একজন tester একটি test-এ নম্বর 105 আর roll “abc” — দুটি অবৈধ মান একসাথে দিলেন। এতে প্রধান সমস্যা কী?
- Test-এর সংখ্যা অপ্রয়োজনে বেড়ে যায়
- অবৈধ মান দিয়ে test করাই উচিত নয়
- প্রথম ভুলে system থেমে গেলে দ্বিতীয় ঘরের যাচাই আছে কি না জানা যায় না
- এতে boundary value বাদ পড়ে যায়
শব্দকোষ
লেখার মধ্যে ডটেড দাগ দেওয়া শব্দে মাউস রাখুন বা চাপুন — অর্থ দেখাবে। এই পর্বের সব নতুন শব্দ এখানে একসাথে:
মনে রাখুন
- সব ইনপুট অসম্ভব, তাই কৌশল দিয়ে নমুনা: প্রতিটি test-এ পূর্বশর্ত, ইনপুট আর আগে লেখা প্রত্যাশিত ফল; oracle হলো requirement, কোড নয়।
- EP: valid আর invalid partition, প্রতিটি থেকে একটি মান; বৈধ মান মেলানো যায়, অবৈধ মান একবারে একটি। BVA: on point আর off point; ২-মান (a−1, a, b, b+1) আর ৩-মান (প্রতিটি সীমার দুই প্রতিবেশীসহ)।
- Decision table: n শর্তে 2n rule, একই ফলের rule don't-care দিয়ে মেশানো; প্রতিটি rule একটি test, আর don't-care-এর দাবি ঝুঁকি থাকলে যাচাই।
- State transition: state table-এ বৈধ আর অবৈধ সব ঘর; 0-switch = প্রতিটি transition, 1-switch = পরপর দুটির প্রতিটি জোড়া। Scenario: use case-এর প্রতিটি পথ। Pairwise: ৮১ থেকে ৯, কিন্তু মান বাছাই আর ৩-মিলনের ঝুঁকি মনে রেখে।
- অভিজ্ঞতা আর property: error guessing, checklist, charter-ভিত্তিক exploratory; property-based test এলোমেলো ইনপুটে নিয়ম যাচাই করে আর shrinking-এ ছোট উদাহরণ দেয়। কৌশল বাছাই আচরণের ধরন আর ঝুঁকি দেখে।
আরও পড়ুন · ABCL TECH-এ আপনার পরবর্তী পদক্ষেপ
- পর্ব ৪৮ · TDD আর BDD — red-green-refactor আর Given-When-Thenপরবর্তী পর্বনকশা করা test আগে লিখে কোডকে পথ দেখানো।
- সফটওয়্যার ইঞ্জিনিয়ারিং ও Git · কোর্স হোম৭০টি পর্বপুরো কোর্সের রোডম্যাপ।
- পর্ব ৪৫ · Testing-এর মৌলিক — verification-validation, test-এর স্তর আর black-box বনাম white-boxভিত্তিএই পর্বের কৌশলগুলো কোন স্তরে, কোন দৃষ্টিভঙ্গিতে।
- পর্ব ৫০ · Coverage আর complexity — statement, branch coverage, cyclomatic complexity আর mutation testingসম্পর্কিতকোডের ভেতর থেকে দেখা white-box coverage আর path testing.
- পর্ব ২২ · Use case আর user story — acceptance criteria, INVEST আর Given-When-Thenসম্পর্কিতScenario test-এর উৎস: main scenario, extension আর acceptance criteria.
- Python · pytest দিয়ে testingপাশের ট্র্যাকনকশা করা test Python-এ লেখা আর চালানো।
- JavaScript · Testingপাশের ট্র্যাকJavaScript-এ test লেখার হাতে-কলমে রূপ।