رفتن به محتوای اصلی

تست جریان داده چیست؟ آموزش DU Pair و Def-Use

تست جریان داده را با مثال بازپرداخت یاد بگیرید؛ از ساخت CFG و DU Pair تا All-Uses، مسیر Def-clear، طراحی تست و محدودیت ابزارها.

محمد عسکری ۱۷ دقیقه مطالعه به‌روزرسانی
فهرست مطالب
  1. تست جریان داده چیست؟
  2. قصد جست‌وجوی این مقاله
  3. DFT با چه چیزهایی اشتباه می‌شود؟
  4. تحلیل جریان داده با تست جریان داده یکسان نیست
  5. یک جریان کاری ترکیبی و قابل اتکا
  6. واژگان پایه: Def، Use و Kill
  7. تعریف یا Definition
  8. استفادهٔ محاسباتی یا c-use
  9. استفادهٔ شرطی یا p-use
  10. Kill یا پایان اعتبار
  11. DU Pair، DU Path و Def-clear Path
  12. Def-clear Path چیست؟
  13. DU Pair چیست؟
  14. DU Path چیست؟
  15. مثال عملی: تابع بازپرداخت با کارمزد
  16. گراف جریان کنترل تابع
  17. جفت‌های DU اصلی
  18. حداقل تست‌های هدفمند مثال
  19. چرا Branch Coverage کافی نیست؟
  20. پوشش بیشتر، تضمین صحت نیست
  21. معیارهای پوشش جریان داده
  22. All-Defs Coverage
  23. All-c-Uses و All-p-Uses
  24. All-Uses Coverage
  25. All-DU-Paths Coverage
  26. فرمول گزارش پوشش
  27. ناهنجاری‌های جریان داده را دقیق نام‌گذاری کنید
  28. روش انجام تست جریان داده، قدم‌به‌قدم
  29. ۱. دامنه و ریسک را محدود کنید
  30. ۲. گراف جریان کنترل بسازید
  31. ۳. Def و Use را برچسب بزنید
  32. ۴. Reaching Definition و مسیر Def-clear را محاسبه کنید
  33. ۵. معیار پوشش را براساس ریسک انتخاب کنید
  34. ۶. تست کیس و Oracle بنویسید
  35. ۷. اجرا، Trace و بازبینی کنید
  36. پیاده‌سازی تست‌های مثال با Vitest
  37. چه تست‌های دیگری برای تابع لازم است؟
  38. سناریوی ایرانی: قیمت، ریال و تومان
  39. محدودیت‌های واقعی DFT
  40. مسیرهای ناممکن
  41. Alias و Pointer
  42. آرایه، Collection و Field Sensitivity
  43. Async و Concurrency
  44. هزینهٔ نگهداری
  45. انتخاب ابزار تست و تحلیل جریان داده
  46. چک‌لیست ارزیابی ابزار
  47. ضدالگوهای رایج
  48. الگوی گزارش یک DU Obligation
  49. چک‌لیست نهایی برای بازبینی
  50. منابع معتبر برای مطالعه بیشتر
  51. جمع‌بندی
  52. سوالات متداول درباره تست جریان داده
  53. ۱. تفاوت Data Flow Testing و Data Flow Analysis چیست؟
  54. ۲. DU Pair و DU Path چه تفاوتی دارند؟
  55. ۳. آیا Branch Coverage صددرصد، All-Uses را تضمین می‌کند؟
  56. ۴. آیا باید همیشه All-DU-Paths را هدف بگیریم؟
  57. ۵. آیا ابزارهای تحلیل استاتیک تست جریان داده را خودکار می‌کنند؟
تست جریان داده چیست؟ آموزش DU Pair و Def-Use

فرض کنید تابع بازپرداخت یک فروشگاه اینترنتی در همه تست‌های شاخه‌ای سبز است، اما وقتی کارمزد غیرفعال و بازپرداخت تأیید می‌شود، مقدار نهایی NaN برمی‌گردد. هر دو شاخهٔ if اجرا شده‌اند؛ پس مشکل کجاست؟ یک مقدار اولیه در مسیری خاص هرگز به محل مصرف درست نرسیده است. این همان شکافی است که تست جریان داده با دنبال‌کردن رابطهٔ «تعریف تا استفاده» آشکار می‌کند.

در این راهنما، Data Flow Testing یا DFT را از تحلیل جریان داده، تست مسیر، تست پایگاه داده و Data-driven Testing جدا می‌کنیم؛ سپس با یک مثال TypeScript، گراف جریان کنترل، مجموعه‌های Def/Use، جفت‌های DU، مسیرهای Def-clear و تست‌های لازم را قدم‌به‌قدم محاسبه می‌کنیم. هدف این است که پس از مطالعه بتوانید برای یک تابع واقعی معیار پوشش جریان داده تعریف کنید، نه اینکه فقط چند اصطلاح تئوری را به خاطر بسپارید.

