یک مسیر پرداخت را تصور کنید که تست‌های دستی آن همگی سبز هستند: سفارش ساخته می‌شود، کاربر به درگاه می‌رود، Callback موفق می‌رسد و سفارش «پرداخت‌شده» می‌شود. اما در تولید، Callback تکراری بعد از لغو سفارش می‌رسد؛ موجودی دوبار تغییر می‌کند و تیم نمی‌تواند دقیقاً بگوید کدام توالی رویداد از قلم افتاده است. مشکل کمبود یک Test Case دیگر نیست؛ مشکل این است که فضای رفتار سیستم جایی مدل نشده است.

تست مبتنی بر مدل یا Model-Based Testing (MBT) این فضا را به یک مدل قابل‌اجرا تبدیل می‌کند و از روی آن، مسیر تست، داده، انتظار و شواهد تولید می‌کند. ارزش MBT در «کشیدن نمودار» نیست؛ در زنجیره‌ای است که از هدف تست آغاز می‌شود و به رأی قابل‌تکرار دربارهٔ محصول می‌رسد.

پاسخ کوتاه: در MBT ابتدا رفتار مرتبط با ریسک را با حالت، رویداد، Guard، داده و Invariant مدل می‌کنیم؛ سپس یک معیار انتخاب مثل پوشش تمام Transitionها می‌دهیم، مولد مسیرهای انتزاعی را می‌سازد، Adapter آن‌ها را روی سیستم واقعی اجرا می‌کند و Oracle تفاوت رفتار واقعی و مدل را تشخیص می‌دهد. اگر فقط نمودار ساخته‌اید یا Test Caseها را دستی از آن خوانده‌اید، از مدل کمک گرفته‌اید؛ اما هنوز یک خط لولهٔ کامل MBT ندارید.

تست مبتنی بر مدل (MBT) دقیقاً چیست؟

مدل در MBT یک نمایش انتزاعی و هدفمند از رفتار مورد انتظار System Under Test یا SUT است. «انتزاعی» یعنی همهٔ جزئیات محصول را کپی نمی‌کند؛ فقط آن بخش از حالت، قانون و تعامل را نگه می‌دارد که برای هدف تست لازم است. «هدفمند» یعنی مدل بدون سؤال و ریسک مشخص، به‌سرعت به یک نسخهٔ دوم و پرهزینه از کد تولید تبدیل می‌شود.

در تعریف عملی، MBT فرایندی برای طراحی مدل، انتخاب تست از مدل، تولید Testware، انطباق تست انتزاعی با SUT، اجرا و تحلیل بازخورد است. صفحهٔ رسمی CT-MBT مؤسسه ISTQB نیز مدل‌سازی، معیار انتخاب، پیاده‌سازی/اجرا و ارزیابی استقرار را اجزای جداگانهٔ این رویکرد می‌داند. بنابراین ابزار تولید مسیر تنها یک جزء از MBT است، نه خود آن.

زنجیرهٔ ارزش MBT

  1. هدف و ریسک: قرار است دربارهٔ کدام رفتار، قانون یا شکست شواهد بسازیم؟
  2. مدل: کدام حالت‌ها، عمل‌ها، شرط‌ها، داده‌ها و انتظارها برای پاسخ کافی‌اند؟
  3. معیار انتخاب: چه مقدار و کدام بخش مدل باید پیمایش شود؟
  4. مولد: چه مسیرهایی با چه ترتیب و بودجه‌ای تولید شوند؟
  5. Concretization و Adapter: یک عمل انتزاعی چگونه به API، پیام، کلیک یا دادهٔ واقعی تبدیل شود؟
  6. Oracle: نتیجهٔ درست را چگونه، مستقل و قابل‌اعتماد تشخیص دهیم؟
  7. اجرا و شواهد: نسخهٔ مدل، Seed، مسیر، ورودی، خروجی و Trace چگونه ثبت شوند؟
  8. بازخورد: شکست از محصول است، مدل، Adapter، محیط یا Oracle؟ مدل چگونه اصلاح شود؟

پیش‌نویس ISO/IEC/IEEE 29119-8 در زمان به‌روزرسانی این مقاله هنوز «در دست تدوین» است، نه یک استاندارد منتشرشده. این منبع نیز تولید خودکار Testware را محور می‌داند و فرض می‌کند اجرا خودکار است؛ اما الگوریتم تولید و انتخاب ابزار را وابسته به ابزار و بیرون از دامنهٔ سند اعلام می‌کند. این وضعیت را باید دقیق گزارش کرد، نه اینکه پیش‌نویس را به‌عنوان استاندارد نهایی معرفی کنیم.

MBT با چه چیزهایی اشتباه گرفته می‌شود؟

