স্টেট ট্রানজিশন টেস্টিং
এই পাঠে যা শিখবেন
- স্টেট ট্রানজিশন টেস্টিং কখন প্রাসঙ্গিক (যখন আচরণ ইতিহাস/অবস্থা-নির্ভর)
- একটি ছোট স্টেট মেশিনকে
ALLOWEDট্রানজিশন ডিকশনারি দিয়ে মডেল করা - এমন একটি টেস্ট সিকোয়েন্স ডিজাইন করা যা সবগুলো বৈধ ট্রানজিশন কভার করে
- Python-এর
unittestদিয়ে বৈধ ট্রানজিশন সিকোয়েন্স ও একটি অবৈধ ট্রানজিশন প্রত্যাখ্যান — উভয়ই যাচাই করা
১ · স্টেট ট্রানজিশন টেস্টিং কী
স্টেট ট্রানজিশন টেস্টিংState Transition Testingএকটি সিস্টেমকে সীমিত সংখ্যক "অবস্থা" (state) ও তাদের মধ্যে বৈধ "ট্রানজিশন" হিসেবে মডেল করে, তারপর প্রতিটি বৈধ ট্রানজিশন অন্তত একবার ঘটিয়ে এবং অবৈধ ট্রানজিশন প্রত্যাখ্যাত হয় কি না যাচাই করে নিয়মতান্ত্রিকভাবে টেস্ট করা।
একটি অর্ডারের কথা চিন্তা করুন — এটি placed (তৈরি হয়েছে) দিয়ে শুরু হয়ে shipped
(পাঠানো হয়েছে) হয়ে delivered (পৌঁছেছে)-এ যায়। এছাড়া placed বা
shipped থেকে সরাসরি cancelled (বাতিল) হওয়া যায়, কিন্তু একবার
delivered বা cancelled হয়ে গেলে আর কোনো ট্রানজিশন সম্ভব নয়। ইনপুট নিজে থেকে যথেষ্ট
নয় এখানে — "cancelled" এ যাওয়া চেষ্টা বৈধ কি না তা নির্ভর করে অর্ডারটি এখন কোন
স্টেটে আছে তার উপর।
২ · কোড সেলে সম্পূর্ণ কভারেজ ডিজাইন করা
একটি ভালো টেস্ট সিকোয়েন্সের লক্ষ্য হলো চারটি বৈধ ট্রানজিশনই অন্তত একবার ঘটানো — শুধু একটি "হ্যাপি পাথ"
(placed → shipped → delivered) টেস্ট করলে cancelled-এর দুটো পথ কখনোই যাচাই হতো
না। নিচের কোডে তিনটি ভিন্ন অর্ডার দিয়ে চারটি ট্রানজিশনই কভার করা হয়েছে, এবং কভারেজ সত্যিকারভাবে গণনা করা
হয়েছে।
import unittest
ALLOWED = {
"placed": {"shipped", "cancelled"},
"shipped": {"delivered", "cancelled"},
"delivered": set(),
"cancelled": set(),
}
class Order:
def __init__(self):
self.state = "placed"
def transition(self, new_state):
if new_state not in ALLOWED[self.state]:
raise ValueError(f"অবৈধ ট্রানজিশন: {self.state} -> {new_state}")
self.state = new_state
return self.state
# স্টেট মেশিনে সংজ্ঞায়িত সবগুলো বৈধ ট্রানজিশনের সেট -- কভারেজ যাচাইয়ের জন্য
ALL_VALID_TRANSITIONS = {(src, dst) for src, dests in ALLOWED.items() for dst in dests}
def do_transition(order, dest):
src = order.state
order.transition(dest)
return (src, dest)
exercised = set()
o1 = Order()
exercised.add(do_transition(o1, "shipped"))
exercised.add(do_transition(o1, "delivered"))
o2 = Order()
exercised.add(do_transition(o2, "cancelled"))
o3 = Order()
exercised.add(do_transition(o3, "shipped"))
exercised.add(do_transition(o3, "cancelled"))
coverage_pct = len(exercised & ALL_VALID_TRANSITIONS) / len(ALL_VALID_TRANSITIONS) * 100
print(f"মোট বৈধ ট্রানজিশন: {len(ALL_VALID_TRANSITIONS)}")
print(f"টেস্ট সিকোয়েন্সে যাচাইকৃত ট্রানজিশন: {sorted(exercised)}")
print(f"স্টেট ট্রানজিশন কভারেজ: {coverage_pct:.0f}%")
class OrderStateTransitionTests(unittest.TestCase):
def test_full_delivery_path(self):
order = Order()
self.assertEqual(order.transition("shipped"), "shipped")
self.assertEqual(order.transition("delivered"), "delivered")
def test_cancel_from_placed(self):
order = Order()
self.assertEqual(order.transition("cancelled"), "cancelled")
def test_cancel_from_shipped(self):
order = Order()
order.transition("shipped")
self.assertEqual(order.transition("cancelled"), "cancelled")
def test_invalid_transition_rejected(self):
order = Order()
order.transition("shipped")
order.transition("delivered")
with self.assertRaises(ValueError):
order.transition("shipped") # delivered থেকে আর কোনো ট্রানজিশন বৈধ নয়
suite = unittest.TestLoader().loadTestsFromTestCase(OrderStateTransitionTests)
unittest.TextTestRunner(verbosity=2).run(suite)
exercised সেটটি ঠিক ALL_VALID_TRANSITIONS-এর সমান — অর্থাৎ
চারটি বৈধ ট্রানজিশনই অন্তত একবার সিকোয়েন্সে ঘটেছে, কভারেজ ১০০%। এরপর
test_invalid_transition_rejected নিশ্চিত করে delivered স্টেট থেকে
shipped-এ ফিরে যাওয়ার চেষ্টা সঠিকভাবে ValueError রেইজ করে প্রত্যাখ্যাত হয় —
চারটি unittest টেস্টই পাস করে, কারণ এই Order ক্লাসটি স্পেসিফিকেশন অনুযায়ী
সঠিকভাবেই লেখা।
স্টেট ট্রানজিশন টেস্টিং শুধু "কী ইনপুট দিলাম" নয়, "সিস্টেম এখন কোথায় আছে" প্রশ্নটিও যোগ করে। একটি
ALLOWED ট্রানজিশন ডিকশনারি স্পেসিফিকেশনকে কোডে সরাসরি প্রতিফলিত করে, আর সবগুলো বৈধ ট্রানজিশন
অন্তত একবার কভার করা প্লাস অন্তত একটি অবৈধ ট্রানজিশন প্রত্যাখ্যান যাচাই করাই একটি ন্যূনতম-শক্তিশালী স্টেট
ট্রানজিশন টেস্ট সেট তৈরি করে।
ভাবনার প্রশ্ন
প্রতিটি প্রশ্ন নিজে কিছুক্ষণ ভাবুন — তারপর "→ উত্তর" চাপুন।
প্র ০১
যদি টেস্ট সিকোয়েন্সে শুধু test_full_delivery_path রাখা হতো (বাকি টেস্টগুলো বাদ দিয়ে),
কভারেজ কত শতাংশ হতো?
50% — কারণ শুধু placed→shipped আর shipped→delivered এই দুটো
ট্রানজিশন ঘটত, মোট চারটির মধ্যে। placed→cancelled ও shipped→cancelled
পথ দুটো একেবারেই অযাচাইকৃত থেকে যেত — যদি সেই কোডে বাগ থাকত, তা কখনোই ধরা পড়ত না।
প্র ০২
ALLOWED["delivered"] আর ALLOWED["cancelled"] খালি সেট (set())
কেন — এর মানে কী?
এর মানে delivered ও cancelled হলো টার্মিনাল স্টেট —
একবার এই দুটোর কোনো একটাতে পৌঁছালে, সিস্টেমের নিয়ম অনুযায়ী আর কোনো ট্রানজিশন সম্ভব নয়। খালি সেট মানে
"এই স্টেট থেকে কোথাও যাওয়া বৈধ নয়", তাই transition()-এর যেকোনো চেষ্টা স্বয়ংক্রিয়ভাবে
ValueError রেইজ করবে।
প্র ০৩ বাস্তব জীবনে এমন কোন কোন সিস্টেমে স্টেট ট্রানজিশন টেস্টিং স্বাভাবিকভাবেই প্রযোজ্য বলে মনে হয়?
যেকোনো সিস্টেম যেখানে একটি এনটিটি সুনির্দিষ্ট কয়েকটি "অবস্থা"-র মধ্য দিয়ে যায় — যেমন একটি লগইন সেশন (লগড-আউট → লগড-ইন → এক্সপায়ার্ড), একটি বাগ-রিপোর্ট (M11/L48-এ বিস্তারিত), একটি পেমেন্ট (pending → authorized → captured/failed), বা একটি অ্যাপের লাইফসাইকেল। যেকোনো জায়গায় "আগে কী হয়েছিল" ভবিষ্যতের বৈধ পদক্ষেপ ঠিক করে দেয়, সেখানেই এই টেকনিক প্রাসঙ্গিক।
অনুশীলন
-
চিন্তা করুন: যদি স্পেসিফিকেশনে নতুন একটি নিয়ম যোগ হয় —
deliveredথেকেreturned(রিটার্ন করা) স্টেটে যাওয়া যাবে — তাহলেALLOWEDডিকশনারিতে কী পরিবর্তন করতে হবে, এবং টেস্ট সিকোয়েন্সে কী যোগ করতে হবে?ALLOWED["delivered"]-কেset()-এর বদলে{"returned"}করতে হবে, এবংALLOWED["returned"] = set()(যদি এটিও টার্মিনাল হয়) যোগ করতে হবে। তারপর একটি নতুন টেস্ট মেথড লিখতে হবে যাplaced→shipped→delivered→returnedসিকোয়েন্স চালিয়ে এই নতুন ট্রানজিশনটিও কভারেজে যোগ করে। -
পরীক্ষা করুন: উপরের কোড সেলে
o2 = Order()ও তারdo_transition(o2, "cancelled")লাইন দুটো মুছে ফেলে Run চেপে দেখুন কভারেজ শতাংশ কীভাবে বদলায়।কভারেজ
100%থেকে75%-এ নেমে যাবে (৪টির মধ্যে ৩টি ট্রানজিশন কভার হবে), কারণplaced→cancelledপথটি আর কোনো অর্ডার দিয়ে যাচাই হবে না —o3-এরshipped→cancelledপথটি অবশ্য থেকে যাবে।
আরও পড়ুন · ABCL TECH-এ আপনার পরবর্তী পদক্ষেপ
- পরের পাঠ: ইউজ কেস ও সিনারিও-ভিত্তিক টেস্টিং L09 একক ট্রানজিশনের বদলে একটি ব্যবহারকারীর সম্পূর্ণ যাত্রা — শুরু থেকে শেষ পর্যন্ত — কীভাবে একটি সিনারিও হিসেবে টেস্ট করা হয়।
- কোর্সের সম্পূর্ণ সিলেবাস দেখুন ৫৭টি পাঠ টেস্ট ডিজাইন টেকনিক, ইউনিট টেস্টিং, টেস্ট ডাবলস, ইন্টিগ্রেশন টেস্টিং, কভারেজ, অটোমেশন, পারফরম্যান্স ও সিকিউরিটি টেস্টিং, অ্যাডভান্সড টেকনিক ও CI/CD ইন্টিগ্রেশন।
-
Mobile App Development-এ App Lifecycle স্টেট মেশিন সহোদর কোর্স
একই ধরনের
ALLOWEDট্রানজিশন ডিকশনারি প্যাটার্ন সেই কোর্সে অ্যাপ লাইফসাইকেল মডেল করতে ব্যবহৃত হয়েছে।