پاسخ کوتاه: تست جریان داده یک تکنیک ساختاری و پویاست که تست‌ها را طوری انتخاب می‌کند تا تعریف‌های متغیرها به استفاده‌های قابل‌دسترسی آن‌ها در مسیرهای بدون تعریف مجدد برسند. واحد اصلی برنامه‌ریزی آن معمولاً Def‑Use Pair است و معیارهایی مانند All‑Defs، All‑Uses و All‑DU‑Paths شدت پوشش را تعیین می‌کنند.

تست جریان داده چیست؟

تست جریان داده (Data Flow Testing) نوعی تست جعبه سفید و پوشش کد است که رفتار متغیرها را روی گراف جریان کنترل بررسی می‌کند. به‌جای اینکه فقط بپرسیم «کدام دستور یا شاخه اجرا شد؟»، می‌پرسیم:

  • این مقدار در کجا تولید یا بازتعریف شد؟
  • کدام استفاده‌ها می‌توانند به این تعریف متصل شوند؟
  • آیا بین تعریف و استفاده، مقدار دوباره نوشته شده است؟
  • کدام ورودی، مسیر تعریف تا استفاده را واقعاً اجرا می‌کند؟
  • آیا خروجی یا اثر جانبی، یک Oracle قابل‌اعتماد برای صحت مقدار دارد؟

بنابراین DFT صرفاً رسم فلش میان متغیرها نیست. نتیجهٔ تحلیل باید به ورودی تست، خروجی مورد انتظار و شواهد اجرای یک تعهد پوشش تبدیل شود.

قصد جست‌وجوی این مقاله

کلیدواژهٔ اصلی «تست جریان داده» است. عبارت‌های مکمل شامل «Data Flow Testing»، «Def Use»، «DU Pair»، «DU Path»، «Def-clear Path»، «All Uses Coverage»، «ناهنجاری جریان داده» و «پوشش جریان داده» هستند. این راهنما برای تستر فنی، توسعه‌دهنده، مهندس SDET و رهبر QA نوشته شده که به مثال اجرایی و روش اندازه‌گیری نیاز دارد.

DFT با چه چیزهایی اشتباه می‌شود؟

  • Data Flow Diagram: نمودار جریان داده در تحلیل سیستم، جابه‌جایی داده میان فرایندها و مخازن را مدل می‌کند؛ الزاماً مسیرهای کد و Def‑Use را نمی‌سنجد.
  • Data-driven Testing: یک سناریوی تست را با چند مجموعه داده اجرا می‌کند؛ ممکن است هیچ معیار Def‑Use نداشته باشد.
  • تست پایگاه داده: صحت Schema، Query، تراکنش، Migration و یکپارچگی را می‌سنجد. برای آن، راهنمای تست پایگاه داده مرجع مرتبط‌تری است.
  • تست خط لوله داده یا ETL: کیفیت و تبدیل داده میان سامانه‌ها را بررسی می‌کند؛ DFT این مقاله روی جریان مقدار در ساختار برنامه متمرکز است.

تحلیل جریان داده با تست جریان داده یکسان نیست

این تمایز، مهم‌ترین اصلاح مفهومی مقاله است. تحلیل و تست ایستا بدون اجرای نرم‌افزار، مسیرهای ممکن و توالی تعریف، استفاده و نابودی متغیر را بررسی می‌کند و هشدار می‌دهد. در مقابل، تست جریان داده پویا است: تست کیس تولید و اجرا می‌شود تا یک Definition‑Use Pair در کد واقعاً پیموده شود.

سرفصل رسمی Technical Test Analyst از ISTQB نیز همین مرز را صریح بیان می‌کند: Data Flow Analysis ایستا است، اما Data Flow Testing تست پویا برای اجرای Definition‑Use Pairهاست. پس خروجی ابزار تحلیل ایستا می‌تواند ورودی طراحی DFT باشد، ولی گزارش هشدار به‌تنهایی مدرک پوشش پویا نیست.

یک جریان کاری ترکیبی و قابل اتکا

  1. Compiler، Linter یا SAST نقاط مشکوک را بدون اجرا پیدا می‌کند.
  2. تیم مسیرهای شدنی، موارد عمدی و هشدارهای کاذب را بازبینی می‌کند.
  3. برای Def‑Useهای پرریسک، تست با ورودی و Oracle مشخص طراحی می‌شود.
  4. اجرای تست یا Instrumentation نشان می‌دهد کدام DU Pair پیموده شده است.
  5. نتیجه کنار ریسک و نسخهٔ کد ثبت می‌شود تا در تغییرات بعدی قابل تکرار باشد.