رویکرد مالک چه مسئله‌ای است؟ تفاوت با MBT
تست انتقال حالت طراحی تست از State، Event و Transition یک تکنیک طراحی است و می‌تواند منبع مدل MBT باشد؛ اما تولید، Adapter، اجرای خودکار و حلقهٔ بازخورد را الزام نمی‌کند. برای عمق این تکنیک، راهنمای تست انتقال حالت را ببینید.
جدول تصمیم ترکیب شرط‌ها و نتیجهٔ قواعد مدل قاعده‌محور می‌تواند ورودی MBT باشد. روش ساخت قواعد در مقالهٔ تست جدول تصمیم جداگانه پوشش داده شده است.
گراف علت و معلول رابطهٔ منطقی Causeها و Effectها برای ساخت مدل منطقی مفید است، اما به‌تنهایی مسیر اجرایی تولید نمی‌کند. آموزش Cause-Effect Graph این مرز را با مثال توضیح می‌دهد.
Property-Based Testing تولید داده/دنباله برای شکستن یک ویژگی عمومی هم‌پوشانی دارد، به‌ویژه در Stateful Testing؛ ولی الزاماً از یک مدل رفتاری مستقل و معیار پوشش مدل استفاده نمی‌کند.
اتوماسیون UI/API اجرای تکرارپذیر اسکریپت‌های از قبل نوشته‌شده Runner مسیرهای ثابت را اجرا می‌کند؛ در MBT مسیرها از مدل و معیار انتخاب مشتق می‌شوند.
Model-Driven Development تولید یا طراحی کد محصول از مدل هدف مدل ساخت محصول است؛ در MBT هدف مدل تولید شواهد تست و مقایسهٔ محصول با انتظار است.
ضبط و بازپخش ثبت رفتار یک کاربر و تکرار همان مسیر رفتار موجود را ضبط می‌کند و معمولاً فضای مسیرهای ممکن، Guardها و پوشش را مدل نمی‌کند.

چه زمانی تست مبتنی بر مدل انتخاب خوبی است؟

MBT برای هر پروژه‌ای اقتصادی نیست. هرچه رفتار سیستم توالی‌محورتر، قانون‌مندتر و پرریسک‌تر باشد، احتمال بازگشت سرمایه بیشتر می‌شود. از این پرسش‌ها برای تصمیم اولیه استفاده کنید:

  • آیا نتیجه به تاریخچه و ترتیب عمل‌ها وابسته است، نه فقط آخرین ورودی؟
  • آیا تعداد مسیرهای معتبر/نامعتبر از توان طراحی دستی فراتر می‌رود؟
  • آیا قواعد یا Workflow مرتب تغییر می‌کنند و Test Caseهای دستی هم‌زمان به‌روز نمی‌شوند؟
  • آیا شکست‌های توالی، دوباره‌کاری، Race، Retry یا Timeout خسارت مالی/عملیاتی دارند؟
  • آیا سیستم نقطهٔ کنترل، Reset و مشاهدهٔ کافی برای اجرای تکرارپذیر دارد؟
  • آیا تیم می‌تواند Oracle مستقلی بسازد و مدل را همراه محصول نگهداری کند؟

پرداخت، سفارش، احراز هویت، پروتکل، دستگاه متصل، Workflow سازمانی و APIهای Stateful نامزدهای طبیعی‌اند. یک صفحهٔ ثابت با دو فیلد و چند Boundary محدود احتمالاً با تکنیک‌های ساده‌تر بهتر پوشش داده می‌شود. برای انتخاب بر اساس پیامد، نه جذابیت ابزار، از تست مبتنی بر ریسک کمک بگیرید.

نشانه‌های «فعلاً MBT نکنید»

  • هدف تست مبهم است و تیم می‌خواهد «کل محصول» را در یک مدل جا دهد.
  • رفتار مطلوب هنوز بین Product، توسعه و عملیات توافق نشده است.
  • محیط قابل Reset نیست، Run ID ندارید و نتیجه‌ها به دادهٔ Run قبلی آلوده می‌شوند.
  • هیچ منبع مستقلی برای انتظار وجود ندارد و مدل صرفاً منطق همان کد تولید را تکرار می‌کند.
  • تغییرات سریع‌اند، ولی مالک و بودجهٔ نگهداری مدل تعریف نشده است.

این موارد ممنوعیت دائمی نیستند؛ نشان می‌دهند ابتدا باید Design for Testability را اصلاح کنید. چارچوب تست‌پذیری نرم‌افزار برای تعریف Control، Observe، Oracle، Reset و Diagnose نقطهٔ شروع مناسبی است.

گام صفر: هدف تست و مرز مدل را بنویسید

پیش از انتخاب ابزار، یک Model Charter یک‌صفحه‌ای بسازید:

فیلد نمونه برای پرداخت
سؤال آیا هر سفارش با وجود Retry، Timeout و Callback تکراری حداکثر یک ثبت مالی موفق دارد؟
ریسک دو بار ثبت بدهی/بستانکاری یا اختلاف وضعیت سفارش و دفترکل
داخل مدل چرخهٔ پرداخت، Callback، لغو، بازپرداخت، شناسهٔ یکتای رویداد
خارج مدل رندر پیکسل، رتبه‌بندی محصول، عملیات انبار
سطح انتزاع API و Event؛ نه جزئیات DOM یا کلاس‌های داخلی
Oracle وضعیت سفارش + سطرهای دفترکل + Outbox + پاسخ API
معیار انتخاب تمام Transitionهای پرریسک + Transition Pairهای Callback/Cancel/Retry
بودجه PR حداکثر ۸ دقیقه؛ Nightly حداکثر ۶۰ دقیقه
مالک QA مدل، Product قواعد، توسعه Adapter/Seam و مالی Oracle دفترکل

