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

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

    Досвідні техніки: передбачення помилок і чеклисти

    Зміст

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

    Досвідні техніки (experience-based test techniques) — це спосіб зняти таку статистику з конкретної голови і зробити з неї робочий інструмент: гіпотези, списки типових дефектів, атаки, чеклисти. На співбесіді тема виринає у формулюваннях «як тестувати без вимог», «звідки береш ідеї для негативних перевірок» і в класичному «протестуйте поле вводу» — там очікують саме мікс системних технік із досвідними (розбір таких задач — у главі «Класичні задачі співбесід»).

    Error guessing: звідки береться «нюх»

    Передбачення помилок (error guessing) — техніка, у якій тестувальник вгадує, де в продукті ховаються дефекти, спираючись на досвід: як цей застосунок ламався раніше, які помилки типово роблять розробники, як падали схожі системи. Формально це найпростіша з технік — «подумай, де може бути баг, і перевір». Уся цінність — у якості здогадок.

    Ключова відмінність від специфікаційних технік — напрямок виведення. Класи еквівалентності відповідають на питання «що обіцяє вимога і як це перевірити». Error guessing відповідає на інше: «де розробник найімовірніше помилився, навіть якщо вимога виконана буквально». Тобто одиниця роботи тут — не вимога, а гіпотеза про дефект: «тут, мабуть, забули обрізати пробіли», «цей список, певно, не тестували на порожньому стані», «дата з іншої таймзони цю логіку зламає».

    Звідки беруться гіпотези — тобто той самий «нюх»:

    • Власна історія багів. Кожен знайдений дефект — це не тільки тікет, а й патерн: якщо один раз бачив, як пошук падає від лапки в запиті, наступного разу перевіриш лапку першою.
    • Баг-трекер команди. Найшвидший спосіб для джуна «купити» чужий досвід — прочитати останні пару сотень закритих багів свого проєкту. Стане видно, які модулі й типи помилок повторюються (принцип скупчення дефектів працює й тут).
    • Знання типових помилок розробки. Помилки на одиницю (off-by-one) на межах, необроблений null, порожня колекція, конкурентний доступ, помилки округлення float, час і таймзони, кодування рядків. Ці класи не залежать від проєкту.
    • Знання платформи. Для веба це, наприклад, розуміння, що «тест бачить стару версію сторінки» — це найчастіше кеш, а «залогінило не того користувача» — cookie й сесії. Досвідна гіпотеза тим точніша, чим краще знаєш механіку під капотом.

    Практичний висновок: «нюх» — не талант, а накопичення. Його можна прискорити: розбирати причини чужих багів, а не лише свої; після кожного пропущеного в прод дефекту питати «яка перевірка його б зловила»; і користуватися готовими списками — про них далі.

    Таксономії дефектів: чужий досвід списком

    Таксономія дефектів (defect taxonomy) — класифікований перелік типових видів помилок. Це той самий досвід, тільки вже зібраний і розкладений по категоріях кимось до тебе. Замість того щоб роками набивати власні ґулі, береш готовий каталог і проходиш ним по своїй фічі, питаючи біля кожного пункту: «а тут таке можливе?»

    Класичні приклади — таксономія Бориса Бейзера у «Software Testing Techniques» і додаток «Common Software Errors» до книги «Testing Computer Software» Канера, Фолка і Нгуєна: каталог на кілька сотень типових дефектів, згрупованих за категоріями — обробка вводу, обчислення, стан і дані, обробка помилок, навантаження, документація тощо.

    Для щоденної роботи вистачає компактної таксономії під свій тип продукту. Для типової веб-форми вона може виглядати так:

    • Ввід: порожнє значення, самі пробіли, максимальна довжина і довжина понад максимум, юнікод та емодзі, HTML-теги і лапки, невидимі символи з копіпасту;
    • Дії: подвійний клік по сабміту, кнопка «назад» після відправлення, оновлення сторінки посеред флоу, дві вкладки з однією формою;
    • Стан: порожній список, рівно один елемент, дуже багато елементів, протухла сесія посеред заповнення;
    • Час і локаль: межа доби і року, інша таймзона, формат дати й розділювач дробової частини в іншій локалі.

    Кожен рядок такого списку — згорнута гіпотеза error guessing. Розгортання пунктів у конкретні значення — це вже тема тестових даних.

    Сенс таксономії не в повноті, а в передаваності: особистий нюх лишається в голові, таксономія — артефакт, який можна дати новачкові, обговорити на рев'ю і поповнювати.

    Систематизовані атаки

    Наступний крок формалізації — атака (fault attack): цілеспрямована спроба викликати конкретний клас відмови. ISTQB описує це як методичний підхід до error guessing — скласти перелік можливих помилок, дефектів і відмов, а тоді спроєктувати тести, які ці відмови спровокують. Канонічне джерело — книга Джеймса Віттекера «How to Break Software», де тестування описане саме як набір атак на ввід, вивід, дані й обчислення.

    Атака відрізняється від «потикати навмання» трьома речами: у неї є мішень (конкретний клас дефекту), метод (що саме робимо) і очікуваний симптом (як зрозуміти, що влучили). Кілька атак із класичного репертуару:

    • Викликати всі повідомлення про помилки. Кожне повідомлення — це гілка обробки помилок у коді, а такі гілки тестують найрідше. Мішень — необроблені й неправильно оброблені помилкові стани.
    • Змусити застосунок застосувати значення за замовчуванням. Пропустити необов'язкове поле, стерти передзаповнене. Мішень — дефолти, яких насправді немає або які невалідні.
    • Дослідити допустимі набори символів і типи даних. Юнікод у поле «тільки латиниця», текст у числове поле, від'ємне число в поле кількості. Мішень — валідація, що довіряє клієнту.
    • Переповнити буфер вводу. Рядок на десятки тисяч символів, файл на порядок більший за очікуваний. Мішень — відсутні ліміти довжини й розміру.
    • Повторити ту саму дію багато разів. Повторний сабміт, повторний запит, повторне скасування. Мішень — стан, що не переживає повторення (неідемпотентні операції — докладніше в главі про HTTP-методи).

    Той самий цикл, який перетворює випадкову знахідку на системний інструмент, виглядає так:

    Так

    Ні, давно

    Баг у проді або
    пізня знахідка

    Розбір: до якого класу
    дефектів належить

    Пункт у таксономії
    або чеклисті

    Атака на кожній
    наступній фічі

    Влучає?

    Ревізія: пункт вигорів,
    переформулювати або прибрати

    Так

    Ні, давно

    Баг у проді або
    пізня знахідка

    Розбір: до якого класу
    дефектів належить

    Пункт у таксономії
    або чеклисті

    Атака на кожній
    наступній фічі

    Влучає?

    Ревізія: пункт вигорів,
    переформулювати або прибрати

    Гілка «пункт вигорів» — не дрібниця: це парадокс пестицидів у дії. Якщо атака роками нічого не ловить, або команда вже навчилася цього класу помилок не робити, або атака сформульована так, що б'є повз ціль.

    Чеклист-тестування

    Чеклист-тестування (checklist-based testing) — проєктування і виконання перевірок за переліком пунктів-нагадувань. Чеклист фіксує «що перевірити», але лишає тестувальнику свободу в «як» — цим він відрізняється і від детального тест-кейсу (там зафіксовано і «що», і «як»), і від вільного дослідження (там не зафіксовано нічого наперед).

    ПідхідЩо зафіксовано напередСвобода виконавця
    Детальний тест-кейсКроки, дані, очікуваний результатМінімальна
    ЧеклистПерелік перевірок-інваріантівЯк саме перевіряти
    Сесійне дослідницькеЧартер: напрям і таймбоксЩо і як перевіряти
    Ad hocНічогоПовна

    Дослідницьке тестування — окрема велика тема з власними механіками (чартери, тури, дебрифи), їй присвячена наступна глава. ISTQB відносить до досвідних технік усі три: error guessing, exploratory і checklist-based.

    Головне ремесло чеклиста — правильний рівень абстракції пункту. Занадто детально — і чеклист виродився в тест-кейс, який дорого підтримувати. Занадто загально («перевірити форму») — і пункт не нагадує нічого. Робочий пункт формулюється як інваріант, що має виконуватись: не «перевірити поле email», а «email із пробілом на кінці приймається після trim або чемно відхиляється». Тоді пункт одночасно і нагадує перевірку, і задає критерій проходження. Докладніше про вибір рівня деталізації — у главі «Тест-кейси й чеклисти: рівень деталізації».

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

    Власні чеклисти: як накопичувати досвід у файлі

    Готові таксономії дають загальне, але найцінніший чеклист — свій, вирощений на багах саме твого продукту. Правила накопичення прості:

    1. Кожен дефект, що доїхав до проду, стає пунктом. Не «баг №4521», а узагальнений інваріант: пропустили баг з датою 29 лютого — з'являється пункт «дати перевіряються на високосний рік».
    2. Пункти групуються за об'єктом: поле вводу, форма, таблиця зі списком, завантаження файлу, платіж. Тоді на новій фічі береш лише релевантні групи, а не ввесь файл.
    3. Чеклист живе під рев'ю. Раз на кілька місяців — ревізія: що вигоріло, що дублюється, що сформульовано як дія замість перевірки.

    Для команди такий файл — прискорювач онбордингу: джун отримує «нюх сеньйора» у придатному для використання вигляді. А ще пункти чеклиста природно перетікають в автоматизацію: пункт-інваріант — це готова специфікація параметризованого тесту.

    // Пункт чеклиста «поле переживає небезпечні рядки» → data-driven тест
    const naughtyInputs = [
      '   ',
      'a'.repeat(10_000),
      '<script>alert(1)</script>',
      'емодзі 🐛 і юнікод 𝕊',
      "O'Brien; DROP TABLE--",
    ];
    
    for (const value of naughtyInputs) {
      test(`назва товару: приймає або відхиляє ${JSON.stringify(value.slice(0, 15))}`, async ({ page }) => {
        await page.goto('/products/new');
        await page.getByLabel('Назва товару').fill(value);
        await page.getByRole('button', { name: 'Зберегти' }).click();
        // інваріант: явна відповідь UI, а не 500 чи мовчазна втрата вводу
        await expect(page.getByRole('alert').or(page.getByText('Збережено'))).toBeVisible();
      });
    }

    Зверни увагу: тест не каже, що саме правильно — прийняти чи відхилити. Він перевіряє інваріант нижчого рівня: система відповідає передбачувано. Це типово для перевірок, народжених з атак: вони ловлять «застосунок поводиться неадекватно», а не «результат не збігся зі специфікацією». Готовий набір таких рядків не обов'язково вигадувати — є, наприклад, Big List of Naughty Strings.

    Межі досвідних технік

    Досвідні техніки мають три системні обмеження, і на співбесіді їх знання цінується не менше, ніж знання самих технік.

    Немає моделі — немає вимірного покриття. У класів еквівалентності можна порахувати: класів вісім, покрито вісім. У error guessing лічити нема чого: список гіпотез принципово відкритий, тож «ми покрили 80% здогадок» — беззмістовна фраза. Наслідок: досвідними техніками неможливо обґрунтувати достатність тестування, тільки доповнити його.

    Якість дорівнює досвіду виконавця. Error guessing джуна об'єктивно слабший за error guessing сеньйора — і це не лікується старанністю, лише часом і таксономіями. Тому в командах досвідні перевірки нової критичної фічі варто або віддавати досвідченішим, або підпирати спільним чеклистом.

    Погана відтворюваність і трасованість. Перевірку «мені здалося підозрілим, і я спробував» важко трасувати до вимоги та повторити рік по тому. У доменах, де потрібна формальна звітність про покриття вимог, досвідні техніки можуть існувати лише як доповнення до задокументованих.

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

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

    • Виглядає як «error guessing — це тикати куди попало», а насправді це перевірка сформульованих гіпотез про конкретні класи дефектів. Немає гіпотези («що я очікую тут зламати?») — це вже не техніка, а ad hoc.
    • Виглядає як «чеклист — це просто скорочений тест-кейс», а насправді це інший інструмент: кейс фіксує кроки, чеклист — інваріанти. Скоротивши кейс, отримаєш поганий кейс; чеклист пишеться одразу на іншому рівні.
    • Виглядає як «досвідні техніки швидші, тож почнемо з них, а системні — якщо лишиться час», а насправді так утворюються діри в базовому покритті: досвід ловить екзотику, але не гарантує, що перевірені звичайні класи еквівалентності.
    • Виглядає як «чеклист написали — і він працює вічно», а насправді пункти вигорають (парадокс пестицидів): команда перестає робити ці помилки, а нові класи багів у чеклист ніхто не додав. Чеклист без ревізії деградує до ритуалу.
    • Виглядає як «нюх або є, або нема», а насправді це накопичувана статистика: баг-трекер, розбори продових інцидентів, чужі таксономії. Джун без нюху — норма; джун, який не читає закриті баги свого проєкту, — втрачена можливість.

    Підсумок

    • Досвідні техніки виводять перевірки не з вимог, а з гіпотез про дефекти; їхня цінність дорівнює якості цих гіпотез.
    • «Нюх» — це стиснута статистика минулих багів, і її можна накопичувати свідомо: баг-трекер, розбори інцидентів, готові таксономії.
    • Атака — це error guessing із мішенню, методом і очікуваним симптомом; цикл «баг → пункт списку → атака на наступній фічі» перетворює випадкові знахідки на систему.
    • Чеклист фіксує «що перевірити» і лишає свободу в «як»; робочий пункт — інваріант із критерієм, а не дія.
    • Досвідні техніки не мають вимірного покриття, тому доповнюють специфікаційні, а не замінюють їх: спершу системний каркас, потім досвідний прохід.

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

    • «Що таке error guessing і коли ти його застосовуєш?» Інтерв'юер дивиться, чи розумієш ти це як техніку з гіпотезами, а не «тестування навмання», і чи назвеш місце в процесі — після системних технік, не замість.
    • «Як тестувати фічу, на яку немає вимог?» Очікують структуру: зібрати неявні джерела вимог, побудувати базові перевірки, а тоді досвідний прохід — таксономія, атаки, чеклист. Кандидат, який одразу каже «клацав би навмання», втрачає бали.
    • «Чим чеклист відрізняється від тест-кейсу і коли що вибрати?» Дивляться на розуміння компромісу: відтворюваність проти швидкості й гнучкості, і чи згадаєш, що рівень деталізації — свідомий вибір.
    • «Звідки береш ідеї для негативних перевірок?» Сильна відповідь — конкретика: класи типових дефектів (довжина, юнікод, порожньо, повтори, час), баг-трекер проєкту, готові списки на кшталт naughty strings. Слабка — «з досвіду» без деталей.
    • «Розкажи про баг, який ти знайшов завдяки досвіду, а не тест-кейсам». Перевіряють, чи є за словами практика: очікують історію з гіпотезою («я запідозрив, що...»), а не випадкову знахідку.
    • «Які слабкі місця в досвідних технік?» Питання на зрілість: невимірне покриття, залежність від виконавця, погана відтворюваність. Хто називає межі техніки — той нею справді користувався.

    Джерела

    • ISTQB CTFL 4.0 Syllabus — розділ 4.4 «Experience-based Test Techniques»: error guessing (4.4.1), exploratory testing (4.4.2), checklist-based testing (4.4.3). Ця глава покриває 4.4.1 і 4.4.3.
    • ISTQB Glossary — канонічні означення термінів error guessing, fault attack, checklist-based testing.
    • James A. Whittaker. How to Break Software: A Practical Guide to Testing. Addison-Wesley, 2002 — першоджерело підходу «атак» на ПЗ.
    • Cem Kaner, Jack Falk, Hung Q. Nguyen. Testing Computer Software, 2nd ed. — додаток «Common Software Errors», класичний каталог типових дефектів.
    • Big List of Naughty Strings — готовий набір небезпечних рядків для атак на поля вводу.