واژگان پایه: Def، Use و Kill

تعریف یا Definition

Def نقطه‌ای است که متغیر مقدار می‌گیرد یا مقدار تازه‌ای جای مقدار قبلی می‌نشیند؛ مانند x = 5، دریافت پارامتر تابع یا خواندن داده در متغیر. Declaration همیشه معادل Definition نیست. برای مثال let fee: number; نام را اعلام می‌کند، اما تا قبل از مقداردهی یک تعریف قابل‌استفاده ایجاد نکرده است.

استفادهٔ محاسباتی یا c-use

Computational Use زمانی است که مقدار در محاسبه، سمت راست انتساب، فراخوانی تابع، خروجی یا Return مصرف می‌شود. در total = price - discount، متغیرهای price و discount استفادهٔ محاسباتی دارند.

استفادهٔ شرطی یا p-use

Predicate Use مصرف مقدار در تصمیمی مانند if یا while است. در نمایش دقیق CFG، برخی منابع p-use را روی یال درست/نادرست برچسب می‌زنند، نه روی خود گره تصمیم. در این مقاله برای خوانایی، نام گره تصمیم را می‌آوریم و هنگام محاسبه هر دو یال را جدا نگه می‌داریم.

Kill یا پایان اعتبار

Kill یعنی مقدار دیگر معتبر یا در دسترس نیست؛ برای نمونه خروج از Scope، آزادشدن حافظه یا بسته‌شدن منبع. معنای Kill به زبان و مدل حافظه وابسته است. در JavaScript جمع‌آوری حافظه خودکار است، اما بسته‌شدن Stream یا پایان Scope هنوز می‌تواند برای تحلیل چرخهٔ حیات مهم باشد.

DU Pair، DU Path و Def-clear Path

Def-clear Path چیست؟

مسیر تعریف تا استفاده برای متغیر v زمانی Def-clear است که بعد از Definition آغازین و پیش از Use هدف، هیچ Definition دیگری از v رخ ندهد. اگر مقدار در میانه دوباره نوشته شود، استفادهٔ انتهایی به تعریف اول وابسته نیست.

DU Pair چیست؟

جفت تعریف–استفاده دو نقطهٔ مرتبط برای یک متغیر است؛ مثلاً (fee, N1, N6). در قرارداد عملی این راهنما، جفت فقط زمانی یک تعهد معتبر است که دست‌کم یک مسیر شدنی و Def-clear میان دو نقطه وجود داشته باشد. در بعضی متون، DU Pair فقط دو انتهای قابل‌دسترسی را نام‌گذاری می‌کند و Def-clear بودن به خود DU Path نسبت داده می‌شود؛ مهم این است که قرارداد ابزار و تیم در آغاز کار ثبت شود.

DU Path چیست؟

مسیر تعریف–استفاده یک مسیر ساده و Def-clear از Definition تا Use است. «ساده» معمولاً یعنی جز در حالت مجاز آغاز و پایان، گره‌ای تکرار نشود. این قید باعث می‌شود حلقه‌ها تعداد نامتناهی مسیر نسازند، ولی همچنان تعداد مسیرها در کد واقعی می‌تواند زیاد باشد.

مثال عملی: تابع بازپرداخت با کارمزد

این تابع TypeScript مبلغ قابل بازپرداخت را محاسبه می‌کند. سقف کارمزد ۵۰ هزار تومان است و برای سادگی، اعتبارسنجی مبلغ را خارج از این تابع فرض کرده‌ایم.

export function refundAmount(
  paidAmount: number,
  feeEnabled: boolean,
  approved: boolean
): number {
  let fee = 0;                                      // N1: def(fee)

  if (feeEnabled) {                                 // N2: p-use(feeEnabled)
    fee = Math.min(paidAmount * 0.02, 50_000);       // N3: def(fee), c-use(paidAmount)
  }

  let refund = 0;                                   // N4: def(refund)

  if (approved) {                                   // N5: p-use(approved)
    refund = paidAmount - fee;                      // N6: def(refund), c-use(paidAmount, fee)
  }

  return refund;                                    // N7: c-use(refund)
}

گراف جریان کنترل تابع

یال‌ها چنین‌اند: N1 → N2؛ شاخهٔ درست N2 → N3 → N4؛ شاخهٔ نادرست N2 → N4؛ سپس N4 → N5؛ شاخهٔ درست N5 → N6 → N7 و شاخهٔ نادرست N5 → N7.