سطح انتزاع باید آن‌قدر بالا باشد که با Refactor داخلی نشکند و آن‌قدر دقیق باشد که Oracle و مسیر اجرایی بسازد. نام حالت‌هایی مثل SCREEN_2 یا METHOD_X_CALLED معمولاً به UI/پیاده‌سازی چسبیده‌اند؛ نام‌هایی مثل PAYMENT_PENDING و REFUNDED زبان دامنه را حفظ می‌کنند.

مثال عملی MBT: مدل پرداخت و بازپرداخت ایرانی

فرض کنید مبلغ پایه در دامنه ریال است، UI آن را به تومان نمایش می‌دهد و PSP با Callback غیرهمگام نتیجه را اعلام می‌کند. حالات اصلی چنین‌اند:

  • CREATED: سفارش ساخته شده، پرداختی آغاز نشده است.
  • PAYMENT_PENDING: درخواست به PSP ثبت شده و پاسخ قطعی نداریم.
  • PAID: تراکنش Verify شده و دفترکل یک ثبت موفق دارد.
  • CANCELLED: سفارش پیش از Commit مالی لغو شده است.
  • REFUND_PENDING: بازپرداخت پذیرفته شده اما قطعی نیست.
  • REFUNDED: بازپرداخت در PSP و دفترکل قطعی شده است.

Transitionها، Guardها و انتظارها

مبدأ رویداد Guard مقصد انتظار قابل‌بررسی
CREATED startPayment amount_rial > 0 PAYMENT_PENDING Payment Attempt با order_id یکتا ساخته شود.
PAYMENT_PENDING callbackSuccess signatureValid && amountMatches PAID Verify موفق؛ دقیقاً یک Ledger Entry و یک Outbox Event.
PAYMENT_PENDING callbackFailure attempt شناخته‌شده CREATED ثبت مالی صفر؛ امکان تلاش جدید طبق Policy.
PAYMENT_PENDING cancel commit نشده CANCELLED لغو ثبت شود؛ Callback دیرهنگام حق Commit دوم ندارد.
PAID callbackSuccessDuplicate callback_id تکراری PAID پاسخ Idempotent؛ تعداد Ledger Entry ثابت بماند.
PAID requestRefund refundable_amount > 0 REFUND_PENDING Refund Command یکتا و قابل ردیابی ساخته شود.
REFUND_PENDING refundConfirmed PSP reference معتبر REFUNDED خالص دفترکل صفر و وضعیت سفارش سازگار باشد.
REFUNDED requestRefund مبلغ قابل‌استرداد صفر REFUNDED رد دامنه‌ای؛ هیچ Side Effect جدیدی ایجاد نشود.

Transition نامعتبر هم بخشی از مدل است. مثلاً requestRefund در CREATED باید با خطای دامنه‌ای پایدار رد شود؛ اما نباید حالت را تغییر دهد. اگر فقط مسیرهای معتبر مدل شوند، سیستم در برابر عمل‌های نابجا شواهدی نخواهد داشت.

متغیرهای مدل و Invariantها

مدل صرفاً چند دایره و فلش نیست. برای رفتار مالی، متغیرهایی مانند attempt_id، callback_ids_seen، amount_rial، refunded_rial و ledger_entry_count لازم‌اند. سپس قواعدی تعریف می‌کنیم که پس از هر گام باید برقرار بمانند:

  • برای هر attempt_id حداکثر یک ثبت مالی موفق وجود دارد.
  • refunded_rial هیچ‌گاه از paid_rial بیشتر نیست.
  • نمایش تومان فقط تبدیل نمایشی است؛ جمع و ذخیره با واحد canonical ریال انجام می‌شود.
  • Callback با امضای نامعتبر هیچ تغییر حالت یا اثر مالی ندارد.
  • در REFUNDED، وضعیت سفارش، دفترکل و سابقهٔ Refund باید با یکدیگر سازگار باشند.

Invariant سراسری جلوی این اشتباه را می‌گیرد که فقط خروجی آخر هر Transition بررسی شود. ممکن است API پاسخ ۲۰۰ بدهد و وضعیت درست به نظر برسد، اما Outbox دوبار پیام فرستاده باشد؛ Invariant اثر جانبی را نیز می‌بیند.

مدل را پیش از تست محصول، اعتبارسنجی کنید

مدل یک فرضیهٔ رسمی‌تر دربارهٔ محصول است و خودش هم می‌تواند غلط باشد. ابتدا آن را با Product Owner، توسعه، عملیات و صاحب دامنه مرور کنید. سپس این کنترل‌ها را اجرا کنید:

  • Reachability: آیا حالت یا Transition دست‌نیافتنی وجود دارد؟
  • Dead end: آیا سیستم ناخواسته در حالتی بدون خروج گیر می‌کند؟
  • Guard consistency: آیا Guardها هم‌پوشان، متناقض یا فاقد پوشش‌اند؟
  • Determinism policy: اگر برای یک ورودی چند خروجی ممکن است، آیا این عدم‌قطعیت آگاهانه مدل شده است؟
  • Domain review: واحد پول، زمان، Retry، Cancel و مسئولیت PSP درست فهمیده شده‌اند؟
  • Simulation: آیا نمونه‌مسیرهای واقعی و شکست‌خوردهٔ تولید روی مدل قابل بازپخش‌اند؟

