vyvchy
    Теми розділу

    02 · Тест-дизайн

    Попарне тестування й комбінаторика

    Зміст

    Форма оплати має пʼять параметрів по три-чотири значення кожен — і раптом чесне «перевірити всі комбінації» перетворюється на сотні тест-кейсів, які ніхто ніколи не виконає. Попарне тестування (pairwise testing) — техніка, яка стискає цей набір до пари десятків рядків, і робить це не «на око», а з чіткою математичною гарантією: кожна пара значень будь-яких двох параметрів зустрінеться хоча б в одному тесті. На співбесідах ця тема спливає у двох формах: пряме «що таке pairwise» і замасковане «як би ви тестували форму з N параметрами» — і друга форма підступніша, бо перевіряє, чи вмієте ви рахувати комбінації взагалі.

    Це глава-поглиблення: при першому проході розділу її можна пропустити й повернутися після «Вибір і комбінування технік». Базовий трек — класи еквівалентності, граничні значення і таблиці рішень — самодостатній без неї. Але якщо у вашому продукті є конфігураційна матриця «браузери × ОС × ролі» або форма з купою незалежних опцій — ця техніка окупиться швидко.

    Комбінаторний вибух

    Кількість повних комбінацій — це добуток кількостей значень кожного параметра. Візьмімо чекаут інтернет-магазину:

    ПараметрЗначенняКількість
    Спосіб оплатикартка, PayPal, Apple Pay3
    ВалютаUSD, EUR, UAH3
    Доставкакурʼєр, самовивіз, поштомат3
    Платформаweb, iOS, Android3

    Повний перебір: 3 × 3 × 3 × 3 = 81 тест. Додаємо промокод (є/немає) — уже 162. Додаємо стан користувача (гість/залогінений) — 324. Кожен новий параметр множить, а не додає — саме тому це називають комбінаторним вибухом (combinatorial explosion). Для конфігураційної матриці на кшталт «4 браузери × 3 ОС × 4 вʼюпорти × 2 локалі» це 96 повних прогонів сюїти — і жодна команда стільки не потягне на кожен реліз.

    Це практична ілюстрація другого принципу тестування: вичерпне тестування неможливе (детальніше — розділ «Основи тестування»). Питання не «як перевірити все», а «як вибрати підмножину, що ловить максимум багів за мінімум тестів». Перший крок скорочення ви вже знаєте: значення кожного параметра — це не «всі можливі», а представники класів еквівалентності плюс межі. Другий крок — скоротити самі комбінації. Тут і починається комбінаторика.

    Гіпотеза попарності

    На чому ґрунтується право викинути більшість комбінацій? На емпіричному спостереженні: переважна більшість дефектів спричинена або одним параметром окремо, або взаємодією двох. Дослідники NIST (Kuhn зі співавторами) розбирали звіти про відмови реальних систем — медичного ПЗ, браузера, сервера, розподіленої бази даних — і виявили: значну частку відмов тригерить одне-єдине значення, разом із парними взаємодіями це закриває переважну більшість, а відмов, що вимагали збігу понад шести параметрів, у вивчених системах не зафіксували взагалі.

    Звідси гіпотеза попарності: якщо набір тестів покриває кожну пару значень кожних двох параметрів, він зловить більшість дефектів взаємодії за малу частку вартості повного перебору. Формально pairwise — це окремий випадок t-wise покриття при t = 2: можна вимагати покриття всіх трійок (3-wise), четвірок і далі — кількість тестів зростає, але лишається незрівнянно меншою за повний перебір. Математичне коріння техніки — ортогональні масиви (orthogonal arrays), які статистики використовували в планах експериментів задовго до тестування ПЗ.

    Важливо чесно розуміти, що гарантія дає і чого не дає:

    • Дає: будь-яка комбінація значень двох параметрів точно зустрінеться щонайменше в одному тесті.
    • Не дає: конкретна трійка значень може не зустрітися ніколи. Баг, який відтворюється лише за збігу «Apple Pay + UAH + поштомат», pairwise-набір має право пропустити.

    Тобто це усвідомлений розмін ризику на вартість, а не безкоштовний обід.

    Побудова pairwise вручну

    Для двох-трьох параметрів набір реально скласти руками — і на співбесіді це іноді просять зробити. Візьмемо мінімальний приклад: браузер (Chrome, Firefox, Safari), ОС (Windows, macOS), роль (admin, user). Повний перебір: 3 × 2 × 2 = 12 тестів.

    Нижня межа pairwise-набору — добуток двох найбільших кількостей значень: тут 3 × 2 = 6, бо всі шість пар «браузер × ОС» мусять десь зʼявитися, а в одному тесті живе лише одна така пара. Алгоритм ручної побудови: виписати декартів добуток двох найбільших параметрів як каркас, а колонки решти параметрів заповнювати так, щоб кожне нове значення закривало ще не покриті пари.

    #БраузерОСРоль
    1ChromeWindowsadmin
    2ChromemacOSuser
    3FirefoxWindowsuser
    4FirefoxmacOSadmin
    5SafariWindowsadmin
    6SafarimacOSuser

    Перевірте самі: усі 6 пар «браузер × ОС» на місці (каркас), усі 6 пар «браузер × роль» — рядки 1–6, усі 4 пари «ОС × роль» — рядки 1, 2 (або 6), 3, 4. Шість тестів замість дванадцяти, і жодна пара не втрачена.

    На чотирьох-пʼяти параметрах ручне заповнення перетворюється на судоку: додав значення в один рядок — зламав покриття в іншому. Тому на практиці набір генерують інструментом.

    PICT: генерація набору

    PICT (Pairwise Independent Combinatorial Tool) — консольний opensource-інструмент від Microsoft, один із найпоширеніших для цієї задачі. Модель — звичайний текстовий файл: параметр, двокрапка, значення через кому.

    PaymentMethod: Card, PayPal, ApplePay
    Currency:      USD, EUR, UAH
    Shipping:      Courier, Pickup, Locker
    Platform:      Web, iOS, Android

    Запуск — одна команда:

    pict model.txt

    На виході — таблиця тест-кейсів (tab-separated), яка покриває всі пари: для моделі вище це близько десяти рядків замість 81 повної комбінації. За замовчуванням PICT генерує саме пари; ключ /o піднімає порядок взаємодії — pict model.txt /o:3 покриє всі трійки, якщо аналіз ризиків каже, що пар замало.

    Constraints: неможливі комбінації

    У реальних системах параметри рідко повністю незалежні: Apple Pay не існує на Android, Safari не живе на Windows. Перша інтуїція — згенерувати набір і викреслити безглузді рядки руками — неправильна: разом із рядком ви викидаєте всі валідні пари, які він покривав, і в наборі зʼявляються діри, яких ніхто не бачить.

    Правильний шлях — описати обмеження (constraints) у самій моделі, тоді генератор перекладе валідні пари в інші рядки:

    IF [PaymentMethod] = "ApplePay" THEN [Platform] <> "Android";
    IF [Platform] = "iOS" THEN [Shipping] <> "Locker";

    Ще дві можливості PICT, які варто знати:

    • Невалідні значення позначаються тильдою (~ExpiredCard): PICT гарантує не більше одного невалідного значення на рядок — це та сама дисципліна «кожен невалідний клас окремим тестом» із глави про класи еквівалентності, тільки автоматизована.
    • Сідінг (seeding) — файл із обовʼязковими комбінаціями (ключ /e): бізнес-критичні сценарії, які мусять бути в наборі, фіксуються явно, а генератор добудовує решту покриття навколо них.

    Коли pairwise недостатньо

    Pairwise — інструмент для одного конкретного класу задач: багато незалежних параметрів, кожна валідна комбінація має сенс, результат не залежить від хитрої логіки між умовами. Поза цим класом техніка або мовчки пропускає баги, або просто не застосовна:

    • Логіка між умовами. Якщо «знижка 10% дається тільки залогіненому з промокодом при сумі понад 1000» — це не незалежні параметри, а бізнес-правила. Тут потрібна таблиця рішень: вона перелічує правила явно й дає очікуваний результат для кожного.
    • Послідовність має значення. Pairwise комбінує значення, але нічого не знає про порядок дій. Сценарії «скасувати після оплати, але до відправлення» — територія переходів станів.
    • Відомі ризиковані трійки. Якщо аналітика багів чи досвід підказує, що конкретні три параметри взаємодіють (оплата × валюта × платіжний шлюз), — підніміть порядок до 3-wise для цієї підмножини або зафіксуйте комбінації сідінгом. Ризик-орієнтований підхід до вибору глибини — канонічно в розділі «Основи тестування».
    • Проблема оракула. Генератор видає входи, але не каже, який результат очікувати в кожному рядку. Якщо очікуваний результат для довільної комбінації визначити важко — виграш від згенерованих 200 рядків ілюзорний: ви не зможете їх перевірити.

    Так

    Ні

    Так

    Ні

    Так

    Ні

    Багато параметрів зі значеннями

    Комбінацій мало —
    до кількох десятків?

    Повний перебір:
    дешевше ніж думати

    Результат визначають
    бізнес-правила між умовами?

    Таблиця рішень

    Критична послідовність
    дій чи станів?

    Переходи станів

    Pairwise / n-wise
    + constraints + сідінг

    Так

    Ні

    Так

    Ні

    Так

    Ні

    Багато параметрів зі значеннями

    Комбінацій мало —
    до кількох десятків?

    Повний перебір:
    дешевше ніж думати

    Результат визначають
    бізнес-правила між умовами?

    Таблиця рішень

    Критична послідовність
    дій чи станів?

    Переходи станів

    Pairwise / n-wise
    + constraints + сідінг

    Комбінаторика в конфігураційному тестуванні

    Найчастіше pairwise у вебі застосовують не до полів форми, а до конфігураційної матриці: браузери × ОС × вʼюпорти × локалі. Логіка та сама, але з двома особливостями.

    Перша: значення параметрів беруться не зі специфікації, а з аналітики користувачів. Немає сенсу тримати в матриці комбінацію, якою користується 0.1% аудиторії, і пропустити ту, де сидить 40%. Спершу ріжеться список значень (реальні браузери й пристрої вашої аудиторії), потім pairwise ріже комбінації.

    Друга: конфігурації нерівноцінні за вартістю. Прогнати сюїту в headless Chromium — хвилини; на реальному Safari під macOS у фермі — набагато дорожче. Тому типова стратегія — асиметрична: повна сюїта на основній конфігурації, pairwise-редукована матриця на решті, і смоук — на екзотиці.

    В автоматизації конфігураційна матриця живе прямо в конфігурації ранера. У Playwright це projects — кожен рядок вашого pairwise-набору стає окремим проєктом:

    // playwright.config.ts — pairwise-редукована матриця замість повної
    export default defineConfig({
      projects: [
        { name: 'chromium-desktop-uk', use: { ...devices['Desktop Chrome'], locale: 'uk-UA' } },
        { name: 'webkit-desktop-en',   use: { ...devices['Desktop Safari'], locale: 'en-US' } },
        { name: 'firefox-desktop-en',  use: { ...devices['Desktop Firefox'], locale: 'en-US' } },
        { name: 'chromium-mobile-en',  use: { ...devices['Pixel 7'], locale: 'en-US' } },
      ],
    });

    Повна матриця «3 рушії × 2 форм-фактори × 2 локалі» — це 12 проєктів; строгий pairwise дав би 6 (нижня межа — 3 × 2, як у ручному прикладі вище). Чотири рядки вище — наступний крок редукції, уже ризик-орієнтований: кожен рушій проганяється хоча б раз, а «дорогі» пари з локаллю uk-UA і мобільним вʼюпортом лишаються тільки на основному рушії аудиторії. Це і є типова еволюція матриці: повний перебір → pairwise → підрізання за аналітикою. Той самий принцип працює для матриць у CI (стратегія прогонів — розділ «Git і CI/CD») і для кросбраузерного тестування верстки (окрема глава в розділі «Веб і мережі»).

    Типові помилки

    • Виглядає як «pairwise покрив усі комбінації», а насправді він гарантує лише пари: трійка «Apple Pay + UAH + поштомат» може не зустрітися в жодному рядку, і баг на її перетині набір пропустить. Скорочення — це прийнятий ризик, і його треба вміти назвати вголос.
    • Виглядає як «згенерував набір, викреслив неможливі рядки — готово», а насправді разом із рядком випали валідні пари, які він покривав. Неможливі комбінації описуються constraints у моделі — тоді генератор перекриє пари іншими рядками.
    • Виглядає як «параметр = поле, значення = все, що є в дропдауні», а насправді значення мають бути представниками класів еквівалентності й межами. Пхати в модель 25 країн замість 3–4 класів — отримати вибух ще до генерації.
    • Виглядає як «інструмент згенерував 15 тестів — тест-дизайн готовий», а насправді генератор видав лише входи. Очікуваний результат кожного рядка все одно визначає людина; без оракула це не тести, а перелік натискань.
    • Виглядає як «критичні сценарії якось потраплять у набір», а насправді генератор нічого не знає про бізнес-ризик. Комбінацію, на якій компанія заробляє гроші, фіксують сідінгом явно — а не сподіваються на випадковість.

    Підсумок

    • Повний перебір росте як добуток кількостей значень параметрів — кожен новий параметр множить, а не додає. Тому вичерпне комбінування неможливе майже одразу.
    • Pairwise гарантує рівно одне: кожна пара значень будь-яких двох параметрів зустрінеться хоча б в одному тесті. Трійки й глибші взаємодії — поза гарантією; за потреби порядок піднімають до t-wise.
    • Значення для комбінаторики — представники класів еквівалентності й межі, а не сирий перелік із UI. Спершу ріжуться значення, потім — комбінації.
    • Неможливі комбінації описуються constraints у моделі, критичні — фіксуються сідінгом. Ручне викреслювання рядків руйнує покриття непомітно.
    • Pairwise — для незалежних параметрів. Логіка між умовами — таблиці рішень; послідовності — переходи станів; вибір за контекстом — глава про комбінування технік.

    Що питають на співбесіді

    • «Форма має 5 параметрів по 4 значення. Скільки тестів для повного перебору і як скоротити?» — Чекають миттєвого 4^5 = 1024 і слова «pairwise» з поясненням гарантії. Інтервʼюер дивиться, чи вмієте ви взагалі порахувати добуток і чи розумієте, що саме втрачається при скороченні.
    • «Що гарантує попарне тестування і чого не гарантує?» — Сильна відповідь: гарантує покриття всіх пар, не гарантує трійок; згадка гіпотези попарності (більшість багів — взаємодія одного-двох параметрів, дані NIST) і що це усвідомлений розмін ризику на вартість.
    • «Якими інструментами будували pairwise-набір?» — Достатньо PICT: модель, constraints, ключ порядку взаємодії. Червоний прапорець для інтервʼюера — кандидат чув слово, але не знає, що робити з неможливими комбінаціями.
    • «Як ви обрали б браузерну матрицю для регресії?» — Дивляться на порядок міркування: аналітика аудиторії → значення параметрів → pairwise-редукція → асиметрія (повна сюїта на основній конфігурації, смоук на решті). Відповідь «тестуємо на всьому» — програшна, бо ігнорує вартість.
    • «Коли pairwise застосовувати не варто?» — Перевірка зрілості: залежні параметри й бізнес-правила (таблиця рішень), важливий порядок дій (переходи станів), нема оракула для довільної комбінації. Кандидат, який знає межі техніки, цінніший за того, хто застосовує її всюди.

    Формат розгорнутої відповіді на задачі типу «протестуйте форму» — у главі «Класичні задачі співбесід».

    Джерела

    • Microsoft PICT — інструмент і вихідний код; повна документація моделей: constraints, сідінг, невалідні значення, порядок взаємодії.
    • NIST: Automated Combinatorial Testing for Software (ACTS) — емпіричні дослідження Куна зі співавторами, що обґрунтовують гіпотезу попарності, і безкоштовні матеріали з комбінаторного тестування.
    • Силабус ISTQB CTFL 4.0 попарного тестування не містить — з чорноскринькових технік там лише класи еквівалентності, граничні значення, таблиці рішень і переходи станів. Комбінаторні техніки (зокрема pairwise) описані в силабусі ISTQB Advanced Level Test Analyst — розділ про техніки тест-дизайну.