گرهعملDefc-usep-use
N1fee = 0fee——
N2if (feeEnabled)——feeEnabled
N3محاسبهٔ کارمزدfeepaidAmount—
N4refund = 0refund——
N5if (approved)——approved
N6محاسبهٔ بازپرداختrefundpaidAmount, fee—
N7return refund—refund—

پارامترهای تابع را Definition در گره ورود در نظر می‌گیریم. این انتخاب باید در قرارداد پوشش نوشته شود؛ وگرنه دو ابزار ممکن است برای یک تابع، مخرج متفاوتی گزارش کنند.

جفت‌های DU اصلی

متغیرDU Pairیک مسیر Def-clearشرط لازم
fee(N1, N6)N1-N2F-N4-N5T-N6کارمزد خاموش، تأیید روشن
fee(N3, N6)N3-N4-N5T-N6کارمزد و تأیید روشن
refund(N4, N7)N4-N5F-N7تأیید خاموش
refund(N6, N7)N6-N7تأیید روشن

تعریف fee در N1 از مسیر N2T به N6 نمی‌رسد، چون N3 آن را بازتعریف می‌کند. این مسیر برای جفت (N1,N6) Def-clear نیست. همین حذف ساده جلوی تولید تست اشتباه را می‌گیرد.

حداقل تست‌های هدفمند مثال

تستpaidAmountfeeEnabledapprovedخروجیتعهد برجسته
T11,000,000falsetrue1,000,000fee N1→N6
T21,000,000truetrue980,000fee N3→N6
T31,000,000falsefalse0refund N4→N7

T1 یا T2 تعریف refund در N6 را نیز به N7 می‌رساند. T2 استفادهٔ paidAmount در N3 و N6 را پوشش می‌دهد. برای p-useها، مجموعه T2 و T3 یال‌های درست و نادرست هر دو تصمیم را اجرا می‌کند.

چرا Branch Coverage کافی نیست؟

فقط با T2 و T3، هر دو خروجی تصمیم feeEnabled و approved اجرا می‌شوند؛ بنابراین Branch Coverage تابع ۱۰۰٪ است. بااین‌حال، مقدار اولیهٔ fee در N1 هرگز به مصرف N6 نمی‌رسد. T3 شاخهٔ کارمزد خاموش را می‌گیرد، اما چون بازپرداخت تأیید نشده، fee مصرف نمی‌شود.

DFT تست T1 را اضافه می‌کند. اگر let fee = 0 اشتباهاً به let fee: number تبدیل شود، T1 می‌تواند مقدار نامعتبر را در محاسبهٔ بازپرداخت آشکار کند. گزینه‌های Strict در TypeScript بخشی از این خطاها را زودتر می‌گیرند، اما تست پویا هنوز Oracle کسب‌وکاری «بازپرداخت دقیقاً یک میلیون است» را اثبات می‌کند.

پوشش بیشتر، تضمین صحت نیست

اجرای همه DU Pairها فقط می‌گوید ارتباط‌های تعریف–استفادهٔ مدل‌شده پیموده شده‌اند. اگر فرمول کارمزد به‌جای ۲٪، ۳٪ نوشته شده باشد و Oracle همان خروجی غلط را بپذیرد، پوشش کامل سبز می‌ماند. برای طراحی ورودی و مرز سقف کارمزد باید از افراز هم‌ارزی و تحلیل مقدار مرزی نیز کمک گرفت.

معیارهای پوشش جریان داده

ادبیات دانشگاهی قراردادهای جزئی متفاوتی دارد. سند NIST شش معیار را فهرست می‌کند؛ در عمل چهار عنوان زیر بیشترین کاربرد را برای برنامه‌ریزی دارند. پیش از گزارش درصد، تعریف دقیق ابزار، نحوهٔ شمارش p-use و سیاست مسیر ناممکن را ثبت کنید.

All-Defs Coverage

برای هر Definition، دست‌کم یک استفادهٔ قابل‌دسترسی از همان تعریف روی یک مسیر Def-clear اجرا شود. این معیار اقتصادی است و Dead Definitionهای مهم را برجسته می‌کند، اما سایر Useهای یک Definition را تضمین نمی‌کند.

All-c-Uses و All-p-Uses

در All-c-Uses، هر تعریف باید به تمام استفاده‌های محاسباتی قابل‌دسترسی برسد. در All-p-Uses همین الزام برای استفاده‌های شرطی مطرح است. بعضی تعریف‌ها یکی از دو نوع Use را ندارند؛ سیاست «در نبود یک نوع، نوع دیگر را بپوشان» باید صریح و مطابق ابزار باشد.

All-Uses Coverage