ابزار متن‌باز GraphWalker مدل را به Vertex و Edge تقسیم می‌کند؛ Edge عمل/Transition و Vertex محل Verification است، سپس با Generator و Stop Condition مسیر می‌سازد. این قرارداد ساده برای فهم تفاوت «مدل»، «تولید مسیر» و «کد اجرا» مفید است.

معیار پوشش مدل را بر اساس ریسک انتخاب کنید

عبارت «پوشش کامل MBT» معنای دقیقی ندارد مگر مخرج و معیار را نام ببرید. معیارهای رایج:

معیار چه چیزی را تضمین می‌کند؟ چه چیزی را تضمین نمی‌کند؟
All States هر حالت مدل حداقل یک‌بار دیده شود. همهٔ راه‌های ورود/خروج یا Transitionها را پوشش نمی‌دهد.
All Transitions/Edges هر Transition مدل حداقل یک‌بار اجرا شود. ترتیب‌های حساس دو یا چند Transition را تضمین نمی‌کند.
Transition Pairs هر جفت Transition متوالیِ feasible پیمایش شود. تاریخچه‌های طولانی‌تر یا همهٔ داده‌ها را پوشش نمی‌دهد.
All Paths تا طول N مسیرهای feasible تا طول تعریف‌شده بررسی شوند. با Loop و داده به انفجار مسیر می‌رسد؛ «تمام مسیرها» عموماً متناهی نیست.
Requirement/Risk Coverage آیتم‌های دارای Trace در مدل انتخاب شوند. کیفیت Requirement، Trace و Oracle را تضمین نمی‌کند.
Weighted/Usage Coverage مسیرها بر اساس ریسک یا الگوی مصرف وزن بگیرند. مسیر کم‌وقوع اما فاجعه‌بار را بدون وزن ریسک حفظ نمی‌کند.

برای مثال پرداخت، All Transitions یک Baseline مناسب است؛ ولی ریسک Callback تکراری بعد از Cancel به یک Transition Pair یا مسیر هدفمند نیاز دارد. داده‌های مبلغ، رقم فارسی/عربی و مرزهای زمانی نیز باید با تکنیک‌های داده‌ای تکمیل شوند. MBT جایگزین Pairwise و پوشش ترکیب‌ها، Boundary Value یا تست اکتشافی نیست؛ مدل تعیین می‌کند این تکنیک‌ها کجا به مسیر تزریق شوند.

اصل مهم: ۱۰۰٪ پوشش مدل یعنی تمام عناصرِ تعریف‌شده طبق معیار انتخاب پیمایش شده‌اند؛ نه اینکه ۱۰۰٪ رفتار واقعی، کد، داده، وابستگی یا ریسک محصول پوشش دارد. مدل ناقص می‌تواند با پوشش ۱۰۰٪ کاملاً سبز باشد.

مولد مسیر و Stop Condition چگونه کار می‌کنند؟

Generator تصمیم می‌گیرد گام بعدی از میان Transitionهای مجاز چگونه انتخاب شود. Stop Condition می‌گوید تولید چه زمانی کافی است. چند الگوی متداول:

  • Shortest path: کوتاه‌ترین مسیر برای رسیدن به یک حالت/Transition هدف؛ سریع و مناسب تشخیص یا Smoke.
  • Random walk با Seed: تنوع مسیر ایجاد می‌کند؛ Seed باید ثبت شود تا شکست بازتولیدپذیر بماند.
  • Coverage-driven: عناصر پوشش‌نداده را ترجیح می‌دهد تا به معیار توقف برسد.
  • Weighted walk: وزن ریسک، مصرف یا تغییر را در انتخاب دخیل می‌کند.
  • Requirement-targeted: مسیرهایی می‌سازد که Requirement یا Risk ID مشخص را لمس کنند.

توقف می‌تواند «۱۰۰٪ Transitionهای feasible»، «پوشش ۹۰٪ وزن ریسک»، «حداکثر ۵۰۰ گام» یا ترکیبی از پوشش و زمان باشد. بودجهٔ بی‌نهایت نداریم؛ بنابراین بهتر است دو شرط داشته باشیم: هدف شواهد و سقف هزینه. اگر سقف زمان زودتر رسید، نتیجه «پوشش ناقص با دلیل» است، نه Pass پنهانی.

مسیر ناممکن و انفجار حالت

با افزودن داده، Guard و Loop، تعداد مسیرها به‌شدت رشد می‌کند. درمان، حذف کور رفتار نیست:

  • دامنه را به مدل‌های کوچک با Interface روشن تقسیم کنید.
  • داده را به کلاس‌های هم‌ارزی و مقادیر مرزی تقلیل دهید.
  • Guardهای واقعی را در مدل نگه دارید تا مسیر infeasible تولید نشود.
  • مسیرهای حیاتی را Seed/Target کنید و برای بقیه Sampling کنترل‌شده داشته باشید.
  • طول History را بر اساس Failure Mechanism تعیین کنید، نه یک عدد دلخواه.
  • State Explosion را با سنجهٔ تعداد حالت/Transition/مسیر و زمان تولید پایش کنید.

Offline یا Online؛ تست چه زمانی تولید شود؟

تولید Offline

