یک Checkout پنج پارامتر دارد: پلتفرم، نوع کاربر، وضعیت تخفیف، روش پرداخت و شبکه. فقط با چند مقدار نماینده، ۴۸ ترکیب ساخته میشود. اگر بعضی ترکیبها نامعتبر باشند هنوز ۳۶ سناریوی معتبر باقی میماند. آیا باید همه را اجرا کنیم؟
Pairwise Testing یا All-Pairs مجموعه کوچکتری میسازد که هر ترکیب دوتاییِ feasible از مقادیرِ دو پارامتر متفاوت را دستکم یکبار پوشش دهد. اما پوشش جفتها تضمین کشف باگ نیست، Interaction سهتایی را کامل پوشش نمیدهد و جای Test case مرزی، State transition یا سناریوی بحرانی را نمیگیرد.
در این آموزش، مدل فروشگاه ایرانی را از Parameter و Value تا Constraint، مجموعه ۸ردیفی، Oracle، Seed، Mixed strength و Automation میسازیم.
خلاصه عملی: ابتدا مقادیر را با افراز همارزی و مرز کمینه کنید؛ Constraintهای واقعی را بنویسید؛ Strength را بر اساس ریسک انتخاب کنید؛ خروجی ابزار را از نظر پوشش و امکان اجرا Verify کنید؛ سپس سناریوهای بحرانی، نامعتبر، ترتیبی و اکتشافی را جدا اضافه کنید.
Pairwise Testing دقیقاً چه چیزی را تضمین میکند؟
فرض کنید سه Factor داریم: مرورگر، نوع کاربر و زبان. Pairwise تضمین میکند هر مقدار Browser با هر مقدار feasible از User، هر Browser با هر Language و هر User با هر Language در حداقل یک Row دیده شود.
این تضمین درباره مدل ورودی است:
- اگر Factor مهمی جا بماند، پوشش آن صفر است؛
- اگر Valueهای نماینده بد انتخاب شوند، Pairwise آن را اصلاح نمیکند؛
- اگر Oracle نادرست باشد، Row سبز اعتماد کاذب میدهد؛
- اگر Constraint بیش از حد باشد، تعامل پرریسک حذف میشود؛
- Pairwise تمام توالی Eventها یا Stateها را پوشش نمیدهد.
جفت یعنی بین دو پارامتر متفاوت
مقادیر Chrome و Firefox هر دو متعلق به Factor «Browser» هستند و یک Pair interaction محسوب نمیشوند. جفت نمونه، Browser=Chrome با Locale=fa-IR است.
پوشش با کشف نقص یکسان نیست
NIST بر اساس مطالعه سیستمهای مشخص، Interactionهای کمتعداد را منشأ بسیاری از Failureها گزارش کرده است؛ اما صفحه رسمی بایدها و نبایدهای Combinatorial Testing صریحاً میگوید نباید فرض کنیم ۲-way همیشه کافی است. Strength باید از Risk و Evidence بیاید، نه یک درصد عمومی مانند «۹۰٪ باگها».
از Pairwise تا t-way و Covering Array
- 1-way: هر Value feasible حداقل یکبار؛
- 2-way / Pairwise: هر جفت feasible؛
- 3-way: هر ترکیب feasible سه Factor؛
- t-way: تعمیم به Strength دلخواه؛
- Mixed-strength: Strength بالاتر فقط برای زیرمجموعه پرریسک Factorها.
Covering array معمولاً با تعداد Rowها، Strength، تعداد Factor و تعداد Levelها توصیف میشود. در محصول واقعی تعداد Levelها اغلب متفاوت است؛ ابزار Mixed-level array میسازد. هدف، پوشش Interactionها با Rowهای کمتر از Full Cartesian product است؛ لزوماً یافتن کوچکترین مجموعه ممکن نیست.
NIST SP 800-142: Practical Combinatorial Testing مفاهیم، ابزار، هزینه و ملاحظات عملی را بهصورت Tutorial پوشش میدهد.
چه زمانی Pairwise مناسب است؟
- Compatibility میان Browser، OS، Device، Locale و Configuration؛
- فرم یا API با چند Factor مستقل/نیمهمستقل؛
- Feature flag و Permissionهای متعدد؛
- ترکیب روش پرداخت، نوع کاربر و حالت شبکه؛
- Regression بزرگی که Full combination غیرعملی است؛
- انتخاب Configuration برای تست System/Integration؛
- Input space یک مدل AI/Rule engine، همراه با Strength و Slice مناسب.
چه زمانی بهتنهایی مناسب نیست؟
- چند ترکیب کم و Full coverage ارزان است؛
- توالی Event و History تعیینکنندهاند؛
- خطر اصلی در مرز عددی یا فرمت پیچیده است؛
- یک سناریوی کسبوکار خاص باید هر Release اجرا شود؛
- Failure به Interactionهای ۳+ Factor وابسته است؛
- Oracle یا Expected outcome تعریف نشده است؛
- پارامترها بهشدت وابستهاند و مدل Constraint پیچیده/نادرست است.
Pairwise یک روش انتخاب داده/پیکربندی است. برای Rule از جدول تصمیم و برای تاریخچه رفتار از تست انتقال حالت استفاده کنید.
گام ۱: Scope و سؤال تست را روشن کنید
مثال ما Checkout فروشگاه است. سؤال:
آیا ایجاد Order و انتخاب روش پرداخت در ترکیب پلتفرم، نوع کاربر، تخفیف و شبکه، Rule و پیام سازگاری دارد؟
داخل Scope: Web و Android، Guest/Member، Gateway/Wallet، سه حالت تخفیف و دو وضعیت شبکه. خارج Scope: iOS، چندارزی، callback ترتیبی و Performance capacity؛ برای آنها Test جدا داریم.
بدون Scope، تیم ممکن است «Platform» را با ده Device و «Network» را با صد سرعت خام پر کند و خروجی بیمعنا بسازد.
گام ۲: Parameter و Value را مدل کنید
| Factor | Valueهای نماینده | منطق انتخاب |
|---|---|---|
| Platform | Web، Android | Scope Release |
| User | Guest، Member | Permission و State متفاوت |
| Discount | None، Valid، Expired | نبود، پذیرش و رد Rule |
| Payment | Gateway، Wallet | دو Integration/Rule متفاوت |
| Network | Normal، Slow | مسیر عادی و Timeout/UX risk |
Value خام را مستقیماً وارد نکنید
برای مبلغ، هر عدد یک Value نیست. ابتدا افراز همارزی و تحلیل مقدار مرزی انجام دهید؛ سپس نمایندههایی مانند زیر حد، دقیقاً حد و بالای حد بسازید. اگر همه را در یک Factor Pairwise بریزید، Suite بزرگ میشود و هنوز ممکن است مرز درست را نداشته باشید.
Factor پنهان را پیدا کنید
Requirement و Architecture را با Use case ترکیب کنید:
- Role/Permission؛
- State و History؛
- Configuration و Feature flag؛
- Data shape و Boundary class؛
- Protocol/Version؛
- Locale/Timezone؛
- Dependency mode و Failure mode.
NIST نیز توصیه میکند Input model فقط از Use case ساخته نشود؛ Specification و منابع دیگر برای یافتن Parameter/Value لازماند.
گام ۳: Constraint را صریح کنید
Rule محصول: Wallet فقط برای Member فعال است.
IF User = "Guest" THEN Payment != "Wallet"
Full Cartesian product پنج Factor برابر 2 × 2 × 3 × 2 × 2 = 48 است. با حذف ترکیب Guest+Wallet، ۳۶ Row معتبر میماند.
Constraint با تست منفی فرق دارد
اگر Guest در UI نباید Wallet ببیند، آن ترکیب در مجموعه «جریان معتبر» Constraint میشود. اما API هنوز باید درخواست غیرمجاز Guest+Wallet را رد کند؛ بنابراین یک Test منفی صریح جدا لازم است. حذف از Generator به معنی حذف Risk از Test plan نیست.
خطر Over-constraint
Constraint «هرگز رخ نمیدهد» را با شواهد بررسی کنید. Race، API مستقیم، داده Legacy یا Migration ممکن است State ظاهراً ناممکن را بسازد. Constraint باید Rule دامنه باشد، نه راهی برای کوچککردن مصنوعی خروجی.
گام ۴: Strength را بر اساس Risk انتخاب کنید
Pairwise برای Baseline
اگر هدف Compatibility اولیه است و شواهدی از Interaction سطح بالاتر نداریم، ۲-way نقطه شروع اقتصادی است.
۳-way برای ناحیه پرریسک
فرض کنید Incident قبلی فقط در ترکیب Discount=Valid + Payment=Wallet + Network=Slow رخ داده است. سه Factor Discount×Payment×Network را ۳-way و بقیه را ۲-way کنید. این Mixed strength از اجرای ۳-way روی همه Factorها کوچکتر است.
Seed برای Journey حیاتی
Golden path یا Incident regression را به شانس Generator واگذار نکنید. Rowهایی مانند Web+Member+Valid+Gateway+Normal را Seed/Required test کنید؛ ابزار مجموعه را حول آن تولید میکند یا آن را جدا نگه میدارید.
گام ۵: مجموعه Pairwise نمونه را تولید و Verify کنید
برای مدل پنجعاملی بالا، ۴۷ جفت feasible وجود دارد. یک تولید حریصانه، همه آنها را با ۸ Row زیر پوشاند:
| ID | Platform | User | Discount | Payment | Network |
|---|---|---|---|---|---|
| P01 | Web | Guest | None | Gateway | Normal |
| P02 | Web | Member | Valid | Wallet | Slow |
| P03 | Android | Guest | Expired | Gateway | Slow |
| P04 | Android | Member | None | Wallet | Normal |
| P05 | Web | Member | Expired | Gateway | Normal |
| P06 | Android | Guest | Valid | Gateway | Normal |
| P07 | Web | Guest | None | Gateway | Slow |
| P08 | Web | Member | Expired | Wallet | Normal |
این مجموعه نمونه لزوماً Minimal نیست. الگوریتم، ترتیب Parameter، Constraint و Seed میتوانند Rowهای دیگری بسازند. معیار، پوشش Verifyشده و قابلیت اجرای مدل است، نه شباهت خروجی به این جدول.
Verification خروجی
- هر ۴۷ Pair feasible حداقل یکبار دیده شده است؛
- هیچ Guest+Wallet در Valid suite نیست؛
- هر Value feasible حداقل یکبار وجود دارد؛
- Row تکراری یا غیرقابلساخت نداریم؛
- Golden path و Incident regression اضافه شدهاند؛
- Oracle هر Row قابل تعیین است.
گزارش Coverage ابزار را همراه Model، نسخه و Constraint Archive کنید. «۸ تست داریم» بدون Proof جفتها قابلاعتماد نیست.
گام ۶: Row را به Test Case قابلاجرا تبدیل کنید
Covering array فقط Data combination است. برای P02 باید بنویسید:
Precondition:
- Member مصنوعی با Wallet balance کافی
- کد Valid و Order واجد حداقل مبلغ
- Network profile = Slow
Actions:
1. ورود و ساخت Cart
2. اعمال Discount
3. انتخاب Wallet
4. Submit یکباره و سپس Refresh کنترلشده
Oracle:
- تخفیف دقیقاً یکبار اعمال شود
- مبلغ Wallet = مبلغ قابلپرداخت Order
- یک Order و یک Ledger effect ایجاد شود
- UI در Slow network حالت Pending/Retry درست نشان دهد
- Secret/PII در Log یا پیام نمایش داده نشود
Expected outcome ممکن است برای Rowهای دیگر متفاوت باشد. Rule و State را از Specification/Decision table استخراج کنید؛ مدل Pairwise خود Oracle تولید نمیکند. ساختار کامل Case در راهنمای نوشتن Test Case آمده است.
گام ۷: تستهای مکمل را عمداً اضافه کنید
Boundary tests
حداقل مبلغ −۱، برابر و +۱، سقف تخفیف، طول ورودی و Time boundary را جدا اجرا کنید.
Invalid/negative combinations
Guest+Wallet، Role اشتباه، Expired token و مبلغ ناهماهنگ باید رد ایمن و بدون Side effect داشته باشند.
Sequence و State
callback تکراری، دیررس، Cancel→Paid و Retry بعد از Timeout Interaction ایستا نیستند. State transition و Sequence model لازم است.
Exploratory testing
Output Pairwise را بهعنوان Charter input استفاده کنید: Row پرریسک را با Tab دوم، Refresh، Back و اختلال مشاهده کنید. تست اکتشافی ساختاریافته Gapهای مدل ازپیشتعریفشده را آشکار میکند.
Risk-based seeds
Journey درآمدی، Configuration پرترافیک، Incident قبلی و الزام قانونی بدون توجه به حضور تصادفی در Covering array باید Test صریح داشته باشند.
ابزارهای تولید Pairwise و t-way
NIST ACTS
پروژه رسمی NIST ACTS برای Combinatorial testing است. مقاله رسمی ابزار، پشتیبانی از t-way، Mixed-strength، Constraint و رابط GUI/CLI/API را توضیح میدهد. صفحه Quick Start آن در ژوئن ۲۰۲۶ بهروزرسانی شده است.
Microsoft PICT
PICT در مخزن رسمی Microsoft یک Generator خط فرمان متنباز برای Pairwise/Combinatorial model است. پیش از وابستگی، Release، License، Constraint syntax و امکان Pin کردن نسخه را در Repository بررسی کنید.
معیار انتخاب ابزار
- Strength ۲-way و بالاتر؛
- Mixed-level و Mixed-strength؛
- Constraint، Seed/Required row و Weight؛
- Coverage verification و Export CSV؛
- CLI/API برای اجرای تکرارپذیر؛
- نسخهگذاری Model و نصب آفلاین/پایدار؛
- توان تشخیص Unsatisfiable constraint؛
- هزینه، License و دسترسی تیم در ایران.
ابزار نام مدلسازی ضعیف را جبران نمیکند. یک Model کوچک را با خروجی قابلدستیسنجی PoC کنید.
Automation با خروجی Pairwise
Pipeline پیشنهادی
- Model و Constraint در Version control؛
- Generator با نسخه Pin شده؛
- Coverage verification؛
- خروجی CSV/JSON با Model version و Case ID؛
- Parameterized test با Data builder؛
- Assertion/Oracle مستقل؛
- گزارش Row، Pair و Failure-inducing combination؛
- Archive خروجی و Artifact.
for (const row of pairwiseRows) {
test(`checkout ${row.id}`, async ({ app }) => {
const context = await buildCheckoutContext(row);
const result = await app.checkout.submit(context);
assertCheckoutByRules(result, row);
});
}
تکرارپذیری
اگر ابزار با Seed تصادفی یا الگوریتم جدید خروجی متفاوت میدهد، نسخه و Seed را ثبت کنید. تغییر Rowها میتواند Trend و Investigation را دشوار کند. Model change باید Code review و Diff قابلفهم داشته باشد.
Failure localization
اگر P02 Fail شد، نمیتوان فوراً گفت کدام Pair علت است؛ هر Row چندین Interaction دارد. Test augmentation، مقایسه Rowهای Pass/Fail و آزمایش هدفمند برای جداسازی علت لازماند.
مثالهای کاربردی دیگر
Compatibility فارسی
- Browser: Chrome/Firefox/Safari هدف؛
- OS: Windows/macOS/Android/iOS هدف؛
- Locale: fa-IR/en-US؛
- Digits: Persian/Latin؛
- Direction: RTL/LTR؛
- Network: Normal/Slow/Offline transition.
Constraint واقعی Hardware/Browser را بنویسید. فونت، نیمفاصله و تاریخ شمسی شاید Factor یا Test صریح باشند؛ بسته به Risk.
API سفارش
- Role × Method × Resource ownership × Order state × Idempotency؛
- Pairwise برای Valid matrix؛
- Authorization matrix کامل برای Denyهای بحرانی؛
- State/Concurrency جدا برای Race و callback.
Security permission را صرفاً به Pairwise نسپارید؛ Denyهای حساس باید کامل و صریح باشند. الگوی API در راهنمای تست API آمده است.
چگونه اثربخشی Pairwise را بسنجیم؟
- Full valid combinations در برابر Rowهای تولیدشده؛
- Coverage همه Interactionهای Strength هدف؛
- تعداد Constraint و Pairهای infeasible؛
- زمان Model/Review/Execution، نه فقط تعداد Case؛
- Defectهای Interaction با Strength و Model gap؛
- نرخ Invalid/Blocked به دلیل داده یا Constraint؛
- تغییرپذیری Suite با Model version؛
- ریسکهای مکمل پوششدادهشده/نشده.
«تعداد باگ بهازای Row» را KPI فردی نکنید. هدف Model، پوشش قابلتوضیح است؛ نقصیابی به Oracle، اجرا و Risk نیز وابسته است.
اشتباههای رایج
- ادعای اینکه Pairwise بیشتر باگها را در هر محصول تضمین میکند؛
- واردکردن همه مقدارهای خام بدون Equivalence partition؛
- ساخت Model فقط از Use case و جاانداختن Configuration/State؛
- انتخاب ۲-way بدون تحلیل Risk Interaction بالاتر؛
- حذف ترکیب خطرناک با Constraint برای کوچککردن Suite؛
- فراموشکردن Negative test برای ترکیب نامعتبر؛
- فرض اینکه Generator Expected result میسازد؛
- نداشتن Seed برای Golden path و Incident regression؛
- استفاده از Pairwise برای Sequence/Concurrency بدون مدل مناسب؛
- اعتماد به تعداد Row بدون Coverage verification؛
- تغییر Model/Tool بدون Version و Diff؛
- معادل دانستن Row Fail با علت دوتایی مشخص.
چکلیست طراحی Pairwise
- سؤال، Scope و Risk روشن است.
- Factorها از Requirement، Architecture، Incident و Domain آمدهاند.
- Valueها نماینده Partition/Boundary هستند.
- Constraintها Rule واقعی و Reviewشدهاند.
- Strength و Mixed-strength دلیل دارند.
- Golden path/Incident بهصورت Seed یا Test جدا حفظ شدهاند.
- Invalid، Boundary، State و Exploratory tests مکمل تعریف شدهاند.
- Output از نظر Coverage و Feasibility Verify شده است.
- برای هر Row Oracle و Setup/Cleanup داریم.
- Model، Tool، Version، Seed و خروجی Archive میشوند.
پرسشهای متداول
تفاوت Pairwise و All-Pairs چیست؟
در عمل اغلب مترادفاند: پوشش تمام تعاملهای feasible دوپارامتری. «All pairs» به معنی Full Cartesian product یا Pair بین مقادیر یک Factor واحد نیست.
آیا Pairwise همه باگها را پیدا میکند؟
خیر. فقط پوشش ۲-way مدل را تضمین میکند. باگ میتواند از Value جاافتاده، Oracle، Sequence، Boundary یا Interaction سهتایی و بالاتر ناشی شود.
چه زمانی ۳-way را انتخاب کنیم؟
وقتی Risk، Incident، Architecture یا Evidence نشان میدهد سه Factor با هم اثر دارند، یا Assurance بیشتری لازم است. Mixed-strength میتواند فقط ناحیه پرریسک را ۳-way کند.
Constraintهای نامعتبر را حذف کنیم یا تست کنیم؟
از Valid covering array حذف کنید، اما اگر سیستم باید آن ورودی را رد کند، Negative test صریح بسازید. Constraint نباید مسیر غیرمجاز قابلدستیابی از API را پنهان کند.
آیا Pairwise بدون ابزار ممکن است؟
برای Model بسیار کوچک بله، اما خطای پوشش و Constraint آسان است. برای کار واقعی Generator و Coverage verifier تکرارپذیر بهتر است؛ خروجی ابزار نیز باید Review شود.
جمعبندی
Pairwise راهی برای کمینهکردن هوشمند فضای ترکیب است، نه میانبر حذف تحلیل. کیفیت آن از Model میآید: Factor درست، Value نماینده، Constraint صادقانه، Strength ریسکمحور و Oracle مستقل. با ۲-way شروع کنید اگر شواهد اجازه میدهد، Strength بالاتر و Seed را برای ناحیه حساس اضافه کنید و همیشه Boundary، State، Negative و Exploratory را بهعنوان مکمل نگه دارید.