برای هر Definition، حداقل یک مسیر Def-clear به هر c-use و p-use قابل‌دسترسی اجرا می‌شود. این معیار روی «هر جفت» تمرکز دارد، نه همه مسیرهای ممکن میان آن دو. انتخاب All‑Uses برای هر تابع الزام عمومی نیست؛ آن را برای متغیرهای مالی، مجوز، وضعیت و دادهٔ حساس با ارزیابی ریسک انتخاب کنید.

All-DU-Paths Coverage

تمام مسیرهای ساده و Def-clear از هر تعریف به استفاده‌های مرتبط اجرا می‌شوند. این معیار سخت‌گیرانه‌تر و پرهزینه‌تر است. حلقه، شرط‌های تو‌در‌تو، Exception، Callback و چندریسمانی تعداد تعهدها را به‌سرعت بالا می‌برند. «پوشش ۱۰۰٪ همه مسیرهای اجرای برنامه» با All‑DU‑Paths یکسان نیست؛ قید مسیر ساده و Def-clear دامنه را محدود می‌کند.

فرمول گزارش پوشش

DU Coverage (%) =
  executed feasible DU obligations
  --------------------------------- × 100
  all feasible DU obligations

اگر ابزار مسیر ناممکن را در مخرج نگه دارد، درصد گمراه‌کننده می‌شود. هر مورد Excluded باید شناسه، دلیل فنی، بازبین و نسخهٔ کد داشته باشد. «نشد تست بنویسیم» دلیل ناممکن‌بودن نیست؛ باید محدودیت‌های مسیر ثابت شود.

ناهنجاری‌های جریان داده را دقیق نام‌گذاری کنید

مخفف‌هایی مانند dd، du یا ur در همه منابع یکسان به کار نمی‌روند. به‌جای اتکا به مخفف، توالی کامل را در گزارش بنویسید.

توالی مشکوکنمونهٔ ریسکحکم درست
Use پیش از Definitionمحاسبه با undefined یا حافظهٔ نامعتبرمعمولاً خطا یا هشدار جدی
Definition سپس Definition بدون Useمقدار اول بی‌اثر شده استممکن است عمدی باشد؛ بازبینی لازم است
Definition بدون Use بعدیDead store، کد زائد یا محاسبهٔ پرهزینهٔ بی‌اثربافت برنامه تعیین‌کننده است
Use پس از Killخواندن منبع بسته یا شیء خارج از عمرریسک بالای خرابی
Kill پیش از Definitionآزادسازی یا بستن منبع ساخته‌نشدهممکن است مدیریت خطا ناقص باشد

ISTQB تأکید می‌کند که بازتعریف بدون استفاده در بسیاری زبان‌ها مجاز و گاهی عمدی است. بنابراین ابزار تحلیل ایستا معمولاً هشدار بالقوه می‌دهد، نه اثبات قطعی باگ.

روش انجام تست جریان داده، قدم‌به‌قدم

۱. دامنه و ریسک را محدود کنید

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

۲. گراف جریان کنترل بسازید

Basic Blockها، تصمیم‌ها، Return زودهنگام، Exception و یال‌های حلقه را ثبت کنید. CFG ناقص، DU Pairهای ناقص تولید می‌کند. برای منطق حالت‌محور، ترکیب آن با تست انتقال حالت کمک می‌کند وضعیت قبلی و بعدی نیز قابل مشاهده باشد.

۳. Def و Use را برچسب بزنید

پارامترها، فیلدهای شیء، مقدارهای بازگشتی و Side Effectها را فراموش نکنید. مشخص کنید Assignment مرکب مانند x += 1 هم Use مقدار قبلی و هم Definition مقدار جدید است. Short-circuit، Optional Chaining و Closure نیز می‌توانند مسیر مصرف را تغییر دهند.

۴. Reaching Definition و مسیر Def-clear را محاسبه کنید

برای هر Use تعیین کنید کدام Definitionها بدون بازتعریف می‌توانند به آن برسند. سپس مسیرهای ناممکن را با دلیل جدا کنید. شرط‌های وابسته ممکن است در CFG یک مسیر بسازند که هیچ ورودی واقعی قادر به اجرای آن نیست.

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

  • کد کم‌ریسک و ساده: All‑Defs یا نمونه‌برداری از DU Pairها.
  • قیمت، مجوز و وضعیت حساس: All‑Uses برای متغیرهای منتخب.
  • هستهٔ ایمنی یا مالی کوچک: بررسی امکان All‑DU‑Paths همراه با محدودیت روشن.
  • کد Legacy بزرگ: ابتدا Definitionهای تغییرکرده و Useهای downstream را هدف بگیرید.