مولد ابتدا مجموعه‌ای از مسیرها را می‌سازد و آن‌ها بعداً اجرا می‌شوند. مزیتش Review، نسخه‌بندی و تکرار دقیق Suite است. عیبش این است که تصمیم بعدی نمی‌تواند به وضعیت واقعی Run واکنش نشان دهد و Suiteهای تولیدشده ممکن است حجیم و قدیمی شوند.

تولید Online

پس از هر گام، نتیجه و حالت مشاهده می‌شود و مولد گام بعدی را انتخاب می‌کند. برای سیستم‌های غیرقطعی، کشف پویا و مسیرهای طولانی مناسب‌تر است؛ اما Reproducibility، Timeout، Recovery و ثبت Trace اهمیت بیشتری پیدا می‌کند.

مستندات AltWalker این تفکیک را عملی نشان می‌دهد: Planner مسیر را از مدل می‌گیرد، Executor متد متناظر با Vertex/Edge را اجرا می‌کند و Reporter رویدادها را ثبت می‌کند؛ ابزار از هر دو Online و Offline پشتیبانی می‌کند. معماری ابزار هرچه باشد، این نقش‌ها را در طراحی خود جدا نگه دارید.

از تست انتزاعی تا اجرای واقعی: Adapter و Concretization

مدل می‌گوید callbackSuccess؛ ولی SUT به URL، Header امضا، Payload، شناسهٔ تلاش و زمان نیاز دارد. Adapter این فاصله را پر می‌کند:

abstract action: callbackSuccess(attempt, amountClass)
concretize:
  attempt_id = context.current_attempt
  amount_rial = choose(amountClass)      // 1000, max-1, max
  callback_id = deterministic_uuid(seed, step)
  signature = psp_test_key.sign(payload)
execute:
  POST /callbacks/psp with Run-ID and payload
observe:
  response + order API + ledger rows + outbox rows

Adapter نباید قواعد دامنه را دوباره پیاده کند؛ وظیفه‌اش نگاشت نام‌ها، ساخت داده، فراخوانی Interface و جمع‌آوری Observation است. اگر همان فرمول تولید در Adapter برای محاسبهٔ انتظار کپی شود، یک خطای مشترک می‌تواند هم SUT و هم تست را سبز نگه دارد.

قرارداد خوب Adapter

  • هر Action انتزاعی یک نام دامنه‌ای و یک اثر مشخص دارد.
  • شناسهٔ Run و Step به درخواست، پیام و Log منتقل می‌شود.
  • انتخاب داده با Seed و نسخهٔ Dataset قابل تکرار است.
  • انتظار برای Async با Poll روی وضعیت معنادار انجام می‌شود، نه Sleep ثابت.
  • Reset و Cleanup صریح‌اند و شکست Cleanup به‌عنوان خطای محیط گزارش می‌شود.
  • Secret و دادهٔ شخصی در Trace و Report ماسک می‌شوند.

Oracle؛ مدل چگونه Pass و Fail را تشخیص می‌دهد؟

Oracle فقط یک Assertion روی Status Code نیست. در مثال مالی، چهار لایهٔ شواهد لازم است:

  1. Contract: Schema، کد پاسخ و خطای دامنه‌ای API.
  2. State: وضعیت سفارش و Payment Attempt با حالت مدل سازگار باشد.
  3. Side effect: تعداد و مبلغ Ledger/Outbox دقیق باشد.
  4. Invariant: قواعد سراسری مانند At-most-once و سقف Refund پس از هر گام برقرار بماند.

Oracle باید تا حد امکان از منبعی مستقل بیاید: قانون تاییدشدهٔ کسب‌وکار، Contract، حسابداری دوبل، Metamorphic relation یا پیاده‌سازی مرجع کوچک. Snapshot گرفتن از خروجی فعلی و اعلام آن به‌عنوان انتظار، رفتار موجود را تثبیت می‌کند؛ صحت را اثبات نمی‌کند.

در Stateful Property-Based Testing نیز همین ایده دیده می‌شود. مستندات رسمی Hypothesis Stateful Testing اجازه می‌دهد Actionها زنجیر شوند، Model سادهٔ درون حافظه با SUT مقایسه شود و Invariant بعد از Ruleها بررسی گردد. این گزینه برای API یا Library پایتون مفید است؛ اما انتخاب معیار پوشش مدل و Governance همچنان بر عهدهٔ تیم است.

یک مسیر تولیدشده چه شکلی دارد؟

با هدف «Transition Pairهای Cancel/Callback و پوشش Idempotency»، مولد ممکن است این Trace را بسازد:

  1. createOrder(amount=1_250_000 IRR) → حالت CREATED
  2. startPayment() → PAYMENT_PENDING و attempt=A17
  3. cancel() → CANCELLED؛ Ledger Entry = ۰
  4. callbackSuccess(A17, callback=C91) → Policy مورد انتظار: بدون Commit مالی
  5. callbackSuccess(A17, callback=C91) → پاسخ Idempotent؛ همچنان Ledger Entry = ۰
  6. بررسی Invariantها و ثبت Snapshot شواهد با Run ID

