এই পর্বে যা শিখবেন
- Test কেন লেখা হয়, আর testing কোন জিনিস কখনোই প্রমাণ করতে পারে না — হিসাবসহ
- Verification আর validation-এর নিখুঁত পার্থক্য, আর static বনাম dynamic testing
- Test-এর চার স্তর, V-model-এ তাদের জায়গা, আর acceptance-এর রকমফের (UAT, alpha, beta, operational)
- Test-এর ধরন, black-box-white-box-grey-box, আর ISTQB-র সাতটি নীতি
- একটি test case-এর গঠন, একটি খুদে test runner, defect-এর জীবনচক্র, severity বনাম priority আর Agile দলে testing
১ · Test কেন — চারটি কাজ
মডিউল ৭-এ দেখেছেন কোড কীভাবে সাজাবেন, পরিষ্কার রাখবেন আর অন্যের চোখে পড়িয়ে নেবেন (পর্ব ৪৪ Code review). কিন্তু একটি প্রশ্ন বাকি: কোডটি যা করার কথা, তা-ই করছে — এটা জানবেন কীভাবে? এই মডিউলের উত্তর হলো software testing.
ঈদের আগে দর্জির কাছে পাঞ্জাবি বানাতে দিয়েছেন। দর্জি মাপ নিয়ে কাপড় কাটলেন, সেলাই করলেন। ডেলিভারির আগে আপনাকে একবার গায়ে দিয়ে দেখতে বলেন — হাতা ঠিক আছে কি না, কাঁধে টান লাগছে কি না। দুটি আলাদা জিনিস যাচাই হচ্ছে: দর্জি মাপের খাতা মেনে কেটেছেন কি না, আর মাপ মেনে বানানো পাঞ্জাবিটি আসলেই আপনার গায়ে আরাম দেয় কি না। Software testing-এও ঠিক এই দুই প্রশ্ন — আর দুটি প্রশ্নের নাম পাবেন অংশ ৩-এ।
একটি দল test লেখে চারটি কারণে:
ভুল খুঁজে বের করা
ব্যবহারকারীর আগে নিজেরা failure ঘটিয়ে দেখা, যাতে পেছনের fault খুঁজে ঠিক করা যায়।
আস্থা তৈরি
Release-এর সিদ্ধান্তে প্রমাণ: কোন কোন পরিস্থিতিতে software ঠিক চলেছে, আর কোথায় এখনো ঝুঁকি।
নির্ভয়ে বদলানো
Refactoring বা নতুন feature-এর পরে পুরোনো test আবার চালালে বোঝা যায় কিছু ভাঙল কি না (পর্ব ৪২)।
আচরণের নথি
একটি ভালো test বলে “এই ইনপুটে এই ফল হওয়ার কথা” — এমন নথি, যা পুরোনো হলে নিজেই লাল হয়ে জানান দেয়।
Fault, error আর failure-এর পার্থক্য পর্ব ৩-এ (Software quality) বিস্তারিত দেখেছেন। মনে করিয়ে দিই: কোডের ভেতরের ভুল শর্তটি fault (প্রচলিত কথায় bug বা defect); চলার সময় তার কারণে ভুল অবস্থা হলো error; আর বাইরে থেকে দেখা ভুল ফল বা crash হলো failure. সাবধান: ISTQB আর IEEE-র glossary-তে “error” মানে মানুষের ভুল কাজ (mistake), যা থেকে defect জন্মায়; ভুল অবস্থার অর্থটি dependability-র ধারার। Test সরাসরি fault দেখে না, failure দেখে — তারপর debugging করে fault খোঁজা হয়। এ কারণেই fault থাকা সত্ত্বেও test সবুজ থাকতে পারে, যদি কোনো test সেই fault-কে জাগিয়ে তোলার মতো ইনপুট না দেয়।
এই মডিউলে কী কী আছে
এই পর্বটি পুরো Testing মডিউলের মানচিত্র। এখানে ধারণা আর শব্দভান্ডার; প্রতিটি অংশের হাতে-কলমে রূপ পরের পর্বগুলোতে:
| পর্ব | বিষয় | এই পর্বের কোন ধারণা গভীরে যাবে |
|---|---|---|
| ৪৬ | Unit test | Unit স্তর, test case-এর গঠন, assertion |
| ৪৭ | Test case নকশা | Black-box কৌশল — exhaustive testing-এর বিকল্প |
| ৪৮ | TDD আর BDD | Early testing, shift-left, Given-When-Then |
| ৪৯ | Test double | Unit-কে বাকি system থেকে আলাদা করা |
| ৫০ | Coverage আর complexity | White-box মাপ, mutation testing |
| ৫১ | Test pyramid আর integration test | Integration ও end-to-end স্তর, flaky test |
২ · Testing কী পারে না — Dijkstra আর সব ইনপুটের হিসাব
Edsger W. Dijkstra-র নামে সবচেয়ে উদ্ধৃত বাক্যগুলোর একটি: “Program testing can be used to show the presence of bugs, but never to show their absence!” আমাদের জানামতে কথাটি ১৯৬৯ সালের রোমে NATO software engineering সম্মেলনের প্রতিবেদন আর তাঁর Notes on Structured Programming (১৯৬৯–৭০)-এ পাওয়া যায়; ১৯৭২ সালের Turing Award ভাষণ “The Humble Programmer”-এ প্রায় একই কথা: testing ভুল দেখাতে খুব কার্যকর, কিন্তু ভুল নেই তা দেখাতে “hopelessly inadequate”. হুবহু শব্দ উৎসভেদে একটু আলাদা।
কেন অনুপস্থিতি দেখানো যায় না? কারণ একটি test মানে একটি ইনপুটে একবার চালানো। “সব ইনপুটেই ঠিক” বলতে হলে সব ইনপুটে চালাতে হবে — একে বলে exhaustive testing. হিসাবটি দেখুন। ধরুন সেকেন্ডে ১০০ কোটি (109) test চালাতে পারেন — বাস্তবের বেশিরভাগ test suite-এর চেয়ে অনেক উদার অনুমান:
// Exhaustive testing calculator. ASSUMPTION: 1,000,000,000 tests per second
// (far faster than most real test suites), one machine, no setup cost.
const RATE = 1_000_000_000n;
const SEC_PER_YEAR = 365.25 * 24 * 3600;
const domains = [
["3 boolean flags", 2n ** 3n],
["one 8-bit byte", 2n ** 8n],
["one 32-bit int", 2n ** 32n],
["8-char id, a-z and 0-9", 36n ** 8n],
["two 32-bit ints", 2n ** 64n],
["16-char password, 95 symbols", 95n ** 16n],
];
function human(seconds) {
if (seconds < 1) return (seconds * 1e9).toFixed(0) + " ns";
if (seconds < 60) return seconds.toFixed(1) + " s";
if (seconds < 3600) return (seconds / 60).toFixed(1) + " min";
if (seconds < 86400) return (seconds / 3600).toFixed(1) + " h";
const y = seconds / SEC_PER_YEAR;
return y < 1e6 ? y.toFixed(1) + " years" : y.toExponential(2) + " years";
}
console.log("input domain".padEnd(30) + "cases".padStart(33) + " time");
for (const [name, cases] of domains) {
const seconds = Number(cases) / Number(RATE);
console.log(name.padEnd(30) + cases.toString().padStart(33) + " " + human(seconds));
}
const y64 = Number(2n ** 64n) / Number(RATE) / SEC_PER_YEAR;
console.log("\n2^64 cases = " + (2n ** 64n).toString());
console.log("at 1e9 tests/s: " + y64.toFixed(1) + " years");আউটপুট:
input domain cases time
3 boolean flags 8 8 ns
one 8-bit byte 256 256 ns
one 32-bit int 4294967296 4.3 s
8-char id, a-z and 0-9 2821109907456 47.0 min
two 32-bit ints 18446744073709551616 584.5 years
16-char password, 95 symbols 44012666865176569775543212890625 1.39e+15 years
2^64 cases = 18446744073709551616
at 1e9 tests/s: 584.5 years
ইনপুটের সংখ্যা হলো প্রতিটি ইনপুটের সম্ভাব্য মানের গুণফল, যোগফল নয়। একটি 32-bit integer-এর 232 ≈ ৪৩০ কোটি মান — ৪.৩ সেকেন্ডেই শেষ, এটা সম্ভব। কিন্তু দুটি 32-bit integer মানে 232 × 232 = 264 জোড়া: 264 / 109 ≈ ১.৮৪ × 1010 সেকেন্ড, বছরে ৩,১৫,৫৭,৬০০ সেকেন্ড ধরে ≈ ৫৮৪.৫ বছর. ১৬ অক্ষরের password-এর ঘরে 9516 — সংখ্যাটি মহাবিশ্বের বয়সের (প্রায় ১.৩৮ × 1010 বছর) চেয়েও বহু গুণ বড়। আর এ হিসাবে অবৈধ ইনপুট (অক্ষরের জায়গায় সংখ্যা, খালি ঘর), ইনপুটের ক্রম, সময় আর পরিবেশ (OS, browser, network) ধরাই হয়নি। BigInt (2n ** 64n) ব্যবহার করেছি, কারণ সাধারণ JavaScript number 253-এর ওপরে সব integer নিখুঁতভাবে রাখতে পারে না।
এবার একটি বাস্তবধর্মী function ধাপে ধাপে বড় করে দেখুন। একটি কাল্পনিক রেলের টিকিট অ্যাপের fare() ভাড়া হিসাব করে; প্রতিটি ধাপে একটি করে ইনপুট যোগ হচ্ছে, হার আগের মতোই সেকেন্ডে 109:
| ইনপুট | |
| হিসাব | |
| মোট case | |
| 10^9/s-এ সময় |
- ছোট ঘর। বয়সের ১২১টি মান, তিন শ্রেণি, ছাত্র হ্যাঁ বা না: ১২১ × ৩ × ২ = ৭২৬টি case. এমন ছোট, সসীম ইনপুটে সব case চালানো সত্যিই সম্ভব — exhaustive testing সবসময় অসম্ভব নয়।
- রুট যোগ। ৪০টি রুটে ৭২৬ × ৪০ = ২৯,০৪০. এখনো সহজ: সেকেন্ডে মাত্র ১০টি test চললেও ৪৮ মিনিটের মতো লাগবে।
- তারিখ যোগ। বছরের ৩৬৫ দিন: ২৯,০৪০ × ৩৬৫ = ১,০৫,৯৯,৬০০ (প্রায় ১ কোটি ৬ লাখ)। দ্রুত যন্ত্রে এখনো এক সেকেন্ডের ভগ্নাংশ, কিন্তু প্রতিটি case-এর প্রত্যাশিত ফল কে লিখবে? (অংশ ৭-এর test oracle.)
- একটি মুক্ত integer. যাত্রী কত টাকা দিলেন (ফেরত টাকার হিসাবের জন্য), 32-bit integer: ১,০৫,৯৯,৬০০ × 232 ≈ ৪.৫৫ × 1016; 109 দিয়ে ভাগ করলে প্রায় ৪.৫৫ কোটি সেকেন্ড ≈ ১.৪৪ বছর। একটি মাত্র সংখ্যার ঘর হিসাবকে সেকেন্ড থেকে বছরে নিয়ে গেল।
- আরেকটি integer. Coupon নম্বর আরেকটি 32-bit ঘর: ≈ ১.৯৬ × 1026 case, প্রায় ৬২০ কোটি বছর — পৃথিবীর বয়সের (প্রায় ৪৫০ কোটি বছর) চেয়েও বেশি। তাই বাস্তবে সব ইনপুট নয়, বুদ্ধি করে বাছাই করা প্রতিনিধি ইনপুট test করা হয় — equivalence partitioning আর boundary value, পর্ব ৪৭-এ (Test case নকশা)।
Testing হলো নমুনা নেওয়া (sampling). প্রতিটি test একটি প্রশ্ন: “এই ইনপুটে ঠিক আছে তো?” সব প্রশ্ন জিজ্ঞেস করা অসম্ভব বলে testing-এর আসল দক্ষতা হলো কোন প্রশ্নগুলো জিজ্ঞেস করবেন তা বাছা — যেখানে ভুল থাকার সম্ভাবনা বেশি, আর ভুল হলে ক্ষতি বড়। এজন্য ISTQB-র ভাষায় testing ঝুঁকি-ভিত্তিক (risk-based). “সব ঠিক আছে” প্রমাণ চাইলে লাগে গাণিতিক প্রমাণ — formal verification (প্র ০১)।
৩ · Verification বনাম validation — আর static বনাম dynamic testing
পর্ব ৩-এ Barry Boehm-এর দুই প্রশ্ন সংক্ষেপে দেখেছেন। এবার নিখুঁতভাবে। Boehm-এর লেখায় (যেমন IEEE Software-এ ১৯৮৪ সালের তাঁর প্রবন্ধ “Verifying and Validating Software Requirements and Design Specifications”) জনপ্রিয় হওয়া রূপটি:
| Verification | Validation | |
|---|---|---|
| প্রশ্ন | “Are we building the product right?” — জিনিসটি ঠিকভাবে বানাচ্ছি? | “Are we building the right product?” — ঠিক জিনিসটি বানাচ্ছি? |
| কিসের সাথে মেলানো হয় | লিখিত specification, নকশা, মান (standard) — আগের ধাপের ফল | ব্যবহারকারী ও stakeholder-এর আসল প্রয়োজন আর ব্যবহারের পরিবেশ |
| ব্যর্থ হলে মানে | spec মানা হয়নি — বানানোয় ভুল | spec মানা হয়েছে, কিন্তু spec-ই ভুল বা অসম্পূর্ণ |
| উদাহরণ | code review, static analysis, unit ও integration test, spec-এর সাথে system test | prototype দেখানো, acceptance test, beta test, ব্যবহারকারীকে বসিয়ে usability test |
| দর্জির উদাহরণে | মাপের খাতা মেনে কাটা হয়েছে কি না | পাঞ্জাবিটি গায়ে আরাম দেয় কি না |
আনুষ্ঠানিক সংজ্ঞাও উৎসভেদে একটু আলাদা। IEEE-র পুরোনো glossary (IEEE 610.12-1990)-তে verification মোটামুটি “একটি development ধাপের ফল সেই ধাপের শুরুতে বেঁধে দেওয়া শর্ত মানে কি না”, আর validation “development-এর সময়ে বা শেষে system নির্দিষ্ট requirement মানে কি না” যাচাই। পরের অনেক মানে (যেমন ISO 9000-ধারার সংজ্ঞা আর IEEE 1012) validation-এর জোর পড়ে উদ্দিষ্ট ব্যবহার আর ব্যবহারকারীর প্রয়োজনের ওপর। মনে রাখার কৌশল: verification তাকায় পেছনে, আগের নথির দিকে; validation তাকায় বাইরে, মানুষের দিকে।
একটি সাধারণ ভুল ধারণা: “unit test = verification, acceptance test = validation”, ব্যস। মোটা দাগে ঠিক, কিন্তু কাজটি কী প্রশ্নের উত্তর দিচ্ছে, সেটিই আসল। একটি acceptance test যদি শুধু লিখিত acceptance criteria-র সাথে মেলায়, সেটি আসলে verification-ও; আর requirement নিজেই ঠিক আছে কি না যাচাই — requirement validation, পর্ব ২৪-এ (Requirement management) — কোনো কোড ছাড়াই validation. এক কাজে দুটোই থাকতে পারে।
Static testing বনাম dynamic testing
কোড না চালিয়েও ভুল খোঁজা যায়। Static testing মানে কাজের ফল (requirement, নকশা, কোড, test নিজেই) পড়ে বা বিশ্লেষণ করে যাচাই: review, walkthrough, inspection, আর static analysis tool — পর্ব ৪৪-এ দেখেছেন। Dynamic testing মানে software চালিয়ে আচরণ দেখা — এই মডিউলের বাকি সব।
Static — না চালিয়ে
সরাসরি fault দেখে (“এই শর্তে <= হবে”)। কোড লেখার আগেই requirement-এর অস্পষ্টতা ধরা যায়। কিন্তু চলার সময়ের আচরণ — গতি, memory, অন্য system-এর সাথে কথা — দেখে না।
Dynamic — চালিয়ে
Failure দেখে, তারপর fault খুঁজতে হয়। আসল পরিবেশের সমস্যা (ধীর database, ভুল config) ধরে। কিন্তু চালানো যায় এমন কিছু লাগে, আর শুধু বাছাই করা ইনপুটের কথা বলে।
৪ · Test-এর স্তর — unit, integration, system, acceptance
বাড়ি বানানোর কথা ভাবুন। প্রথমে প্রতিটি ইট আর রডের মান দেখা হয়; তারপর দেখা হয় কলাম আর বিম ঠিকমতো জোড়া লেগেছে কি না; বাড়ি শেষ হলে পানি, বিদ্যুৎ, গ্যাস — পুরো বাড়ি একসাথে কাজ করে কি না; আর শেষে মালিক ঘুরে দেখে চাবি বুঝে নেন। Software-এর test level-ও এমন চারটি:
| স্তর | কী test হয় | কী ধরনের ভুল ধরে | সাধারণত কে, কিসের সাথে মিলিয়ে |
|---|---|---|---|
| Unit (component) | একটি function, class বা module, বাকি system থেকে আলাদা করে | যুক্তির ভুল, সীমার ভুল, ভুল হিসাব | Developer; module-এর নকশা ও contract |
| Integration | কয়েকটি module, বা module আর database, API, অন্য service-এর জোড় | Interface-এর অমিল, ভুল data format, একক, ক্রম, timeout | Developer বা tester; architecture ও interface-এর নকশা |
| System | পুরো জোড়া-লাগানো system, যতটা সম্ভব আসলের মতো পরিবেশে | End-to-end প্রবাহের ভুল, non-functional ঘাটতি (গতি, নিরাপত্তা) | প্রায়ই আলাদা test দল; SRS (পর্ব ২১) |
| Acceptance | System কি ব্যবহার আর গ্রহণের উপযুক্ত | ভুল বোঝা প্রয়োজন, ব্যবহারে অসুবিধা, ব্যবসার নিয়মের ফাঁক | গ্রাহক, ব্যবহারকারী, operations; ব্যবসার প্রয়োজন ও acceptance criteria |
“Unit” কত বড় — একটি function, না একটি class, না কয়েকটি class মিলে একটি আচরণ — তা নিয়ে বিভিন্ন ঘরানায় মতভেদ আছে; পর্ব ৪৬-এ (Unit test) দেখবেন। Integration-এর বিস্তারিত (contract test, end-to-end) পর্ব ৫১-এ।
Acceptance-এর রকমফের
- User acceptance testing (UAT): আসল ব্যবহারকারী বা তাঁদের প্রতিনিধি নিজের কাজের পরিস্থিতিতে দেখেন — “এ দিয়ে আমার কাজ চলবে?”
- Alpha testing: প্রকাশের আগে, নির্মাতার নিজের জায়গায় বা নিয়ন্ত্রিত পরিবেশে, ভেতরের মানুষ বা বাছাই করা ব্যবহারকারী দিয়ে।
- Beta testing: সীমিত সংখ্যক বাইরের ব্যবহারকারী তাঁদের নিজের পরিবেশে (নিজের ফোন, নিজের network) ব্যবহার করে মতামত দেন।
- Operational acceptance testing (OAT): চালানোর দল দেখে system চালানো যায় কি না — install, backup ও restore, monitoring, বিপর্যয়ের পরে চালু করা, নিরাপত্তার নিয়ম।
- এ ছাড়া চুক্তি বা আইনের শর্ত মানা হয়েছে কি না, তার contractual ও regulatory acceptance — যেমন ব্যাংক বা চিকিৎসার software-এ।
V-model-এ স্তরগুলোর জায়গা
পর্ব ৪-এ (SDLC আর process model) V-model দেখেছেন: বাঁ দিকের প্রতিটি নকশার ধাপের পাশে ডান দিকে একটি test স্তর। ছবিতে প্রতিটি জোড় দেখায় কোন স্তর কোন নথির সাথে মিলিয়ে দেখা হয়:
V-model-এ test-এর স্তর
Each test level checks the work product on the opposite arm of the V
এবার একটি requirement ধরে দেখুন, প্রতিটি স্তরে তার কোন দিকটি যাচাই হয়। কাল্পনিক অনলাইন বাজার অ্যাপের নিয়ম: “সদস্যরা ১,০০০ টাকা বা তার বেশি কেনাকাটায় ১০% ছাড় পাবেন।”
- Requirement
REQ-12ছাড়ের নিয়ম - Unit
discount()হিসাব - Integration
cart + member DBজোড় - System
checkoutপুরো প্রবাহ - Acceptance
UATগ্রহণযোগ্য?
Members get 10% off when cart total >= 1000 taka. AC1: member, 1000 taka -> pays 900 AC2: member, 999 taka -> pays 999 AC3: non-member, 5000 -> pays 5000
discount(1000, member=True) == 100 discount(999, member=True) == 0 discount(1500, member=False) == 0 discount(-5, True) -> raises error
user 42 is a member in the test DB
cart service asks member service -> true
cart total 1200 -> discount line 120
member service down -> checkout still works,
no discount, warning logged
log in as member -> add items worth 1000 -> checkout page shows 900, invoice PDF 900 -> payment gateway (sandbox) charged 900 also: 500 checkouts/min, page < 2 s
owner: "discount must show BEFORE payment,
not only on the invoice"
member: "I could not see why I got no
discount on 990 taka"
-> new requirement: show "10 taka more for 10% off"
- Requirement আর acceptance criteria. নিয়মটি লেখা হয়েছে, সাথে তিনটি মাপা যায় এমন উদাহরণ (পর্ব ২২)। এখান থেকেই প্রতিটি স্তরের test-এর ভিত্তি (test basis) আসে।
- Unit. শুধু
discount()function, database বা UI ছাড়া। সীমার মান ৯৯৯ আর ১,০০০ আলাদা করে দেখা হচ্ছে, কারণ>আর>=-এর ভুল ঠিক এখানেই লুকায়। মিলিয়ে দেখা হচ্ছে module-এর নকশার সাথে — verification. - Integration. Cart service আর সদস্যদের service একসাথে: “সদস্য কি না” প্রশ্নের উত্তর ঠিক আসে কি না, আর ওই service বন্ধ থাকলে checkout আটকে যায় কি না। Interface-এর ভুল এখানে ধরা পড়ে।
- System. Browser থেকে login করে কেনা, invoice আর payment gateway-র sandbox পর্যন্ত — প্রতিটি জায়গায় একই ৯০০ টাকা। সাথে non-functional দিক: ভিড়ের সময়ে গতি। মিলিয়ে দেখা হচ্ছে SRS-এর সাথে।
- Acceptance. সব আগের test সবুজ, তবু মালিক আর সদস্যরা এমন কিছু বললেন যা spec-এ ছিল না: ছাড় payment-এর আগে দেখাতে হবে, আর ৯৯০ টাকায় কেন ছাড় নেই, তা বোঝা যায় না। এটিই validation — spec মানা হয়েছে, কিন্তু প্রয়োজন পুরো মেটেনি। ফল: নতুন requirement.
৫ · Test-এর ধরন — কী যাচাই করছেন
স্তর বলে কতটুকু জোড়া লাগিয়ে দেখছেন; test type বলে কোন গুণ দেখছেন। যেকোনো ধরন যেকোনো স্তরে হতে পারে — performance test একটি function-এও হয়, পুরো system-এও।
| ধরন | প্রশ্ন | উদাহরণ (কাল্পনিক বাজার অ্যাপ) |
|---|---|---|
| Functional | System কী করে — নিয়ম মেনে ঠিক ফল দেয়? | সদস্যের ১,০০০ টাকায় ১০০ টাকা ছাড় |
| Non-functional · performance | কত দ্রুত, কত ভিড় সামলায়? | মিনিটে ৫০০ checkout-এ পাতা ২ সেকেন্ডের মধ্যে; load ও stress test |
| Non-functional · security | অনুমতি ছাড়া কেউ কিছু করতে বা দেখতে পারে? | অন্যের order নম্বর বসিয়ে তাঁর ঠিকানা দেখা যায় কি না |
| Non-functional · usability | মানুষ সহজে কাজ সারতে পারেন? | নতুন ব্যবহারকারী সাহায্য ছাড়া ৩ মিনিটে প্রথম order দিতে পারেন কি না |
| Non-functional · compatibility | নানা পরিবেশে চলে, অন্য system-এর সাথে মেলে? | পুরোনো Android ফোন, ছোট পর্দা, ধীর network |
| Regression | নতুন বদলে পুরোনো কিছু ভাঙল? | Coupon feature যোগের পরে আগের সব checkout test আবার |
| Confirmation (re-testing) | যে defect ঠিক করা হলো, সেটি সত্যিই ঠিক হলো? | B-103 ঠিক করার পরে ঠিক সেই ব্যর্থ test-টি আবার |
| Smoke | নতুন build-এর প্রধান কাজগুলো আদৌ চলে — আরও test করার যোগ্য? | অ্যাপ খোলে, login হয়, একটি পণ্য cart-এ যায়, checkout পাতা খোলে |
| Sanity | ছোট একটি বদলের পরে সেই এলাকাটি যুক্তিসংগত আচরণ করছে? | ছাড়ের হিসাবে ছোট সংশোধনের পরে শুধু ছাড়ের কয়েকটি case দ্রুত দেখা |
সংজ্ঞা সব জায়গায় এক নয়। Smoke আর sanity নিয়ে বই, প্রতিষ্ঠান আর দলভেদে মতভেদ সবচেয়ে বেশি: অনেকে বলেন smoke চওড়া কিন্তু অগভীর (পুরো build), sanity সরু কিন্তু একটু গভীর (একটি বদলানো এলাকা); কেউ দুটিকে সমার্থক ধরেন, কেউ উল্টোও লেখেন। “Smoke test” নামটি hardware থেকে এসেছে বলে প্রচলিত — নতুন যন্ত্রে বিদ্যুৎ দিয়ে দেখা ধোঁয়া বের হয় কি না। পরীক্ষায় বা দলে কাজের সময় কোন সংজ্ঞা ধরছেন, এক লাইনে বলে নিন। আরেকটি পার্থক্য পাকা: confirmation দেখে ঠিক করা ভুলটি সত্যিই গেছে কি না; regression দেখে সেই সংশোধন অন্য কিছু ভাঙল কি না।
Non-functional-এর তালিকা আসে quality-র মডেল থেকে — ISO/IEC 25010-এর characteristic, পর্ব ৩ আর পর্ব ২১ (Functional আর non-functional requirement). Requirement মাপা যায় এমন করে লেখা থাকলে (“২ সেকেন্ডের মধ্যে”), তবেই non-functional test-এর পাস-ফেল ঠিক করা যায়।
৬ · Black-box, white-box আর grey-box
তৃতীয় প্রশ্ন: test বানানোর সময় আপনি ভেতরের কতটা জানেন বা ব্যবহার করছেন?
ভেতরের জ্ঞানের তিন মাত্রা
Black-box (spec only), white-box (code structure), grey-box (partial internal knowledge, external execution)
⬛ Black-box (specification-based)
Spec থেকে test: “১,০০০ টাকার সীমার দুই পাশে দেখি।” কোড বদলালেও test টেকে। কিন্তু কোডে এমন শাখা থাকতে পারে যা spec-এ নেই — সেটি অদেখা থাকে। কৌশল পর্ব ৪৭-এ।
⬜ White-box (structure-based)
কোড দেখে test: “এই if-এর দুই শাখাই চালাই।” Coverage মাপা যায় (পর্ব ৫০)। কিন্তু কোডে যা নেই — বাদ পড়া requirement — তা কখনো ধরে না।
◧ Grey-box
যেমন: database-এর গঠন জেনে API দিয়ে এমন order পাঠানো, যা একটি নির্দিষ্ট constraint ছুঁয়ে যায়; তারপর DB-তে ফল দেখা। Integration আর security test-এ খুব প্রচলিত।
একটি সূক্ষ্ম কথা: black-box আর white-box হলো test বানানোর পদ্ধতি, স্তর নয়। Unit test সাধারণত white-box ভেবে লেখা হয়, তবে black-box-ও হতে পারে; আর system test সাধারণত black-box, তবে coverage মেপে white-box চোখেও দেখা যায়। ISTQB-র নতুন syllabus-এ নামগুলো “black-box test technique” আর “white-box test technique”, পাশে অভিজ্ঞতা-ভিত্তিক (experience-based) কৌশল, যেমন exploratory testing.
৭ · Testing-এর সাতটি নীতি — আর test oracle-এর সমস্যা
ISTQB (International Software Testing Qualifications Board)-এর Foundation Level syllabus সাতটি সাধারণ নীতির তালিকা দেয়, যা চাকরির পরীক্ষা আর ইন্টারভিউতে খুব চেনা। নিচে লেখার সময়ের সর্বশেষ সংস্করণ (CTFL v4.0, ২০২৩)-এর ক্রম আর ভাষা, আমাদের জানামতে; আগের সংস্করণ (২০১৮)-এর নাম বন্ধনীতে:
- Testing ত্রুটির উপস্থিতি দেখায়, অনুপস্থিতি নয়। Dijkstra-র কথাই — test সবুজ মানে “এই case-গুলোতে ভুল পাইনি”।
- Exhaustive testing অসম্ভব। অংশ ২-এর হিসাব; তাই ঝুঁকি আর অগ্রাধিকার ধরে বাছাই।
- আগে testing সময় আর টাকা বাঁচায় (early testing). Requirement-এর review-এ ধরা ভুল কোড লেখার আগেই মেটে।
- Defect জোট বাঁধে (defect clustering). অল্প কিছু module-এ বেশিরভাগ defect — Pareto-র ধাঁচ। নিচে simulation দেখুন।
- Test-এর ধার কমে যায় (tests wear out; আগে নাম ছিল pesticide paradox). একই test বারবার চালালে একসময় নতুন ভুল আর ধরে না — যেমন একই কীটনাশক বারবার দিলে পোকা সয়ে যায়। “Pesticide paradox” নামটি Boris Beizer-এর Software Testing Techniques (দ্বিতীয় সংস্করণ, ১৯৯০) থেকে প্রচলিত। প্রতিকার: test হালনাগাদ করা, নতুন test, নতুন কৌশল। তবে regression-এর জন্য পুরোনো test চালিয়ে যাওয়া তবুও দরকার।
- Testing প্রসঙ্গের ওপর নির্ভর করে. হাসপাতালের ওষুধের মাত্রার software আর একটি রান্নার রেসিপির অ্যাপ একইভাবে test হয় না।
- “ত্রুটি নেই” মানেই সফল নয় (absence-of-defects fallacy; আগে “absence-of-errors fallacy”). সব test সবুজ, কিন্তু ব্যবহারকারীর যা দরকার তা না বানালে লাভ নেই — অংশ ৪-এর acceptance ধাপ।
এগুলো “নীতি” — গাণিতিক সূত্র নয়, অভিজ্ঞতা থেকে আসা সাধারণীকরণ। এবার চতুর্থ নীতিটি একটি simulation-এ দেখুন। Data বানানো, আসল কোনো project-এর নয়: একটি কাল্পনিক অনলাইন বাজার অ্যাপের ২০টি module-এ ৪০০টি defect ছড়ানো হয়েছে, প্রতিটি module-এর “ভুলপ্রবণতা” এসেছে একটি দীর্ঘ-লেজের (heavy-tailed) বণ্টন থেকে, স্থির seed দিয়ে:
import random
# Simulated (NOT real) data: 400 defects spread over 20 modules of a
# fictional online-market app. Each module gets a "proneness" weight from a
# heavy-tailed distribution; a fixed seed makes every run identical.
rng = random.Random(45)
modules = [f"M{i:02d}" for i in range(1, 21)]
weight = {m: rng.paretovariate(0.8) for m in modules}
total_w = sum(weight.values())
count = dict.fromkeys(modules, 0)
for _ in range(400): # place each defect by weight
u, acc = rng.random() * total_w, 0.0
for m in modules:
acc += weight[m]
if u < acc:
count[m] += 1
break
else:
count[modules[-1]] += 1
ranked = sorted(modules, key=lambda m: -count[m])
running = 0
for i, m in enumerate(ranked, 1):
running += count[m]
if i <= 6 or i == 20:
bar = "#" * (count[m] // 4)
print(f"{i:2} {m} {count[m]:4} cum {running / 400:6.1%} {bar}")
elif i == 7:
print(" ...")
top = len(modules) // 5 # top 20% = 4 modules
share = sum(count[m] for m in ranked[:top]) / 400
print(f"\ntop {top} of 20 modules (20%) hold {share:.1%} of defects")
print("modules with 0 defects:", sum(1 for m in modules if count[m] == 0))আউটপুট:
1 M17 74 cum 18.5% ##################
2 M14 47 cum 30.2% ###########
3 M18 45 cum 41.5% ###########
4 M15 44 cum 52.5% ###########
5 M12 31 cum 60.2% #######
6 M10 26 cum 66.8% ######
...
20 M03 6 cum 100.0% #
top 4 of 20 modules (20%) hold 52.5% of defects
modules with 0 defects: 0
সবচেয়ে বেশি defect-ওয়ালা ৪টি module (মোটের ২০%)-এ ৪০০-এর ২১০টি, অর্থাৎ ৫২.৫%; defect সমানভাবে ছড়ালে থাকত ২০%. অর্থাৎ আড়াই গুণেরও বেশি জমাট — কিন্তু “৮০/২০” নয়। Pareto principle-এর ৮০/২০ একটি মনে রাখার সহজ ছবি, প্রকৃতির নিয়ম নয়; আসল অনুপাত project-ভেদে আলাদা। প্রকাশিত গবেষণাতেও (যেমন Fenton আর Ohlsson-এর ২০০০ সালের একটি বড় telecom system-এর বিশ্লেষণ, IEEE TSE) অল্প কিছু module-এ বেশিরভাগ fault পাওয়ার কথা আছে, তবে সংখ্যা পরিবেশভেদে খুব আলাদা। কাজের শিক্ষা: যে module-এ আগে বেশি defect পাওয়া গেছে, সেখানে বেশি test আর বেশি review দিন — কিন্তু বাকিগুলো একেবারে বাদ দেবেন না। random.Random(45) একটি স্থির seed-এর PRNG, তাই প্রতিবার একই ফল; seed বদলালে অনুপাত বদলাবে (৪০% থেকে ৬৫%-এর মতো, আমাদের কয়েকটি চালানোয়)।
Test oracle-এর সমস্যা
একটি test-এ দুটি জিনিস লাগে: ইনপুট, আর প্রত্যাশিত ফল। প্রত্যাশিত ফল কোথা থেকে আসে, তার উৎসকে বলে test oracle — spec, হাতে করা হিসাব, পুরোনো সংস্করণ, অন্য একটি স্বাধীন implementation, বা একজন বিশেষজ্ঞ। Oracle problem হলো: অনেক ক্ষেত্রে সঠিক ফল জানাই কঠিন। আবহাওয়ার পূর্বাভাসের model, machine learning-এর ফল, একটি জটিল বিমার প্রিমিয়াম — “ঠিক উত্তর কত হওয়া উচিত” কে বলবে? ধারণাটি ১৯৭০-এর দশকের শেষের testing-সাহিত্যে (যেমন William Howden-এর কাজে) আসে বলে সাধারণত ধরা হয়, আর Elaine Weyuker-এর ১৯৮২ সালের প্রবন্ধ “On Testing Non-Testable Programs” এর ক্লাসিক আলোচনা।
পুরো উত্তর না জানলেও আংশিক oracle কাজে লাগে: বৈশিষ্ট্য যাচাই (ছাড় কখনো মোটের চেয়ে বেশি হবে না, ফল কখনো ঋণাত্মক নয়), সম্পর্ক যাচাই (মোট দ্বিগুণ হলে ছাড়ও দ্বিগুণ — metamorphic testing), বা crash না হওয়া। আর একটি বিপদ মনে রাখুন: কোড থেকেই প্রত্যাশিত ফল কপি করলে test শুধু বলে “কোড যা করে, তা-ই করে” — ভুলটিও পাস করে যায়। AI দিয়ে test লেখালে এটি খুব বেশি হয় (অংশ ১০)।
৮ · Test case, test runner আর defect-এর জীবনচক্র
একটি test case পরিষ্কার লেখা থাকলে যে কেউ চালিয়ে একই সিদ্ধান্তে পৌঁছাবেন — রান্নার রেসিপির খাতার মতো: উপকরণ, আগে কী তৈরি রাখবেন, ধাপ, আর শেষে কেমন হওয়ার কথা। আনুষ্ঠানিক test নথির মান হলো ISO/IEC/IEEE 29119-3 (আগে IEEE 829); প্রচলিত ঘরগুলো:
| ঘর | মানে | উদাহরণ |
|---|---|---|
| ID ও শিরোনাম | অনন্য নম্বর, এক লাইনে উদ্দেশ্য | TC-DISC-04 · সদস্য, ঠিক ১,০০০ টাকা |
| Requirement | কোন requirement যাচাই করছে (traceability, পর্ব ২৪) | REQ-12, AC1 |
| Precondition | শুরুর আগে কী সত্য থাকতে হবে | ব্যবহারকারী ৪২ সদস্য; cart খালি; ছাড়ের নিয়ম চালু |
| Steps | কী কী করবেন, ক্রমে | login → ১,০০০ টাকার পণ্য cart-এ → checkout খোলা |
| Test data | কোন নির্দিষ্ট মান | পণ্য P-7, দাম ৫০০ টাকা, পরিমাণ ২ |
| Expected result | কী হওয়ার কথা — oracle থেকে, কোড থেকে নয় | ছাড় ১০০ টাকা, প্রদেয় ৯০০ টাকা |
| Actual result ও অবস্থা | চালানোর পরে যা হলো; pass / fail / blocked | ছাড় ০ টাকা — FAIL, defect B-110 |
| Postcondition | শেষে কী পরিষ্কার করবেন | test order মুছে ফেলা |
Automated test-এ এই ঘরগুলো কোডের ভেতরে থাকে: test-এর নাম = শিরোনাম, setup = precondition, function call = steps ও data, assertion = expected result. একটি test runner সব test খুঁজে চালায় আর ফল গুনে দেখায়। ভেতরে জিনিসটি কত সরল, তা দেখতে ২০ লাইনের একটি runner বানিয়ে ফেলি — describe, it আর expect নাম Jest-ধাঁচের framework থেকে নেওয়া। পরীক্ষাধীন কোড: অংশ ৪-এর ছাড়ের নিয়ম।
// A tiny test runner (toy). Real ones: Jest, Vitest, Mocha (JS); pytest, unittest (Python).
const results = [];
function describe(name, fn) { console.log(name); fn(); }
function it(name, fn) {
try { fn(); results.push(true); console.log(" PASS " + name); }
catch (e) { results.push(false); console.log(" FAIL " + name + "\n " + e.message); }
}
function expect(actual) {
return {
toBe(expected) {
if (actual !== expected) throw new Error(`expected ${expected}, got ${actual}`);
},
toThrow() {
try { actual(); } catch (e) { return; }
throw new Error("expected an error, none was thrown");
},
};
}
// Code under test (fictional rule): members get 10% off when total >= 1000 taka.
function discount(total, isMember) {
if (total < 0) throw new RangeError("negative total");
if (isMember && total > 1000) return total * 0.10; // the rule says >=
return 0;
}
describe("discount()", () => {
it("member, 1500 taka -> 150", () => expect(discount(1500, true)).toBe(150));
it("non-member gets nothing", () => expect(discount(1500, false)).toBe(0));
it("member, 999 taka -> 0", () => expect(discount(999, true)).toBe(0));
it("member, exactly 1000 -> 100", () => expect(discount(1000, true)).toBe(100));
it("negative total throws", () => expect(() => discount(-5, true)).toThrow());
});
const passed = results.filter(Boolean).length;
console.log(`\n${passed} passed, ${results.length - passed} failed, ${results.length} total`);আউটপুট:
discount()
PASS member, 1500 taka -> 150
PASS non-member gets nothing
PASS member, 999 taka -> 0
FAIL member, exactly 1000 -> 100
expected 100, got 0
PASS negative total throws
4 passed, 1 failed, 5 total
পাঁচটির চারটি পাস, একটি ফেল — আর ফেলটিই সবচেয়ে মূল্যবান। কোডে total > 1000, অথচ নিয়ম “১,০০০ বা তার বেশি”: ঠিক ১,০০০ টাকায় ছাড় ০. ১,৫০০ আর ৯৯৯-এর test এই fault-কে জাগায় না; শুধু সীমার ঠিক ওপরের মানটি জাগায় — সীমার মান কেন আলাদা করে দেখতে হয়, পর্ব ৪৭-এ। ভেতরের কৌশলটি সরল: it প্রতিটি test একটি try/catch-এ চালায়; expect(...).toBe(...) মিল না পেলে exception ছোড়ে; runner সেটি ধরে FAIL লেখে আর পরের test-এ যায় — একটি test ভাঙলেও বাকিগুলো চলে। আসল framework (JavaScript-এ Jest, Vitest, Mocha; Python-এ standard library-র unittest আর জনপ্রিয় pytest) এর ওপরে যোগ করে test ফাইল খুঁজে বের করা, setup/teardown, test আলাদা রাখা, async, অনেক রকম matcher, সুন্দর diff আর CI-র জন্য রিপোর্ট; কোনটি প্রচলিত, তা সময়ের সাথে বদলায়। বিস্তারিত পর্ব ৪৬-এ।
Defect-এর জীবনচক্র
Test ফেল করলে কাজ শুরু হয়: defect-টি লেখা, কে ঠিক করবেন ঠিক করা, সংশোধন, আবার যাচাই। এই পথের নাম defect lifecycle. অবস্থার নাম tool আর দলভেদে আলাদা (Bugzilla, Jira বা GitHub Issues-এর মতো issue tracker-এ নিজের মতো সাজানো যায়), তবে মূল ধাপগুলো প্রায় একই। অংশ ৮-এর ফেল হওয়া test থেকে একটি defect-এর যাত্রা:
| অবস্থা | |
| কার হাতে | |
| severity · priority | |
| কী হলো |
- New. Tester রিনা ফেল হওয়া TC-DISC-04 থেকে defect লিখলেন: শিরোনাম, পুনরুৎপাদনের ধাপ, test data, প্রত্যাশিত ফল (প্রদেয় ৯০০) আর আসল ফল (প্রদেয় ১,০০০), পরিবেশ আর build নম্বর। নিজের অনুমানে severity: major — টাকার হিসাব ভুল।
- Triaged → Assigned. Triage সভায় দেখা হলো এটি আগের কোনো defect-এর duplicate কি না, আর পুনরুৎপাদন হয় কি না। হয়। Priority P2 ধরা হলো, দায়িত্ব পেলেন developer করিম। (পুনরুৎপাদন না হলে বা ইচ্ছাকৃত আচরণ হলে “Rejected” বা “Won't fix”-ও হতে পারত।)
- In progress → Fixed. করিম
>বদলে>=করলেন, সাথে একটি unit test; PR review পার হলো (পর্ব ৪৪)। Developer-এর “Fixed” মানে এখনো “যাচাই হয়েছে” নয়। - Ready for retest → Reopened. রিনা confirmation test চালালেন: ১,০০০ টাকায় এবার ঠিক। কিন্তু শুধু invoice-এ: checkout পাতায় এখনো প্রদেয় ১,০০০ দেখাচ্ছে, কারণ পাতার JavaScript-এ নিয়মটির আলাদা একটি কপি ছিল, তাতে সেই পুরোনো
>. Defect আবার খুলল। এর মধ্যে ব্যবসার দল জানাল শুক্রবার বড় sale, তাই priority উঠল P1-এ — severity একই রইল। - Fixed (আবার). করিম নিয়মের দ্বিতীয় কপিটি মুছে দিলেন: পাতা এখন server-এর হিসাব করা ছাড়ই দেখায়। একই নিয়ম দুই জায়গায় থাকলে একই ভুল দুবার সারাতে হয় — DRY-এর শিক্ষা (পর্ব ২৬)।
- Verified. Confirmation test পাস; সাথে পুরো regression suite চালানো হলো — সংশোধন অন্য কিছু ভাঙেনি।
- Closed. সীমার দুটি case স্থায়ী regression test হয়ে suite-এ যোগ হলো, যাতে ভুলটি ফিরে এলে সাথে সাথে ধরা পড়ে। Release note-এ এক লাইন।
Severity বনাম priority
দুটি আলাদা মাপ, আর সাধারণত আলাদা মানুষ ঠিক করেন। Severity হলো defect-এর প্রভাব system আর ব্যবহারকারীর ওপর কত বড় (critical, major, minor, trivial-এর মতো ধাপ) — প্রায়ই tester প্রস্তাব করেন। Priority হলো কত তাড়াতাড়ি ঠিক করতে হবে (P1, P2…) — প্রায়ই product owner বা manager ঠিক করেন, ব্যবসা, সময়সীমা আর কতজন ব্যবহারকারী আক্রান্ত তা দেখে। ধাপের নাম আর সংখ্যা প্রতিষ্ঠানভেদে আলাদা।
একটি কাল্পনিক পরীক্ষার ফলাফল পোর্টালের defect-তালিকা নিচে priority, তারপর severity ধরে সাজানো হলো:
from dataclasses import dataclass
# Fictional bug list of an exam-result portal. Severity = how bad the effect is
# on the system; priority = how soon the business needs the fix.
SEV = {"critical": 1, "major": 2, "minor": 3, "trivial": 4}
PRI = {"P1": 1, "P2": 2, "P3": 3, "P4": 4}
@dataclass
class Bug:
id: str
title: str
severity: str
priority: str
bugs = [
Bug("B-101", "crash when exporting 1990s archive", "critical", "P3"),
Bug("B-102", "university name misspelt on home page", "trivial", "P1"),
Bug("B-103", "GPA rounds 3.995 down to 3.99", "major", "P1"),
Bug("B-104", "login fails for every student", "critical", "P1"),
Bug("B-105", "tooltip text overlaps on old phones", "minor", "P4"),
Bug("B-106", "PDF marksheet missing page numbers", "minor", "P3"),
Bug("B-107", "slow search (8 s) on result day", "major", "P2"),
]
queue = sorted(bugs, key=lambda b: (PRI[b.priority], SEV[b.severity], b.id))
print(f"{'#':<3}{'id':<7}{'pri':<5}{'severity':<10}title")
for i, b in enumerate(queue, 1):
print(f"{i:<3}{b.id:<7}{b.priority:<5}{b.severity:<10}{b.title}")
odd = [b.id for b in bugs if abs(PRI[b.priority] - SEV[b.severity]) >= 2]
print("\nseverity and priority far apart:", ", ".join(odd))আউটপুট:
# id pri severity title
1 B-104 P1 critical login fails for every student
2 B-103 P1 major GPA rounds 3.995 down to 3.99
3 B-102 P1 trivial university name misspelt on home page
4 B-107 P2 major slow search (8 s) on result day
5 B-101 P3 critical crash when exporting 1990s archive
6 B-106 P3 minor PDF marksheet missing page numbers
7 B-105 P4 minor tooltip text overlaps on old phones
severity and priority far apart: B-101, B-102
Python-এর sorted একটি tuple-কে key হিসেবে নিয়ে আগে priority, সমান হলে severity, তারপর id দিয়ে সাজায়। শেষ লাইনের দুটি defect-ই শেখার জিনিস। B-101 — উচ্চ severity, কম priority: crash, অর্থাৎ critical; কিন্তু ১৯৯০-এর দশকের পুরোনো archive খুব কম মানুষ export করেন, আর অন্য পথও আছে — তাই P3. B-102 — নগণ্য severity, সর্বোচ্চ priority: হোম পেজে প্রতিষ্ঠানের নাম ভুল বানানে; system-এর কোনো ক্ষতি নেই, কিন্তু ফল প্রকাশের দিনে লাখো মানুষ দেখবেন — সুনামের প্রশ্ন, তাই P1. এ দুটি উদাহরণই পরীক্ষা আর ইন্টারভিউর প্রিয় প্রশ্ন।
৯ · Agile দলে testing — whole-team আর shift-left
Waterfall-ধাঁচের পুরোনো ধারায় testing ছিল একটি আলাদা ধাপ, শেষের দিকে, আলাদা দলের হাতে: developer “শেষ” করে test দলের দিকে কোড “ছুড়ে” দিতেন। দুটি সমস্যা হতো — ভুল ধরা পড়ত দেরিতে, যখন সারানো সবচেয়ে দামি; আর quality হয়ে যেত “অন্যের কাজ”।
Agile দলে (পর্ব ৫ Agile আর Scrum) দুটি বড় বদল:
- Whole-team approach: Quality পুরো দলের দায়িত্ব। Developer নিজের unit আর integration test লেখেন; tester-এর দক্ষতা কাজে লাগে ঝুঁকি খোঁজা, acceptance criteria লেখায় সাহায্য, exploratory testing আর test কৌশল ঠিক করায়; product owner acceptance-এর সিদ্ধান্তে থাকেন। Extreme Programming (Kent Beck, ১৯৯৯)-এ এই ধারণা নাম পায়।
- Shift-left: সময়রেখা বাঁ থেকে ডানে আঁকলে testing-কে বাঁ দিকে — আগে — সরানো: requirement লেখার সময়েই “কীভাবে test করব?” প্রশ্ন (Given-When-Then, পর্ব ২২ আর ৪৮), কোড লেখার আগে test (TDD, পর্ব ৪৮), প্রতিটি commit-এ স্বয়ংক্রিয় test (continuous integration, পর্ব ৫৮)। এর পাশাপাশি অনেক দল “shift-right”-ও করে — production-এ monitoring, ধাপে ধাপে release, feature flag দিয়ে আসল ব্যবহার থেকে শেখা।
Sprint-এর মধ্যে test শেষ না হলে কাজটি “Done” নয় — এটি দলের Definition of Done-এ লেখা থাকে। আর কোন ধরনের test কেন, তা সাজানোর একটি জনপ্রিয় ছক হলো Agile testing quadrants: Brian Marick-এর প্রস্তাব (২০০৩), Lisa Crispin আর Janet Gregory-র বই Agile Testing (২০০৯)-এ জনপ্রিয়। এর চার ঘরে পড়ে unit ও component test, functional ও story test, exploratory ও usability test, আর performance ও security test.
আরও গভীরে: Testing-এর আন্তর্জাতিক মান হলো ISO/IEC/IEEE 29119 সিরিজ (২০১৩ থেকে; ধারণা, process, নথি, কৌশল) — যদিও কিছু টেস্টিং-বিশেষজ্ঞ এর সমালোচনা করেছেন অতি-আনুষ্ঠানিকতার জন্য। ক্লাসিক বই Glenford Myers-এর The Art of Software Testing (১৯৭৯), যেখানে testing-কে “ভুল খোঁজার উদ্দেশ্যে প্রোগ্রাম চালানো” বলা হয়েছে; আর Boris Beizer-এর Software Testing Techniques (১৯৯০). শব্দের নিখুঁত সংজ্ঞার জন্য ISTQB-র অনলাইন glossary কাজের — তবে মনে রাখুন, সেটিও একটি ঘরানার সংজ্ঞা।
পরীক্ষা ও ইন্টারভিউতে:
- Verification আর validation-এর পার্থক্য? — “Building the product right” (spec ও নকশার সাথে মেলানো) বনাম “building the right product” (আসল প্রয়োজন মেটানো); Boehm-এর পরিচিত রূপ।
- Test-এর চার স্তর আর প্রতিটি কী ধরে? — Unit (যুক্তি), integration (interface), system (পুরো প্রবাহ ও non-functional), acceptance (প্রয়োজন ও গ্রহণযোগ্যতা)।
- Smoke আর sanity testing-এর পার্থক্য? — প্রচলিত অর্থে smoke: নতুন build-এর চওড়া অগভীর check; sanity: ছোট বদলের পরে একটি এলাকার সরু check. সংজ্ঞা উৎসভেদে আলাদা, তা উল্লেখ করুন।
- উচ্চ severity কিন্তু কম priority-র উদাহরণ? — খুব কম ব্যবহৃত, বিকল্প পথ আছে এমন feature-এ crash; উল্টোটা: হোম পেজে প্রতিষ্ঠানের নামের বানান ভুল।
- Exhaustive testing কেন অসম্ভব? — ইনপুটের ঘর গুণ হয়: দুটি 32-bit integer = 264 case, সেকেন্ডে 109 test-এ প্রায় ৫৮৪ বছর; ক্রম, অবস্থা আর পরিবেশ ধরলে আরও বেশি।
১০ · AI-এর যুগে — AI-লেখা test আর AI-এর দেওয়া testing-জ্ঞান
AI কয়েক সেকেন্ডে ডজনখানেক test লিখে দেয়, আর “coverage ৯৫%” দেখে মনে হয় কাজ শেষ। কিন্তু অংশ ৭-এর oracle-এর সমস্যা এখানেই সবচেয়ে বেশি কামড়ায়: AI প্রায়ই কোড পড়ে প্রত্যাশিত ফল লেখে, তাই কোডের ভুলটিই test-এ “ঠিক” হিসেবে বসে যায়।
| AI-এর চেনা ভুল | কীভাবে ধরবেন |
|---|---|
Implementation-এর প্রতিচ্ছবি test: কোডে > 1000, test-এও ১,০০০-এ ছাড় ০ প্রত্যাশা | প্রত্যাশিত ফল requirement থেকে নিজে লিখুন; কোডে ইচ্ছে করে একটি ভুল ঢুকিয়ে দেখুন test লাল হয় কি না (mutation testing, পর্ব ৫০) |
Assertion ছাড়া বা অর্থহীন assertion (expect(result).toBeDefined()) | প্রতিটি test পড়ে জিজ্ঞেস করুন: কোন ভুল কোডে এটি ফেল করবে? |
| শুধু “সুখের পথ” — সীমার মান, খালি ইনপুট, ঋণাত্মক, error-এর পথ বাদ | পর্ব ৪৭-এর কৌশলে নিজে case-তালিকা বানিয়ে মেলান |
| বানানো সংজ্ঞা বা মান: “ISTQB বলে smoke মানে …”, ভুল বছর বা ভুল standard নম্বর | Syllabus বা standard-এর মূল উৎস দেখুন; সংজ্ঞাভেদ আছে কি না জানুন |
| নেই এমন framework API বা flag (Jest/pytest-এর বানানো option) | অফিসিয়াল documentation; একটি throwaway project-এ চালিয়ে দেখা |
| “Test ঠিক করতে” assertion বদলে দেওয়া, কোডের ভুল না সারিয়ে | Diff review করুন: test-এর প্রত্যাশা বদলালে কারণ requirement-এ খুঁজুন |
“পরিষ্কার শুরু”-র নামে git reset --hard বা git clean -fdx, সতর্কতা ছাড়া — test fixture আর না-commit করা কাজ মুছে যায় | আগে git status আর git stash; throwaway clone-এ চালান |
| Prompt-এ আসল গ্রাহকের data বা production-এর secret দিয়ে test data বানানো | বানানো বা মুছে-ফেলা (anonymized) data; secret কখনো prompt-এ নয় |
AI-কে test-এর সহকারী হিসেবে ব্যবহারের একটি prompt — এতে requirement আগে, কোড পরে, আর oracle-এর উৎস স্পষ্ট:
AI-এর সাহায্যে testing — চেকলিস্ট
ভাবনার প্রশ্ন
প্রতিটি প্রশ্ন নিয়ে নিজে কিছুক্ষণ ভাবুন — তারপর “→ উত্তর” চাপুন।
প্র ০১Testing যদি ভুলের অনুপস্থিতি প্রমাণ করতে না পারে, তাহলে কি গাণিতিক প্রমাণই (formal verification) আসল সমাধান? তাহলে সবাই কেন সেটি করে না?
Dijkstra নিজে এই যুক্তিতেই প্রোগ্রামকে গাণিতিকভাবে সঠিক প্রমাণ করার পক্ষে ছিলেন — test দিয়ে নয়, প্রোগ্রাম লেখার সময়েই যুক্তি দিয়ে। এই ধারা থেকে এসেছে formal verification: একটি নিখুঁত গাণিতিক specification লিখে প্রমাণ করা যে কোড সেটি সব ইনপুটে মানে। এর সাফল্যের উদাহরণ আছে। seL4 নামের একটি operating system microkernel-এর C implementation তার specification মানে — এই প্রমাণ ২০০৯ সালে প্রকাশিত হয় (Klein ও সহকর্মী, SOSP). Xavier Leroy-র নেতৃত্বে তৈরি CompCert নামের C compiler-এর বড় অংশের সঠিকতা Coq proof assistant-এ প্রমাণিত।
তবু তিনটি সীমা আছে। প্রথমত, খরচ: seL4-এর মতো প্রকল্পে প্রমাণ লিখতে কোড লেখার চেয়ে বহু গুণ বেশি মানুষ-বছর লেগেছিল বলে প্রকাশিত হিসাবে জানা যায়। একটি অনলাইন দোকানের প্রতি সপ্তাহের বদলে এটি অবাস্তব। দ্বিতীয়ত, প্রমাণ শুধু specification-এর সাপেক্ষে — spec নিজেই ভুল হলে প্রমাণিত কোডও ভুল জিনিস করবে। অর্থাৎ formal verification হলো চূড়ান্ত verification, কিন্তু validation-এর প্রশ্ন থেকে যায়। তৃতীয়ত, প্রমাণ কিছু অনুমানের ওপর দাঁড়ায়: compiler, hardware, প্রমাণের tool নিজে ঠিক আছে। Donald Knuth-এর একটি ১৯৭৭ সালের চিঠির বিখ্যাত রসিকতা: “Beware of bugs in the above code; I have only proved it correct, not tried it.”
তাই বাস্তবে মিশ্র পথ: বেশিরভাগ কোডে test, ঝুঁকিপূর্ণ ছোট অংশে (cryptography, consensus algorithm, kernel) formal পদ্ধতি বা model checking, আর মাঝামাঝি হাতিয়ার — type system, static analysis, property-based testing — যা ছোট ছোট “প্রমাণ” সস্তায় দেয়।
মূল উপলব্ধি: Testing আর প্রমাণ প্রতিদ্বন্দ্বী নয়, একটি বর্ণালির দুই প্রান্ত। ঝুঁকি যত বেশি আর কোড যত স্থির, প্রমাণের দিকে সরা তত যুক্তিসংগত; আর spec ঠিক কি না, সেই প্রশ্ন কোনো প্রমাণ মেটায় না।
প্র ০২১৯৯৬ সালে Ariane 5 রকেটের প্রথম উড্ডয়ন ব্যর্থ হয় এমন software-এর কারণে, যা Ariane 4-এ বহু বছর ঠিকঠাক চলেছিল। “আগে চলেছে” কেন যথেষ্ট প্রমাণ নয়? কোন test-এর স্তর এটি ধরতে পারত?
৪ জুন ১৯৯৬, Ariane 5-এর Flight 501 উৎক্ষেপণের প্রায় ৪০ সেকেন্ড পরে পথ হারিয়ে ভেঙে পড়ে। তদন্ত বোর্ডের (Jacques-Louis Lions-এর সভাপতিত্বে) প্রকাশিত প্রতিবেদনের সারকথা অনুযায়ী: Inertial Reference System-এর software Ariane 4 থেকে প্রায় অপরিবর্তিত নেওয়া হয়েছিল। এর ভেতরে একটি 64-bit floating-point মানকে (horizontal velocity-সংক্রান্ত একটি মান) 16-bit signed integer-এ রূপান্তর করা হতো, overflow-এর সুরক্ষা ছাড়া। Ariane 4-এর গতিপথে মানটি কখনো সীমা ছাড়াত না, তাই সুরক্ষা অপ্রয়োজনীয় মনে করা হয়েছিল। Ariane 5 অনেক দ্রুত, তাই মানটি সীমা ছাড়িয়ে exception তুলল; প্রধান আর backup দুটি একই software চালাচ্ছিল, তাই দুটিই একসাথে বন্ধ হলো। আরও বেদনাদায়ক: এই হিসাবটি উৎক্ষেপণের পরে আর দরকারই ছিল না।
Testing-এর ভাষায় এখানে কয়েকটি নীতি একসাথে। Testing প্রসঙ্গ-নির্ভর: Ariane 4-এর জন্য করা test আর উড্ডয়নের অভিজ্ঞতা Ariane 4-এর ইনপুট-পরিসরের কথা বলে; নতুন রকেটের গতিপথ ছিল ভিন্ন ইনপুটের ঘর। Validation বনাম verification: software তার পুরোনো spec ঠিকই মানত — verification-এ দোষ নেই — কিন্তু নতুন পরিবেশের প্রয়োজনের সাথে মেলানো, অর্থাৎ নতুন ব্যবহারে validation, হয়নি। প্রতিবেদনে বলা হয়, Ariane 5-এর আসল গতিপথের data দিয়ে system-টির পূর্ণ simulation-ভিত্তিক test করা হয়নি; করলে ভুলটি ধরা পড়ার সম্ভাবনা ছিল। অর্থাৎ system ও integration স্তরে, আসল ইনপুট-পরিসর দিয়ে test.
আজকের পরিভাষায় এটি reuse-এর ঝুঁকি: library, পুরোনো module বা অন্য দলের service নতুন জায়গায় বসালে তার পুরোনো test-এর ইতিহাস নতুন ইনপুটের কথা বলে না। প্রতিকার: পুনর্ব্যবহৃত component-এর অনুমান (input range, একক, সময়) লিখে রাখা (পর্ব ৩১-এর contract), নতুন প্রসঙ্গে সেই অনুমান যাচাই, আর সীমার মানে test.
মূল উপলব্ধি: “এটা তো বছরের পর বছর চলছে” একটি নির্দিষ্ট ইনপুট-পরিসরের প্রমাণ, সার্বজনীন নয়। প্রসঙ্গ বদলালে test-ও নতুন করে ভাবতে হয়।
প্র ০৩Therac-25 নামের রেডিওথেরাপি যন্ত্রের দুর্ঘটনায় (১৯৮৫–৮৭) রোগীরা মারাত্মক মাত্রায় বিকিরণ পান, অথচ যন্ত্রটি test করা হয়েছিল। কোন ধরনের ভুল সাধারণ test এড়িয়ে যায়, আর এখান থেকে testing কী শিখেছে?
Nancy Leveson আর Clark Turner-এর ১৯৯৩ সালের বিখ্যাত তদন্ত-প্রবন্ধ (IEEE Computer) অনুযায়ী, ১৯৮৫ থেকে ১৯৮৭-র মধ্যে Therac-25-এ অন্তত ছয়টি দুর্ঘটনায় রোগীরা নির্ধারিত মাত্রার বহু গুণ বিকিরণ পান; কয়েকজন মারা যান। কারণগুলোর মধ্যে ছিল software-এর race condition: অপারেটর খুব দ্রুত — কয়েক সেকেন্ডের মধ্যে — চিকিৎসার তথ্য সংশোধন করলে যন্ত্রের একটি অংশ পুরোনো সেটিং ধরে রাখত। আগের মডেলগুলোতে hardware interlock ছিল, যা ভুল অবস্থায় যন্ত্র আটকে দিত; Therac-25-এ এই দায়িত্ব অনেকটাই software-এর ওপর দেওয়া হয়। Error বার্তা ছিল অস্পষ্ট (“MALFUNCTION 54”-এর মতো)।
Testing-এর শিক্ষা কয়েকটি। প্রথমত, সময় আর ক্রম-নির্ভর ভুল সাধারণ test-এ প্রায় ধরা পড়ে না: test-কারী ধীরে, একটি নির্দিষ্ট ক্রমে কাজ করেন; ভুলটি জাগে শুধু অভিজ্ঞ অপারেটরের দ্রুত আঙুলে। হাসপাতালের অভিযোগের পরে প্রস্তুতকারক প্রথমে ভুলটি পুনরুৎপাদন করতে পারেনি বলে জানা যায় — “reproduce হয় না” মানে “ভুল নেই” নয়। দ্বিতীয়ত, পুরোনো যন্ত্রে নিরাপদ চলা কোড নতুন নকশায় নিরাপদ নয় — hardware interlock আগের মডেলে ভুলগুলো ঢেকে রাখছিল (Ariane 5-এর মতোই প্রসঙ্গের শিক্ষা)। তৃতীয়ত, usability-ও নিরাপত্তা: অর্থহীন error বার্তা অপারেটরকে ভুল সিদ্ধান্তে ঠেলে দেয়।
এই ঘটনার পরে safety-critical software-এর জন্য আলাদা মান আর শৃঙ্খলা অনেক জোরদার হয় — চিকিৎসা যন্ত্রের software-এর জন্য IEC 62304-এর মতো মান, hazard analysis, স্বাধীন review, আর concurrency-র জন্য বিশেষ কৌশল (stress test, model checking). Severity-র মাপে এখানে প্রতিটি defect “critical”, আর testing-এর প্রসঙ্গ-নির্ভরতা নীতির সবচেয়ে কঠোর রূপ।
মূল উপলব্ধি: Test দেখে আপনি যা ভেবেছেন তা-ই; যে পরিস্থিতি কল্পনায় আসেনি — দ্রুত হাত, অদ্ভুত ক্রম, hardware সুরক্ষা সরানো — সেটি অদেখা থাকে। ঝুঁকি বেশি হলে test-এর পাশাপাশি নকশায় নিরাপত্তা আর স্বাধীন বিশ্লেষণ লাগে।
প্র ০৪Glenford Myers ১৯৭৯ সালে লিখেছিলেন, প্রোগ্রামারের নিজের প্রোগ্রাম নিজে test না করাই ভালো। আজ Agile দলে developer-রাই বেশিরভাগ test লেখেন। কে ঠিক — আর আলাদা tester-এর ভূমিকা কি শেষ?
Myers-এর The Art of Software Testing (১৯৭৯)-এর যুক্তি মনস্তাত্ত্বিক: testing-এর লক্ষ্য ভুল খোঁজা, আর নিজের তৈরি জিনিসে ভুল খুঁজতে মানুষ স্বভাবত অনিচ্ছুক। তার ওপর, প্রোগ্রামার যদি requirement ভুল বুঝে থাকেন, তাঁর test-ও সেই ভুল বোঝাই মেনে চলবে — একই অন্ধ জায়গা দুবার। এজন্য দীর্ঘদিন বড় প্রতিষ্ঠানে আলাদা QA দল ছিল, আর নিরাপত্তা-সংবেদনশীল ক্ষেত্রে আজও “স্বাধীন verification ও validation” (IV&V) আইন বা চুক্তিতে চাওয়া হয়।
কিন্তু শেষে আলাদা দলের ধারার দামও পরিষ্কার হলো: feedback দেরিতে আসে, দুই দলের মধ্যে কাজ ছোড়াছুড়ি হয়, আর developer quality-কে অন্যের দায়িত্ব ভাবতে শুরু করেন। Extreme Programming আর Agile এর উত্তরে developer-দের হাতে automated test দিল — unit আর integration test কোডের সাথে, প্রতিটি commit-এ। বেশ কয়েকটি বড় software প্রতিষ্ঠান ২০১০-এর দশকে আলাদা “test engineer” ভূমিকা কমিয়ে বা বদলে developer-দের মধ্যে মিশিয়ে দিয়েছে বলে প্রকাশ্যে আলোচিত হয়েছে, যদিও প্রতিটির ফল আর কারণ আলাদা।
আসলে দুটি দাবির বিষয় আলাদা। Myers ঠিক যে একই মনের test-এ অন্ধ জায়গা থাকে। Agile ঠিক যে দ্রুত, স্বয়ংক্রিয়, নিজের লেখা test ছাড়া দিনে দশবার release অসম্ভব। সমাধান হলো অন্ধ জায়গাগুলো অন্যভাবে ঢাকা: acceptance criteria আগে থেকে product owner আর tester-এর সাথে বসে লেখা (Three Amigos), PR-এ অন্য কেউ test review করেন (পর্ব ৪৪), mutation testing (পর্ব ৫০) test-এর শক্তি মাপে, আর exploratory testing — একজন দক্ষ tester-এর কৌতূহলী, পরিকল্পিত-কিন্তু-লিপিহীন খোঁজ — এমন ভুল খোঁজে যা কেউ লিখে রাখেননি।
মূল উপলব্ধি: প্রশ্নটি “কে test করবে” নয়, “কোন অন্ধ জায়গা কে ঢাকবে”। নিজের test দ্রুততা দেয়, অন্যের চোখ স্বাধীনতা দেয় — ভালো দল দুটিই নকশা করে রাখে।
প্র ০৫২০১২ সালে Knight Capital নামের একটি মার্কিন trading প্রতিষ্ঠান প্রায় ৪৫ মিনিটে বিপুল লোকসান করে — নতুন কোডের deploy-এর পরে। সব test পাস করা কোড কীভাবে এমন বিপর্যয় ঘটায়? কোন ধরনের test বা check এখানে কাজে আসত?
মার্কিন Securities and Exchange Commission-এর ২০১৩ সালের প্রকাশিত আদেশে ঘটনার বিবরণ আছে। সংক্ষেপে: ১ আগস্ট ২০১২, Knight-এর একটি order-routing system-এর নতুন কোড আটটি server-এ বসানোর কথা ছিল, কিন্তু হাতে করা deploy-এ একটি server বাদ পড়ে যায়। নতুন কোড একটি পুরোনো flag-এর অর্থ নতুনভাবে ব্যবহার করছিল; বাদ পড়া server-এ সেই flag জাগিয়ে তুলল বহু বছর আগের অব্যবহৃত “Power Peg” নামের কোড, যা অনিয়ন্ত্রিতভাবে order পাঠাতে থাকে। প্রায় ৪৫ মিনিটে লক্ষ লক্ষ order, আর প্রতিষ্ঠানটির প্রায় ৪৬ কোটি ডলারের লোকসান বলে প্রকাশিত হয়; কয়েক দিনের মধ্যে প্রতিষ্ঠানটিকে উদ্ধার-বিনিয়োগ নিতে হয়।
লক্ষ করুন, নতুন কোডের নিজের যুক্তি হয়তো ঠিকই ছিল — unit আর system test এর কথা বলে। ভুলটি ছিল deploy করা পরিবেশ আর পুরোনো কোডের সাথে মিথস্ক্রিয়ায়। কয়েকটি স্তর এটি ধরতে পারত। Operational acceptance ও deploy-এর verification: সব server-এ একই সংস্করণ আছে কি না, স্বয়ংক্রিয়ভাবে মেলানো। Deploy-এর পরে production-এ একটি smoke test আর monitoring: অস্বাভাবিক order-এর হার দেখলে স্বয়ংক্রিয় থামা (kill switch). Regression ও integration-এর চোখে: পুরোনো flag-এর অর্থ বদলানো একটি interface-বদল — পুরোনো ও নতুন কোড মিশে থাকলে কী হবে, তার test. আর dead code সরিয়ে ফেলা (পর্ব ৪৩-এর technical debt): যে কোড নেই, সেটি জাগতে পারে না।
এখান থেকে testing-এর সাতটি নীতির শেষটি মনে পড়ে: test সবুজ থাকা মানেই সফল ব্যবস্থা নয়। Deploy-এর প্রক্রিয়া আর config-ও test-এর বিষয় — continuous delivery-র (পর্ব ৫৯) একটি কেন্দ্রীয় ধারণা।
মূল উপলব্ধি: Software বলতে শুধু কোড নয় — কোড, config, পরিবেশ আর deploy-এর প্রক্রিয়া মিলে যা চলে। Test-এর পরিসরও সেই পুরো জিনিসটির হওয়া উচিত, বিশেষ করে যেখানে এক মিনিটের ভুলের দাম বিশাল।
অনুশীলন
প্রথমে নিজে উত্তর লিখুন, তারপর মিলিয়ে দেখুন।
-
হিসাব (exhaustive testing): একটি function তিনটি 16-bit unsigned integer নেয় (প্রতিটি ০ থেকে ৬৫,৫৩৫)। (ক) মোট কয়টি ইনপুট-সমন্বয়? (খ) সেকেন্ডে ১০ লাখ (106) test চালালে কত বছর লাগবে (বছর = ৩৬৫.২৫ দিন)? (গ) ১,০০০টি যন্ত্রে সমান ভাগ করে চালালে কত দিন? (ঘ) একই ১,০০০টি যন্ত্রে চতুর্থ একটি 16-bit ইনপুট যোগ করলে কত বছর?
(ক) প্রতিটি ইনপুটে 216 = ৬৫,৫৩৬টি মান; তিনটির গুণফল 216 × 216 × 216 = 248 =
281474976710656≈ ২.৮১ × 1014.(খ) 2.81 × 1014 / 106 ≈ ২৮,১৪,৭৪,৯৭৭ সেকেন্ড। এক বছর = ৩৬৫.২৫ × ২৪ × ৩,৬০০ = ৩,১৫,৫৭,৬০০ সেকেন্ড। ভাগ করলে ≈ ৮.৯২ বছর.
(গ) ১,০০০ যন্ত্রে ২,৮১,৪৭৫ সেকেন্ড; ৮৬,৪০০ দিয়ে ভাগ করলে ≈ ৩.২৬ দিন. এটি সম্ভব — তাই কিছু ছোট কিন্তু ঝুঁকিপূর্ণ function (যেমন 16-bit-এর একটি checksum) সত্যিই exhaustive test করা হয়।
(ঘ) আরেকটি ×৬৫,৫৩৬: ৮.৯২ বছর × ৬৫,৫৩৬ / ১,০০০ ≈ ৫৮৪.৫ বছর — অংশ ২-এর দুই 32-bit-এর উত্তরের সমান, কারণ দুটিই মোট 64 bit ইনপুট আর একই কার্যকর হার (১,০০০ × 106 = 109). মোট ইনপুটের bit-সংখ্যাই আসল মাপ। (Python দিয়ে মিলিয়েছি: ৮.৯১৯৪ বছর, ৩.২৫৭৮ দিন।)
-
Test-এর ধরন চিনুন: প্রতিটি পরিস্থিতিতে কোন স্তর আর কোন ধরন? (ক) Payment gateway-র sandbox-এর সাথে cart service-এর কথা বলা যাচাই। (খ) নতুন build আসার পরে ১০ মিনিটে দেখা — অ্যাপ খোলে, login হয়, cart আর checkout পাতা খোলে। (গ) B-103 ঠিক করার পরে ঠিক সেই ব্যর্থ test আবার চালানো। (ঘ) Coupon feature যোগের পরে পুরোনো ৩০০টি automated test আবার চালানো। (ঙ) ১০ জন সদস্যকে নতুন checkout পাতা দিয়ে order দিতে বলা হলো, কোথায় আটকান তা লিখে রাখা হলো। (চ) ফলাফল প্রকাশের দিনের মতো মিনিটে ১০,০০০ অনুরোধ পাঠিয়ে সাড়া দেওয়ার সময় মাপা। (ছ) চালানোর দল দেখল database backup থেকে ৩০ মিনিটে system ফেরানো যায় কি না।
- (ক) Integration স্তর, functional; বাইরের service-এর সাথে interface.
- (খ) Smoke test, system স্তরে — চওড়া, অগভীর, “আরও test করার যোগ্য কি না”।
- (গ) Confirmation testing (re-testing).
- (ঘ) Regression testing — সংশোধন বা নতুন feature অন্য কিছু ভাঙল কি না।
- (ঙ) Usability test (non-functional), acceptance-এর কাছাকাছি — আসল ব্যবহারকারী দিয়ে; এটি validation-ও।
- (চ) Performance (load) test, system স্তরে, non-functional.
- (ছ) Operational acceptance testing — backup/restore আর পুনরুদ্ধারের সময়।
(খ)-কে কেউ “sanity” বললেও ভুল নয় যদি তাঁর দলের সংজ্ঞা তাই হয় — সংজ্ঞাটি বলে নিন। মনে রাখুন, স্তর আর ধরন দুটি আলাদা অক্ষ; একটি test দুটোরই এক একটি ঘরে পড়ে।
-
আউটপুট অনুমান করুন: অংশ ৮-এর
tiny_runner.js-এ দুটি বদল: (১)total > 1000-কেtotal >= 1000করা হলো; (২) শেষ test-এর আগে নতুন একটি test যোগ হলো:it("member, 1001 -> 100.1", () => expect(discount(1001, true)).toBe(100.1));. প্রোগ্রাম কী ছাপবে? কোনো test ফেল করলে, দোষ কোডের না test-এর?আসলে চালালে আউটপুট:
discount() PASS member, 1500 taka -> 150 PASS non-member gets nothing PASS member, 999 taka -> 0 PASS member, exactly 1000 -> 100 FAIL member, 1001 -> 100.1 expected 100.1, got 100.10000000000001 PASS negative total throws 5 passed, 1 failed, 6 totalসীমার fix-এর পরে ১,০০০-এর test পাস। কিন্তু
1001 * 0.10JavaScript-এ100.10000000000001, কারণ 0.1 binary floating point-এ নিখুঁতভাবে রাখা যায় না;toBeহুবহু মিল (!==) চায়। দোষ কার? দুজনেরই কিছুটা। Test-এর দিক থেকে: দশমিক তুলনায় হুবহু মিল ভঙ্গুর — আসল framework-এ এর জন্য আলাদা matcher আছে (Jest-এtoBeCloseTo, pytest-এpytest.approx). কোডের দিক থেকে: টাকার হিসাবে floating point নিজেই ঝুঁকি — ১০০.১০০০০০০০০০০০০০১ টাকা invoice-এ ছাপা হলে বা যোগফলে জমলে সমস্যা। ভালো সমাধান হলো পয়সায় integer হিসাব (১,০০১ টাকা = ১,০০,১০০ পয়সা; ছাড় ১০,০১০ পয়সা), আর নিয়ম ঠিক করা: ছাড় কোন দিকে গোল করা হবে। -
নকশা ও সমালোচনা: একটি কাল্পনিক বিশ্ববিদ্যালয়ের ফলাফল পোর্টালের দলের test-পরিকল্পনা: “আমরা শুধু system স্তরে, হাতে, release-এর আগের দুই দিনে test করব। ১০০% test case পাস হলেই release. সব test case লিখবেন developer নিজে, কোড দেখে প্রত্যাশিত ফল বসিয়ে। Defect-এর শুধু একটি ঘর থাকবে: ‘গুরুত্ব’।” অন্তত পাঁচটি দুর্বলতা দেখান, প্রতিটির জন্য এই পর্বের কোন নীতি বা ধারণা ভাঙছে বলুন, আর একটি উন্নত পরিকল্পনা প্রস্তাব করুন।
- শুধু শেষে, শুধু system স্তরে: early testing নীতি ভাঙছে; unit আর integration-এর ভুল এখন দেরিতে, দামি অবস্থায় ধরা পড়বে, আর system test-এ fault খোঁজা কঠিন। প্রস্তাব: প্রতিটি স্তরে test, unit ও integration স্বয়ংক্রিয়, প্রতিটি commit-এ CI.
- শুধু হাতে: regression প্রতিবার হাতে চালানো অসম্ভব হয়ে উঠবে; “নির্ভয়ে বদলানো”-র কাজটি হারায়। প্রস্তাব: পুনরাবৃত্ত check automated, হাতের সময় exploratory testing-এ।
- “১০০% পাস = release”: absence-of-defects fallacy আর “উপস্থিতি, অনুপস্থিতি নয়” — পাস মানে শুধু এই case-গুলোতে ভুল পাইনি। Validation-এর কোনো ধাপ নেই। প্রস্তাব: ঝুঁকি-ভিত্তিক exit criteria, আর ছাত্র ও পরীক্ষা নিয়ন্ত্রক দপ্তরের প্রতিনিধি দিয়ে UAT.
- Developer কোড দেখে প্রত্যাশিত ফল বসান: test oracle-এর সমস্যা — test কোডের প্রতিচ্ছবি, ভুল GPA হিসাবও “ঠিক”। প্রস্তাব: প্রত্যাশিত ফল নিয়মের নথি বা হাতে করা হিসাব থেকে; অন্য কেউ test review করেন।
- শুধু functional: ফল প্রকাশের দিনের ভিড় (performance), অন্যের ফল দেখা ঠেকানো (security), ফোনে পড়া (compatibility) — non-functional ধরন বাদ। Testing প্রসঙ্গ-নির্ভর: এই প্রসঙ্গে ভিড়ই সবচেয়ে বড় ঝুঁকি।
- শুধু “গুরুত্ব” ঘর: severity আর priority মিশে গেছে — B-102-এর মতো বানান ভুল কোথায় যাবে? প্রস্তাব: দুটি আলাদা ঘর, আর triage-এর নিয়ম।
উন্নত পরিকল্পনার সারাংশ: প্রতিটি requirement-এর acceptance criteria আগে লেখা (shift-left); unit আর integration test প্রতিটি PR-এ; ফল প্রকাশের দিনের মতো load test; access-control-এর security test; নতুন build-এ smoke test; গত বছরের defect বেশি পাওয়া module-এ (যেমন GPA হিসাব) বেশি test (defect clustering); release-এর আগে UAT; defect-এ severity ও priority আলাদা।
নিজেকে যাচাই করুন
ভুল হলে কারণসহ ব্যাখ্যা পাবেন, আবার চেষ্টাও করতে পারবেন।
ক্রমএকটি defect-এর জীবনচক্র সঠিক ক্রমে সাজান (▲▼ চেপে সরান)
- 🐞 Tester ধাপ, data, প্রত্যাশিত ও আসল ফলসহ defect লেখেন (New)
- 🗂️ Triage-এ পুনরুৎপাদন যাচাই, severity ও priority ঠিক, developer-কে দায়িত্ব
- 🔧 Developer কোড ঠিক করেন, PR review পার হয় (Fixed)
- 🔁 Tester confirmation test চালান
- 🧪 পুরো regression suite চালিয়ে দেখা হয় অন্য কিছু ভাঙেনি (Verified)
- ✅ Defect বন্ধ, নতুন regression test suite-এ যোগ (Closed)
একটি হিসাবের software তার লিখিত specification পুরোপুরি মানে, সব test পাস করে; কিন্তু হিসাবরক্ষকেরা বলেন এতে মাসিক বন্ধের (month-end closing) কাজটি করাই যায় না, কারণ spec-এ সেটি ছিল না। কোনটি ব্যর্থ হয়েছে?
- Verification — product ঠিকভাবে বানানো হয়নি
- Validation — ঠিক product বানানো হয়নি
- Regression testing
- Unit test-এর স্তর
একটি function দুটি 32-bit integer নেয়। সেকেন্ডে 109 test চালালে সব ইনপুট-জোড়া পরীক্ষা করতে মোটামুটি কত সময় লাগবে?
- প্রায় ৪.৩ সেকেন্ড
- প্রায় ৮.৬ সেকেন্ড
- প্রায় ২১৩ দিন
- প্রায় ৫৮৪ বছর
একটি ফলাফল পোর্টালে দুটি defect: (A) হোম পেজে প্রতিষ্ঠানের নাম ভুল বানানে, ফল প্রকাশ আগামীকাল; (B) ১৯৯০-এর দশকের archive export করলে crash, যা বছরে দুই-একবার কেউ করেন আর বিকল্প পথ আছে। সবচেয়ে যুক্তিসংগত মূল্যায়ন কোনটি?
- A: উচ্চ severity, উচ্চ priority; B: কম severity, কম priority
- A: কম severity, কম priority; B: কম severity, উচ্চ priority
- A: কম severity, উচ্চ priority; B: উচ্চ severity, কম priority
- Severity আর priority একই জিনিস, তাই দুটিরই মান সমান
একটি দল ৬ মাস ধরে প্রতিদিন একই ২০০টি automated test চালায়; সবগুলো সবুজ, অথচ ব্যবহারকারীরা নিয়মিত নতুন bug জানাচ্ছেন। ISTQB-র কোন নীতি এটি সবচেয়ে ভালো ব্যাখ্যা করে, আর কী করা উচিত?
- Tests wear out (pesticide paradox) — test হালনাগাদ করা আর নতুন কৌশলে নতুন test যোগ করা, পুরোনো regression test রেখেই
- Defect clustering — সব test মুছে নতুন করে লেখা
- Exhaustive testing অসম্ভব — তাই test-এর সংখ্যা কমিয়ে ৫০টি করা
- Early testing — একই test আরও আগে, দিনে দুবার চালানো
একজন tester একটি REST API-র test বানাচ্ছেন। তিনি database-এর table-এর গঠন আর একটি unique constraint জানেন, তাই API দিয়ে এমন দুটি অনুরোধ পাঠালেন যা সেই constraint ছোঁয়, তারপর API-র উত্তর দেখলেন। এটি কোন ধরনের পদ্ধতি?
- Black-box — শুধু spec থেকে
- Grey-box — আংশিক ভেতরের জ্ঞান, বাইরের interface দিয়ে চালানো
- White-box — কোডের প্রতিটি শাখা চালানো
- Static testing — কোড না চালিয়ে বিশ্লেষণ
AI একটি tax(amount) function-এর জন্য test লিখেছে। প্রতিটি test-এর প্রত্যাশিত ফল AI কোডটি চালিয়ে যা পেয়েছে তা-ই বসিয়েছে, আর সব test সবুজ। এর মূল সমস্যা কী?
- Test-এর সংখ্যা কম, আরও test লাগবে
- ভুল test framework ব্যবহার হয়েছে
- Test oracle কোড নিজেই, তাই কোডের ভুল থাকলেও test পাস করবে
- সব test সবুজ হওয়া অসম্ভব, তাই কোথাও ভুল আছে
একটি defect ঠিক করার পরে tester দুটি কাজ করলেন: (১) ঠিক যে test ফেল করেছিল সেটি আবার চালালেন; (২) আশেপাশের এলাকার পুরোনো সব automated test চালালেন। দুটি কাজের সঠিক নাম কী?
- (১) smoke test, (২) sanity test
- (১) regression test, (২) confirmation test
- (১) acceptance test, (২) system test
- (১) confirmation testing (re-testing), (২) regression testing
শব্দকোষ
লেখার মধ্যে ডটেড দাগ দেওয়া শব্দে মাউস রাখুন বা চাপুন — অর্থ দেখাবে। এই পর্বের সব নতুন শব্দ এখানে একসাথে:
মনে রাখুন
- Test-এর কাজ: ভুল খোঁজা, আস্থা, নির্ভয়ে বদলানো, আচরণের নথি। Test failure দেখে, fault নয়; আর ভুলের উপস্থিতি দেখায়, অনুপস্থিতি নয় (Dijkstra). ইনপুটের ঘর গুণ হয় — দুটি 32-bit integer = 264, সেকেন্ডে 109-এ প্রায় ৫৮৪ বছর।
- Verification = spec মেনে ঠিকভাবে বানানো; validation = আসল প্রয়োজনের ঠিক জিনিস। Static testing (review, static analysis) না চালিয়ে; dynamic testing চালিয়ে।
- স্তর: unit → integration → system → acceptance (UAT, alpha, beta, OAT), V-model-এ প্রতিটি নকশার ধাপের জোড়া। ধরন: functional, non-functional (performance, security, usability, compatibility), regression, confirmation, smoke, sanity — শেষ দুটির সংজ্ঞা উৎসভেদে আলাদা।
- Black-box spec থেকে, white-box কোডের গঠন থেকে, grey-box আংশিক জ্ঞানে বানিয়ে বাইরে থেকে চালানো। সাত নীতি: উপস্থিতি-অনুপস্থিতি, exhaustive অসম্ভব, early testing, defect clustering, tests wear out, প্রসঙ্গ-নির্ভরতা, “ত্রুটি নেই”-র ভ্রান্তি।
- Test case: ID, requirement, precondition, steps, data, expected result (oracle থেকে, কোড থেকে নয়)। Defect: New → Assigned → Fixed → retest → Verified → Closed; severity (প্রভাব) আর priority (জরুরিতা) আলাদা। Agile-এ quality পুরো দলের, আর testing বাঁ দিকে — আগে।
আরও পড়ুন · ABCL TECH-এ আপনার পরবর্তী পদক্ষেপ
- পর্ব ৪৬ · Unit test — Arrange-Act-Assert, assertion, fixture আর ভালো test-এর গুণপরবর্তী পর্বUnit স্তরে হাতে-কলমে test লেখা।
- সফটওয়্যার ইঞ্জিনিয়ারিং ও Git · কোর্স হোমরোডম্যাপTesting মডিউলের সাতটি পর্ব আর পুরো কোর্সের মানচিত্র।
- পর্ব ৩ · Software quality — ISO/IEC 25010, maintainability, reliability আর quality মাপাভিত্তিFault-error-failure, quality-র মডেল আর cost of quality.
- পর্ব ৪ · SDLC আর process model — waterfall, V-model, iterative, incremental আর spiralভিত্তিV-model-এর পুরো ছবি।
- পর্ব ৪৪ · Code review — কী দেখবেন, কীভাবে মন্তব্য করবেন, linter আর static analysisসম্পর্কিতStatic testing-এর হাতে-কলমে রূপ।
- System Design · Observabilityপাশের ট্র্যাকProduction-এ software-এর আচরণ দেখা — shift-right-এর ভিত্তি।