۶. تست کیس و Oracle بنویسید

هر تست باید ورودی، پیش‌شرط، مسیر یا DU Pair هدف، خروجی دقیق و شواهد اجرا داشته باشد. الگوی نوشتن تست کیس مؤثر جلوی تبدیل‌شدن DFT به فهرستی از شماره گره‌ها بدون انتظار کسب‌وکاری را می‌گیرد.

۷. اجرا، Trace و بازبینی کنید

Coverage معمول Statement/Branch الزاماً DU Coverage نیست. در صورت نبود ابزار تخصصی، می‌توان برای محدودهٔ کوچک با Instrumentation کنترل‌شده، شناسه Definition و Use را در محیط تست ثبت کرد. لاگ نباید مقدار کارت، رمز، توکن یا دادهٔ شخصی کاربر را ذخیره کند.

پیاده‌سازی تست‌های مثال با Vitest

import { describe, expect, it } from "vitest";
import { refundAmount } from "./refund-amount";

describe("refundAmount data-flow obligations", () => {
  it("T1: initial fee definition reaches approved calculation", () => {
    expect(refundAmount(1_000_000, false, true))
      .toBe(1_000_000);
  });

  it("T2: calculated fee definition reaches approved calculation", () => {
    expect(refundAmount(1_000_000, true, true))
      .toBe(980_000);
  });

  it("T3: initial refund definition reaches return", () => {
    expect(refundAmount(1_000_000, false, false))
      .toBe(0);
  });
});

نام تست‌ها عمداً تعهد جریان داده را بیان می‌کند. در پروژهٔ واقعی، شناسه‌هایی مثل DF-FEE-N1-N6 را به Requirement یا Risk پیوند دهید. برای اجرای پایدار در CI، از اصول اتوماسیون تست، کنترل دادهٔ تست و گزارش شکست قابل‌تشخیص استفاده کنید.

چه تست‌های دیگری برای تابع لازم است؟

DFT همه چیز نیست. مقدار منفی، صفر، اعشار پولی، سقف ۵۰ هزار، Overflow و سیاست واحد پول باید جداگانه تست شوند. یک جدول تصمیم می‌تواند چهار ترکیب دو Boolean را روشن کند و تحلیل مقدار مرزی، اطراف سقف کارمزد را بپوشاند.

سناریوی ایرانی: قیمت، ریال و تومان

در محصول ایرانی، جریان دادهٔ مبلغ به‌سادگی با اشتباه واحد، گردکردن یا تبدیل رشته خراب می‌شود. متغیر amount باید در هر Definition واحد مشخص داشته باشد: ریال، تومان یا Minor Unit. اگر یک Definition مبلغ را به تومان و Definition دیگری آن را به ریال تولید کند، All‑Uses فقط مسیر را اجرا می‌کند؛ Oracle دامنه‌ای باید اختلاف ده‌برابری را تشخیص دهد.

ریسکDefinition حساسUse حساسOracle پیشنهادی
ریال/تومانتبدیل مبلغ در API Adapterکسر از کیف پولتطبیق Minor Unit و Ledger
گردکردنمحاسبهٔ درصد تخفیفثبت فاکتورقاعدهٔ گردکردن مصوب مالی
تقویم شمسیتبدیل تاریخ جلالیشرط سررسیدTimestamp و Zone مرجع
بازگشت درگاهخواندن وضعیت Callbackتغییر وضعیت سفارشتطبیق امضا، مبلغ و شناسه
هم‌زمانیخواندن موجودینوشتن موجودی جدیدقید تراکنش و عدم دوباره‌خرج‌کردن

محدودیت‌های واقعی DFT

مسیرهای ناممکن

ابزار ایستا ممکن است مسیر CFG را ممکن بداند، در حالی که قیود داده‌ای آن را غیرقابل اجرا می‌کنند. حذف این مسیر از مخرج باید مستند و بازبینی شود، نه اینکه صرفاً برای بالا رفتن درصد انجام شود.

Alias و Pointer

دو نام ممکن است به یک حافظه یا شیء اشاره کنند. نوشتن از طریق یک Alias عملاً Definition متغیر دیگری است، ولی تحلیل ساده آن را نمی‌بیند. Reflection، Dynamic Dispatch و Metaprogramming نیز دقت مدل را کاهش می‌دهند.

آرایه، Collection و Field Sensitivity

آیا items[0] و items[1] دو Definition مستقل‌اند؟ آیا نوشتن user.address.city کل user را تعریف می‌کند؟ پاسخ به Field/Index Sensitivity ابزار بستگی دارد و مستقیماً مخرج پوشش را تغییر می‌دهد.