گزارش شکست باید فقط «Step ۴ failed» نباشد. نسخهٔ مدل، معیار، Generator، Seed، مسیر مینیمم، Guardهای فعال، ورودی Concrete، Observation، اختلاف Oracle، نسخهٔ SUT و لینک Trace لازم‌اند. در غیر این صورت تنوع مسیر به تنوع خطاهای غیرقابل‌بازتولید تبدیل می‌شود.

انتخاب ابزار MBT؛ ابتدا قابلیت، بعد نام

ابزار را با یک ماتریس Pilot ارزیابی کنید:

  • کدام زبان مدل را می‌پذیرد: Graph، FSM/EFSM، UML، DSL یا کد؟
  • معیارهای پوشش، Generator، Weight، Seed و Stop Condition قابل‌کنترل‌اند؟
  • Online/Offline، Shrinking یا کوچک‌سازی Trace شکست را پشتیبانی می‌کند؟
  • Adapter برای زبان و Interface تیم چقدر ساده است؟
  • Trace، Export، CI integration و Report ماشین‌خوان دارد؟
  • مدل بزرگ، Guard، داده و Parallel Run را با چه محدودیتی مدیریت می‌کند؟
  • پروژه فعال، مستند، دارای License مناسب و قابل نگهداری در ایران است؟
  • در صورت قطع سرویس ابری یا محدودیت دسترسی، مدل و شواهد قابل‌حمل می‌مانند؟

چند گزینه برای Pilot

  • GraphWalker: Graphمحور، دارای Generator/Stop Condition و مناسب اتصال به کد Java یا سرویس برنامه‌ریز.
  • AltWalker: اجرای مدل‌های GraphWalker با Executorهای Python و .NET و جداسازی Planner/Executor/Reporter.
  • Hypothesis RuleBasedStateMachine: مناسب تیم Python برای تولید دنباله، داده، Invariant و کوچک‌سازی مثال شکست.
  • Modbat: ابزار EFSM برای تست APIهای Java/Scala. صفحهٔ رسمی پروژهٔ Modbat در AIST نحوهٔ انتخاب Transition و گزارش Error Trace را شرح می‌دهد؛ وضعیت توسعه و License را هنگام Pilot دوباره بررسی کنید.

مقالهٔ آرشیوی Microsoft دربارهٔ Spec Explorer هنوز برای مفاهیم Test Sequence، Oracle و Offline generation آموزنده است، اما منبع انتخاب ابزار فعلی نیست: صفحه آرشیوی و ابزار قدیمی‌اند. ابزار را به‌دلیل شهرت تاریخی وارد معماری جدید نکنید.

MBT در CI/CD؛ یک Suite واحد نسازید

مدل واحد می‌تواند چند سیاست انتخاب داشته باشد:

Lane انتخاب هدف بودجهٔ نمونه
PR Shortest paths برای Transitionهای تغییرکرده + Smoke invariants بازخورد سریع تغییر ۵ تا ۱۰ دقیقه
Merge All Transitions در مدل‌های متاثر Baseline رفتاری ۲۰ دقیقه
Nightly Transition Pairs + Random/Weighted با Seedهای چرخشی کشف توالی و History ۶۰ تا ۱۲۰ دقیقه
Release Risk-targeted روی محیط با Fidelity بالاتر شواهد تصمیم انتشار طبق Risk Register
Diagnostic Replay کوتاه‌ترین Trace شکست بازتولید و رفع درخواست‌محور

جای MBT در سبد تست را با شمار Test Case تعیین نکنید. یک مسیر مدل ممکن است مرز Component، API و System را لمس کند. راهنمای هرم تست در عمل کمک می‌کند هر Failure Mechanism در کوچک‌ترین مرز مسئول و با Fidelity کافی بررسی شود؛ سیاست کلان Pilot تا Scale نیز در استراتژی اتوماسیون تست آمده است.

تغییر Requirement و نگهداری مدل

مزیت نگهداری MBT زمانی واقعی است که مدل Source of Truth قابل‌ردیابی باشد، نه یک فایل گرافیکی رهاشده:

  • هر State/Transition/Invariant مهم را به Requirement ID و Risk ID وصل کنید.
  • مدل، Adapter و Dataset را در Version Control و همان Pull Request تغییر دهید.
  • Diff مدل را مانند کد Review کنید: State جدید، Guard تغییرکرده و Transition حذف‌شده چه شواهدی را عوض می‌کند؟
  • مدل قدیمی را صرفاً برای سبز شدن با رفتار فعلی هماهنگ نکنید؛ ابتدا تغییر مورد انتظار را تایید کنید.
  • پس از تغییر مدل، تست‌ها را بازتولید و Impact را ثبت کنید؛ Suite تولیدشده را دستی Patch نکنید.
  • نسخهٔ مدل و Generator را در Report نگه دارید تا نتیجهٔ قدیمی قابل تفسیر باشد.

شکست MBT را درست طبقه‌بندی کنید

کلاس شکست نشانه مالک اقدام
Product defect SUT از رفتار تاییدشدهٔ مدل منحرف شده است. توسعه + Product/QA برای Severity
Model defect قاعده، Guard، حالت یا انتظار مدل غلط/ناقص است. مالک مدل + صاحب دامنه
Adapter defect نگاشت Action، داده یا Observation اشتباه است. توسعه‌دهندهٔ Harness
Oracle defect انتظار مستقل نیست، Precision کافی ندارد یا Race دارد. QA + صاحب داده/دامنه
Environment defect Reset، Dependency، Clock، Network یا Test Data ناپایدار است. Platform/Test Environment
Inconclusive شواهد برای رأی کافی نیست یا بودجه پیش از معیار تمام شده است. Test Lead برای تصمیم تکرار/ریسک

همهٔ شکست‌ها را Bug محصول نکنید و همه را هم «Flaky Test» ننامید. نرخ هر کلاس نشان می‌دهد سرمایه‌گذاری بعدی باید روی محصول، مدل، Harness یا محیط باشد.

سنجه‌های مفید و سنجه‌های فریبنده

سنجه‌های تصمیم‌ساز

  • Model coverage با مخرج: مثلاً ۴۲ از ۴۵ Transition feasible؛ سه مورد با دلیل و مالک.
  • Risk evidence coverage: چند ریسک هدف، شواهد معتبر Pass/Fail/Inconclusive دارند؟
  • Executable-path rate: چه سهمی از مسیرهای تولیدی بدون خطای مدل/Adapter اجرا می‌شوند؟
  • Time to reproduce: از کشف تا بازپخش Trace مینیمم چقدر زمان می‌برد؟
  • Change amplification: یک تغییر Requirement چند عنصر مدل/Adapter/Oracle را عوض می‌کند؟
  • Maintenance cost: زمان نگهداری مدل و Harness در هر Release.
  • Escaped sequence defects: شکست‌های توالی‌محوری که مدل پوشش نداده یا Oracle ندیده است.
  • Failure classification mix: نسبت Product/Model/Adapter/Oracle/Environment.

سنجه‌هایی که نباید تنها KPI باشند

  • تعداد Test Case تولیدشده؛ تولید هزار مسیر مشابه ارزش بیشتری نمی‌سازد.
  • ۱۰۰٪ پوشش مدل بدون کیفیت مدل و Risk Trace.
  • تعداد Bug بدون Severity، تازگی و هزینهٔ نگهداری.
  • نرخ Pass بدون نمایش Inconclusive، مسیرهای اجرا‌نشده و محیط.

برنامهٔ ۳۰روزه برای Pilot تست مبتنی بر مدل

هفتهٔ اول: یک ریسک و یک مرز

  • یک Failure Mechanism واقعی و پرتکرار/پرهزینه انتخاب کنید.
  • Model Charter، مالک‌ها، Oracle و بودجه را توافق کنید.
  • ۱۰ تا ۲۰ حالت/Transition کافی است؛ از مدل کل محصول پرهیز کنید.

هفتهٔ دوم: مدل و Review

  • State/Event/Guard/Action/Invariant را با زبان دامنه بنویسید.
  • یک مسیر موفق، یک مسیر شکست و یک Incident تولید را شبیه‌سازی کنید.
  • Reachability، Dead end و Guardها را بررسی و مدل را نسخه‌بندی کنید.

هفتهٔ سوم: Adapter، Oracle و اجرای محدود

  • دو یا سه Action حیاتی را به SUT وصل کنید.
  • Reset، Run ID، Seed و Observation مستقل را بسازید.
  • All Transitions یا هدف ریسک محدود را روی CI غیرمسدودکننده اجرا کنید.

هفتهٔ چهارم: ارزیابی اقتصادی

  • زمان مدل‌سازی، Adapter، نگهداری، اجرا و تحلیل را اندازه بگیرید.
  • شکست‌ها را طبقه‌بندی و Traceهای تکراری/بی‌ارزش را حذف کنید.
  • با معیارهای از پیش‌توافق‌شده تصمیم بگیرید: Scale، اصلاح Pilot یا توقف.

سیلابس رسمی CT-MBT نسخهٔ ۱.۱ علاوه بر مدل‌سازی و تولید، ارزیابی استقرار و عوامل ROI را نیز جزو مهارت‌های لازم می‌داند. پس موفقیت Pilot را با Demo جذاب ابزار نسنجید؛ با شواهد، هزینه و قابلیت نگهداری بسنجید.

خطاهای رایج در Model-Based Testing

  1. مدل‌کردن کل سیستم: مرز نامحدود، انفجار حالت و مالکیت مبهم می‌سازد.
  2. کپی معماری کد در مدل: Refactor تست را می‌شکند و استقلال Oracle را کم می‌کند.
  3. مدل Happy Path: Invalid transition، Retry، Timeout، Duplicate و Race حذف می‌شوند.
  4. پوشش بدون مخرج: درصد زیباست، اما معلوم نیست چه چیزی پوشش یافته است.
  5. Random بدون Seed: شکست کشف می‌شود ولی بازتولید نمی‌شود.
  6. Sleep ثابت: تست Async را هم کند و هم Flaky می‌کند.
  7. Oracle همسان با SUT: یک خطای منطق در هر دو نسخه تکرار می‌شود.
  8. ابهام Inconclusive: تمام‌شدن زمان یا خرابی محیط به‌اشتباه Pass/Fail محصول می‌شود.
  9. انباشت Suite تولیدشده: خروجی Offline دستی ویرایش می‌شود و از مدل جدا می‌افتد.
  10. خرید ابزار پیش از Pilot: محدودیت Adapter، License، دسترسی و نگهداری دیر کشف می‌شود.
  11. حذف تست‌های مکمل: مدل رفتاری جای امنیت، کارایی، دسترس‌پذیری یا اکتشاف را می‌گیرد.
  12. بی‌مالک ماندن مدل: نمودار پس از نخستین تغییر Requirement منسوخ می‌شود.