Async و Concurrency

در Promise، Queue و Thread، ترتیب Definition و Use فقط از CFG محلی تابع به دست نمی‌آید. Race Condition ممکن است یک Use را به Definition متفاوتی متصل کند. برای این بخش به Trace بین‌سرویسی، تست هم‌زمانی و مدل Happens-before نیاز است.

هزینهٔ نگهداری

Refactor کوچک شماره گره‌ها را تغییر می‌دهد. شناسه تعهد را به مفهوم پایدارتر مثل «تعریف کارمزد پیش‌فرض تا محاسبه بازپرداخت» وصل کنید و موقعیت خط را صرفاً دادهٔ نسخه‌ای بدانید.

انتخاب ابزار تست و تحلیل جریان داده

یک Compiler یا Linter می‌تواند Use-before-Definition و متغیر بلااستفاده را پیدا کند؛ SAST ممکن است جریان Taint یا مقدار را دنبال کند؛ ابزار Coverage معمولی Statement و Branch را اندازه می‌گیرد. هیچ‌کدام را بدون بررسی قابلیت، «ابزار DFT کامل» ننامید.

چک‌لیست ارزیابی ابزار

  • آیا DU Pair و معیار پوشش را صریح گزارش می‌کند یا فقط هشدار ایستا می‌دهد؟
  • از نسخه و ویژگی‌های زبان پروژه پشتیبانی می‌کند؟
  • تحلیل بین‌روالی، Exception و Callback دارد؟
  • Alias، Field، Array و Dynamic Dispatch را چگونه مدل می‌کند؟
  • مسیر ناممکن را تشخیص می‌دهد یا امکان Exclusion مستند دارد؟
  • شواهد اجرای هر تعهد را در CI نگه می‌دارد؟
  • Overhead اجرا و حجم دادهٔ Trace قابل کنترل است؟
  • اطلاعات حساس در Log یا SaaS خارجی نشت نمی‌کند؟

برای پروژه TypeScript، فعال‌کردن strictPropertyInitialization در مستندات رسمی TypeScript بخشی از خطاهای مقداردهی را زودتر آشکار می‌کند. این تنظیم جای تست کسب‌وکاری و DU Coverage را نمی‌گیرد.

ضدالگوهای رایج

  • برابرگرفتن DFT با SAST: هشدار بدون اجرای تست، پوشش پویا نیست.
  • ادعای All‑Uses بدون مخرج: درصد بدون فهرست DU Pairهای شدنی قابل ممیزی نیست.
  • تولید همه مسیرها: انفجار مسیر ایجاد می‌کند و ریسک را نادیده می‌گیرد.
  • نادیده‌گرفتن Oracle: مسیر اجرا می‌شود، اما محاسبهٔ غلط سبز می‌ماند.
  • شمارش هشدار به‌عنوان باگ: Definition دوباره بدون Use می‌تواند عمدی باشد.
  • ثبت مقدار حساس در Trace: برای مشاهده‌پذیری، شناسه رویداد کافی است؛ دادهٔ واقعی مشتری را Log نکنید.
  • مخلوط‌کردن تست‌ها: DFT را جایگزین تست امنیت، کارایی، UI و قرارداد API نکنید.

الگوی گزارش یک DU Obligation

Obligation ID: DF-FEE-DEFAULT-TO-REFUND
Variable: fee
Definition: default fee = 0
Use: approved refund calculation
Criterion: All-Uses (selected variables)
Feasible path: N1-N2F-N4-N5T-N6
Test: T1
Input: paid=1,000,000; feeEnabled=false; approved=true
Expected: refund=1,000,000
Evidence: test run + trace IDs, commit SHA
Risk: incorrect customer refund
Reviewer: technical tester + domain owner

این قالب، اتصال تکنیک به ریسک و نتیجه را حفظ می‌کند. بدون «Expected» و «Evidence»، گزارش فقط یک تحلیل کد است.

چک‌لیست نهایی برای بازبینی

  • دامنه، نسخه کد و قرارداد Def/Use مشخص است.
  • پارامتر، Return، Exception و Side Effect فراموش نشده‌اند.
  • مسیر هر DU Pair واقعاً Def-clear است.
  • مسیرهای ناممکن با دلیل و بازبین ثبت شده‌اند.
  • معیار پوشش با ریسک متغیر تناسب دارد.
  • تست‌ها ورودی و خروجی دقیق دارند.
  • Statement/Branch Coverage با DU Coverage اشتباه نشده است.
  • هشدار ایستا از شواهد اجرای پویا جدا گزارش می‌شود.
  • مقادیر پولی، تاریخ و Encoding Oracle دامنه‌ای دارند.
  • Trace فاقد دادهٔ حساس است.
  • تغییر کد باعث بازتولید فهرست تعهدها می‌شود.