چک‌لیست کیفیت یک پیاده‌سازی MBT

  • هدف، ریسک، مرز و Non-goal مدل مکتوب است.
  • هر State و Transition معنای دامنه‌ای و قابل Review دارد.
  • Guard، داده، Invalid action و Invariantهای حیاتی مدل شده‌اند.
  • مدل با Incident واقعی و نمونه‌مسیرهای دامنه اعتبارسنجی شده است.
  • معیار انتخاب، مخرج پوشش، Generator، Seed، Stop Condition و سقف زمان ثبت می‌شوند.
  • Adapter فقط نگاشت/اجرا می‌کند و منطق Oracle را کپی نمی‌کند.
  • Reset، Run ID، Clock، داده و Async synchronization کنترل‌پذیرند.
  • Oracle چندلایه، مستقل و قادر به دیدن Side Effect است.
  • گزارش شامل نسخهٔ مدل/SUT، Trace مینیمم، داده و Observation است.
  • Failهای محصول از Model/Adapter/Oracle/Environment جدا می‌شوند.
  • مدل، Adapter و Requirement Trace در Version Control مالک دارند.
  • تصمیم Scale بر اساس ریسک، Evidence و هزینهٔ نگهداری گرفته می‌شود.

پرسش‌های متداول دربارهٔ تست مبتنی بر مدل

آیا MBT یعنی تمام تست‌ها خودکار تولید می‌شوند؟

خیر. درجهٔ خودکارسازی متفاوت است: ممکن است مدل فقط Test Design را پشتیبانی کند، مسیر انتزاعی تولید شود ولی اجرای آن دستی باشد، یا تولید و اجرا هر دو خودکار باشند. برای یک خط لولهٔ عملی، تولید مسیر، Concretization، Adapter، Oracle و Report همگی باید تعریف شوند؛ صرف رسم مدل تولید خودکار تست نیست.

بهترین مدل برای شروع MBT چیست؟

مدلی که با Failure Mechanism شما سازگار باشد. Workflow و State Machine برای رفتارهای توالی‌محور، Decision Table برای قواعد ترکیبی و مدل داده/Property برای قیود مناسب‌اند. برای Pilot، یک FSM/EFSM کوچک با ۱۰ تا ۲۰ Transition، Oracle روشن و یک ریسک واقعی معمولاً از UML بزرگ سازمانی بهتر است.

آیا ۱۰۰٪ پوشش Transition برای پایان تست کافی است؟

نه. این معیار فقط می‌گوید هر Transition تعریف‌شده دست‌کم یک‌بار پیمایش شده است. خطا ممکن است در Transition Pair، دادهٔ مرزی، Guard ناقص، وابستگی، Race یا رفتاری باشد که اصلاً در مدل نیامده است. پوشش را همراه Risk Trace، کیفیت مدل، Oracle و تست‌های مکمل تفسیر کنید.

MBT برای تیم بدون برنامه‌نویس تست قابل‌استفاده است؟

مدل‌سازی می‌تواند Low-code یا بصری باشد، اما اتصال مطمئن به SUT، مدیریت داده/محیط، Oracle و CI معمولاً مهارت فنی می‌خواهد. تیم میان‌رشته‌ای بهتر عمل می‌کند: صاحب دامنه قواعد را تایید می‌کند، QA مدل و معیار را می‌سازد و توسعه/Platform تست‌پذیری و Adapter را فراهم می‌کنند.

از کجا بفهمیم MBT بازگشت سرمایه دارد؟

پیش از Pilot Baseline بگیرید: زمان طراحی و نگهداری تست، نقص‌های توالیِ گریخته، زمان بازتولید و هزینهٔ اجرای فعلی. سپس همان سنجه‌ها را همراه هزینهٔ مدل/Adapter، نرخ مسیر اجرایی، Evidence پوشش ریسک و تغییرات نگهداری مقایسه کنید. تعداد تست تولیدشده یا درصد پوشش مدل به‌تنهایی ROI را نشان نمی‌دهد.

جمع‌بندی

تست مبتنی بر مدل یک میان‌بر جادویی برای تولید هزاران Test Case نیست. MBT خوب یک زنجیرهٔ مهندسی قابل ممیزی است: ریسک و سؤال → مدل معتبر → معیار انتخاب → مسیر → Adapter → Oracle مستقل → اجرای تکرارپذیر → شواهد و یادگیری. اگر هر حلقه مبهم باشد، ابزار فقط ابهام را با سرعت بیشتری تکثیر می‌کند.

برای شروع، یک Incident توالی‌محور واقعی را انتخاب کنید، مرز کوچک بسازید، Invalid action و Invariant را جدی بگیرید، پوشش مدل را با پوشش محصول یکی ندانید و Pilot را با هزینهٔ نگهداری و کیفیت تصمیم ارزیابی کنید. آن‌وقت مدل از یک نمودار تزئینی به دارایی زندهٔ تضمین کیفیت تبدیل می‌شود.

دیدگاهتان را بنویسید