منابع معتبر برای مطالعه بیشتر

جمع‌بندی

تست جریان داده فاصلهٔ میان «شاخه اجرا شد» و «مقدار درست از مبدأ درست به مصرف درست رسید» را پوشش می‌دهد. نقطه شروع عملی، انتخاب چند متغیر پرریسک، ساخت CFG کوچک، فهرست‌کردن Def و Use، حذف مستند مسیرهای ناممکن و طراحی تست برای DU Pairهاست. در مثال بازپرداخت دیدیم که Branch Coverage صددرصد با دو تست، رابطهٔ fee N1→N6 را جا می‌اندازد و تست سوم دقیقاً آن شکاف را می‌بندد.

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

سوالات متداول درباره تست جریان داده

۱. تفاوت Data Flow Testing و Data Flow Analysis چیست؟

Data Flow Analysis بدون اجرای برنامه، توالی Definition، Use و Kill را بررسی و هشدارهای بالقوه تولید می‌کند. Data Flow Testing پویاست و تست کیس را اجرا می‌کند تا Definition‑Use Pair مشخص پیموده شود. تحلیل ایستا ورودی خوبی برای طراحی DFT است، اما جای مدرک اجرای تست را نمی‌گیرد.

۲. DU Pair و DU Path چه تفاوتی دارند؟

DU Pair دو نقطهٔ تعریف و استفاده برای یک متغیر را مشخص می‌کند. DU Path مسیر Def-clear میان آن دو است؛ یعنی در فاصلهٔ تعریف تا استفاده، متغیر دوباره تعریف نمی‌شود. یک DU Pair ممکن است بیش از یک DU Path داشته باشد.

۳. آیا Branch Coverage صددرصد، All-Uses را تضمین می‌کند؟

خیر. در مثال مقاله، T2 و T3 همه شاخه‌ها را اجرا می‌کنند، اما تعریف اولیهٔ fee به استفاده در محاسبهٔ بازپرداخت نمی‌رسد. T1 برای پوشش این DU Pair لازم است. معیارها اطلاعات متفاوتی درباره ساختار برنامه می‌دهند.

۴. آیا باید همیشه All-DU-Paths را هدف بگیریم؟

خیر. هزینه آن در حلقه‌ها، شرط‌های تودرتو، Exception و کد Concurrent بسیار زیاد است. معیار را براساس ریسک انتخاب کنید: All‑Defs برای دامنهٔ کم‌ریسک، All‑Uses برای متغیرهای حساس و All‑DU‑Paths فقط جایی که دامنه کوچک و پیامد خطا بالا است.

۵. آیا ابزارهای تحلیل استاتیک تست جریان داده را خودکار می‌کنند؟

بعضی ابزارها Def/Use، Reaching Definition یا هشدار ناهنجاری را پیدا می‌کنند، اما هر ابزار تحلیل ایستا تست پویا یا DU Coverage ارائه نمی‌دهد. قابلیت گزارش جفت‌ها، تولید ورودی، اجرای مسیر و ذخیره شواهد را جداگانه بررسی کنید.

این مقاله را با دیگران به اشتراک بگذارید

نویسنده

محمد عسکری

محمد هستم، توسعه‌دهنده وب با بیش از ده سال تجربه در برنامه‌نویسی سمت سرور و کاربر. به یادگیری و اشتراک‌گذاری دانش علاقه‌مندم و اخیراً تمرکزم را روی حوزه تست نرم‌افزار گذاشته‌ام تا تجربیاتم را در این زمینه نیز به اشتراک بگذارم و به درک بهتر کیفیت نرم‌افزار کمک کنم.

همه مقاله‌ها

ادامه مطالعه

سطوح تست

تست i۱۸n و L۱۰n؛ چک‌لیست فارسی و RTL

راهنمای ریسک‌محور تست جهانی‌سازی و بومی‌سازی برای فارسی و ایران؛ با قرارداد Locale، ماتریس Unicode و RTL، سناریوهای تاریخ و پول، شبه‌بومی‌سازی و نمونه Playwright.

۲۴ اردیبهشت ۱۴۰۴ ۲۱ دقیقه مطالعه

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

ایستگاه بعدی شما

TestRail را همین حالا در محیط دمو ببینید.

محیط تست آماده

دسترسی به دمو TestRail

قبل از ورود، مشخصات زیر را در صفحه ورود محیط دمو وارد کنید.

آدرس دمو

https://demo.testrail.ir

ایمیل ورود

demo@testrail.ir

رمز عبور

demo_user