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

    06 · Автоматизація: стратегія

    Піраміда тестування: рівні, вартість, антипатерни

    Зміст

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

    (testing pyramid) — це не інструмент і не стандарт, а орієнтир для розкладки автотестів за рівнями. Вона відповідає на одне практичне питання: маючи обмежений час на прогін і на підтримку, як розподілити перевірки, щоб набір ловив дефекти якнайдешевше. Ідею популяризував Майк Кон у книзі «Succeeding with Agile» (2009), а канонічним поясненням стала стаття Мартіна Фаулера про TestPyramid. Розберемо не форму заради форми, а причину, чому форма саме така.

    Рівні тестування: від функції до кліку користувача

    Автотести відрізняються обсягом системи, який кожен із них піднімає, щоб зробити одну перевірку. Що більше застосунку в грі — то повільніше, крихкіше й дорожче. Класично виділяють чотири рівні (не плутай із рівнями тестування за ISTQB CTFL §2.2.1 — там їх пʼять: компонентний, компонентно-інтеграційний, системний, системно-інтеграційний і приймальний; тут ідеться саме про автоматизовані перевірки).

    • Модульні (unit) — перевіряють одну функцію чи клас в ізоляції, без мережі, диску й бази. Наприклад, що calculateDiscount(2000) повертає 300. Виконуються за мілісекунди, їх тисячі, пише переважно розробник (а AQA — на своїх утилітах і хелперах).
    • Інтеграційні / компонентні (integration / component) — кілька одиниць разом: сервіс із реальною базою, або UI-компонент із його залежностями. Ловлять баги на стиках, яких unit не бачить у принципі.
    • API / сервісні (service) — перевіряють застосунок через HTTP чи сервісний шар, без UI. Швидкі, стабільні, близькі до бізнес-логіки. Для продуктового QA це часто найсолодша точка (докладніше — у розділі про API-тестування).
    • Наскрізні (end-to-end, E2E) — ганяють увесь застосунок через інтерфейс, як живий користувач: браузер, мережа, база, сторонні сервіси. Найдорожчі й найповільніші, зате єдині, хто підтверджує, що всі шматки справді стикуються.
    РівеньЩо піднімаєШвидкістьЩо валить тестХто зазвичай пише
    Unitодну функцію/класмілісекундизміна логіки цієї одиницірозробник
    Інтеграційнийкілька модулів + БД/дереводесятки–сотні мсзміна контракту між модулямирозробник / AQA
    APIсервіс через HTTP, без UIсотні мсзміна контракту ендпоінтаAQA
    E2Eвесь застосунок через UIсекунди–хвилинибудь-що на шляху: верстка, дані, таймінгиAQA

    Одразу застереження про термінологію: слово «інтеграційний» кожна команда розуміє по-своєму, а API-тести хтось називає інтеграційними, хтось сервісними. Фаулер навіть розрізняє «вузькі» й «широкі» . Не воюйте за ярлики — домовтеся в команді, що на кожному рівні. На співбесіді важить не мітка, а те, чи розумієш ти обсяг і швидкість кожного типу.

    Unit · тисячі тестів
    мілісекунди, дешеві, точна діагностика

    Інтеграційні / компонентні · сотні

    API / сервісні · десятки
    швидкі й стабільні, без UI

    E2E · одиниці
    повільні, крихкі, дорогі

    Unit · тисячі тестів
    мілісекунди, дешеві, точна діагностика

    Інтеграційні / компонентні · сотні

    API / сервісні · десятки
    швидкі й стабільні, без UI

    E2E · одиниці
    повільні, крихкі, дорогі

    Чому вгору дорожчає

    Форма піраміди — не естетика, а прямий наслідок вартості. Що вищий рівень, то дорожчий тест за чотирма незалежними осями, і вони перемножуються.

    • Швидкість і зворотний зв'язок. Тисячу unit-тестів проганяють за секунди, і розробник бачить регресію ще до . Один E2E піднімає реальний браузер, ходить у мережу й чекає на базу — секунди в кращому разі. Повільна сюїта перестає бути захисною сіткою: її просто не запускають на кожен PR.
    • Локалізація фейлу. Червоний unit показує пальцем на конкретну функцію. Червоний E2E каже лише «десь на шляху від кліку до оплати щось не так» — і ти витрачаєш час, щоб відтворити й локалізувати, перш ніж почати чинити.
    • (flaky). Що більше рухомих частин — мережа, анімації, таймінги, спільні дані — то більше приводів для нестабільності. E2E флакають на порядок частіше за unit; таксономію й лікування розбираємо в главі про флакі-тести.
    • Підтримка. E2E крихкі до будь-якої зміни інтерфейсу: переверстали кнопку — впав . Тому стратегія стабільних селекторів для верхнього рівня критична, але навіть найкращі локатори не роблять E2E дешевими.

    Практичний висновок один: дефект, який unit-тест ловить за секунду, з червоного E2E доводиться діставати хвилинами, а то й годинами. Тому чим нижче ти зловив баг — тим дешевше він тобі коштував.

    Правило найнижчого рівня

    З вартості випливає головна евристика піраміди — правило найнижчого рівня: перевіряй кожен дефект на найнижчому рівні, де його ще можна зловити. Бізнес-правило знижки живе в unit; те, що кошика його застосовує — в API; а E2E лишається один , який підтверджує, що користувач взагалі доходить до оплати.

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

    Один інваріант — знижка 15% на замовлення понад 1000 — на трьох рівнях виглядає так:

    // Unit: чиста математика знижки, без мережі й БД
    import { calculateDiscount } from '../src/pricing';
    
    test('знижка 15% застосовується лише понад 1000', () => {
      expect(calculateDiscount(1000)).toBe(0);     // межа: рівно 1000 — без знижки
      expect(calculateDiscount(2000)).toBe(300);   // 15% від 2000
    });
    // API: той самий інваріант через ендпоінт, без UI
    test('кошик віддає total зі знижкою', async ({ request }) => {
      const res = await request.post('/api/cart', {
        data: { items: [{ sku: 'A', price: 2000, qty: 1 }] },
      });
      expect(res.status()).toBe(200);
      const cart = await res.json();
      expect(cart.total).toBe(1700);
    });
    // E2E: один смоук-шлях — користувач реально бачить підсумок
    test('знижка видима в кошику', async ({ page }) => {
      await page.goto('/product/A');
      await page.getByRole('button', { name: 'Додати в кошик' }).click();
      await page.getByRole('link', { name: 'Кошик' }).click();
      await expect(page.getByTestId('cart-total')).toHaveText('1700');
    });

    Математику знижки ганяємо десятками unit-кейсів (межі, від'ємні, нуль), проводку через сервіс — кількома API-тестами, а весь наскрізний шлях — рівно одним E2E. Це і є піраміда в дії.

    Так

    Ні

    E2E зловив дефект

    Чи є нижчий рівень,
    здатний зловити той самий дефект?

    Додати тест на нижчому рівні:
    unit / API / інтеграційний

    Це справді системний дефект —
    лишаємо перевірку в E2E

    E2E зводимо до смоук-перевірки шляху

    Так

    Ні

    E2E зловив дефект

    Чи є нижчий рівень,
    здатний зловити той самий дефект?

    Додати тест на нижчому рівні:
    unit / API / інтеграційний

    Це справді системний дефект —
    лишаємо перевірку в E2E

    E2E зводимо до смоук-перевірки шляху

    Антипатерни форми: ріжок морозива і пісочний годинник

    Коли правило найнижчого рівня ігнорують, піраміда деформується у дві типові фігури. Обидві мають назви, і назви ці не наші: розділ про тестування книги «Software Engineering at Google» ставить їх в один ряд — «Two antipatterns to be aware of are the "ice cream cone" and the "hourglass"».

    Ріжок морозива (ice-cream cone) — перевернута піраміда: гора E2E й ручних перевірок угорі, майже нічого внизу. Джерело описує його так само й додає типове : «Such suites tend to be slow, unreliable, and difficult to work with. This pattern often appears in projects that start as prototypes and are quickly rushed to production, never stopping to address testing debt». Симптоми знайомі кожному: сюїта проганяється десятки хвилин, регулярно червоніє через таймінги, а не через реальні баги, і кожна зміна UI вимагає ремонту десятків тестів. Це найдорожчий спосіб отримати найменше впевненості.

    (hourglass) — товстий низ і товстий верх, тонка талія: «many end-to-end tests and many unit tests but few integration tests». Команда перевірила окремі одиниці й наскрізні клікання, але пропустила рівень, де живе більшість інтеграційних багів — стики модулів і контракти сервісів. Дефект стику unit не бачить за визначенням, тож його доводиться ловити дорогим E2E: джерело формулює це як «many end-to-end test failures that could have been caught quicker and more easily with a suite of medium-scope tests», і додає, що годинник — не така сильна хвороба, як ріжок («It isn't quite as bad as the ice cream cone»).

    І головне, чого в цій главі раніше не було, — причина. Пісочний годинник не з'являється від недогляду в плануванні : «The hourglass pattern occurs when tight coupling makes it difficult to instantiate individual dependencies in isolation». Тобто тонка талія — симптом звʼязаності продуктового коду, і лікувати її додаванням інтеграційних тестів «згори» не вийде, доки залежність не можна підняти окремо.

    ФормаРозкладкаГоловна біль
    Пірамідабагато unit, менше API, мало E2Eздорова: дешево і швидко
    Ріжок морозивабагато E2E/ручних, мало unitповільно, флакі, дорога підтримка
    Пісочний годинникбагато unit і E2E, тонка серединаінтеграційні баги ловляться найдорожчим рівнем

    Лікування обох деформацій те саме — переносити перевірки вниз, і це вже наш практичний висновок, а не текст джерела: візьми найповільніші, найкрихкіші E2E й спитай, чи не можна той самий інваріант перевірити на API або unit. Зазвичай можна. Для годинника до цього додається робота з продуктовим кодом: розчепити залежності, щоб середній рівень узагалі став можливим.

    далі наступний E2E

    Ріжок морозива: гора E2E, майже нічого нижче

    Наявні E2E лишаємо працювати —
    поки що вся захисна сітка на них

    Беремо найповільніші й найкрихкіші E2E

    Той самий інваріант закриваємо API-тестом

    Чисту логіку опускаємо ще нижче — в unit

    Тепер дубльований E2E прибираємо
    або зводимо до смоуку

    Низ несе покриття,
    зверху — лише критичні шляхи

    далі наступний E2E

    Ріжок морозива: гора E2E, майже нічого нижче

    Наявні E2E лишаємо працювати —
    поки що вся захисна сітка на них

    Беремо найповільніші й найкрихкіші E2E

    Той самий інваріант закриваємо API-тестом

    Чисту логіку опускаємо ще нижче — в unit

    Тепер дубльований E2E прибираємо
    або зводимо до смоуку

    Низ несе покриття,
    зверху — лише критичні шляхи

    Ключова думка діаграми: ріжок морозива розбирають знизу й поштучно — спершу під наявними E2E виростає шар API-тестів, і тільки тоді верхній тест можна прибрати; «перепишемо все з нуля» лишає продукт без жодної захисної сітки саме на час переписування.

    Окремо про цифри. Google у тій самій книзі наводить свою розкладку — приблизно 80% вузьких unit-тестів, 15% інтеграційних середнього обсягу, 5% наскрізних — але подає її з прямим застереженням: «As a very rough guideline». І тут же відмовляється нормувати її для інших: «When considering your own mix, you might want a different balance», бо здоровий набір змішує розміри й обсяги тестів «appropriate to the local architectural and organizational realities». Тому конкретне співвідношення на співбесіді — не знання, а червоний .

    Testing trophy та інші моделі

    Піраміда — не єдина модель, і сучасні фронтенд-команди часто малюють інакше. testing trophy (трофей) Кента Доддса має чотири рівні: (типи, лінтер), unit, інтеграційні (найширший шар) і E2E. Логіка в тому, що для JavaScript-фронтенду інтеграційний тест компонента з його залежностями дає найкраще співвідношення впевненості до вартості — більше, ніж ізольований unit, який пів світу. Девіз трофея — «чим більше твої тести нагадують те, як софтом реально користуються, тим більше впевненості вони дають» (Доддс), а корінь цієї ідеї — відома фраза Ґільєрмо Рауха «Пишіть тести. Не забагато. Здебільшого інтеграційні».

    Є й інші орієнтири: у розмовах про мікросервіси згадують модель Testing Honeycomb, яку пов'язують зі Spotify — але розгорнутого опису її форми в джерелах розділу немає, тож спиратися на неї як на готову розкладку шарів не варто. Мотив у таких моделей той самий, що й у трофея: піраміду вони не спростовують, а переносять центр ваги туди, де ізольований unit дає мало впевненості — тонка логіка, багато стиків, розподілена система. У тестуванні мікросервісів центр ваги закономірно зсувається до контрактних та інтеграційних тестів.

    Піраміда як орієнтир, а не догма

    Крізь усі моделі проходить один інваріант: дешеві, швидкі, стабільні тести мають переважати, а повільні й дорогі — бути нечисленними й зарезервованими під те, що тільки вони й підтвердять. Це і є суть, а конкретна геометрія залежить від архітектури й .

    Тому не варто ставитися до трикутника як до закону. Насамперед не існує «правильних» цифр: пропорцію 70/20/10 часто цитують, але ні Кон, ні Фаулер її не проголошували — це радше міф, ніж норма. Немає магічного відсотка E2E; є питання «скільки в нас критичних наскрізних шляхів». Для продуктового QA талія піраміди — рівень API — часто найцінніша: тести там швидкі, стабільні, близькі до бізнес-логіки й не залежать від верстки. Для мікросервісів вага зміщується до контрактів. Для CLI-утиліти без інтерфейсу верхнього шару може не бути взагалі.

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

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

    • Виглядає як «повне покриття», а насправді ріжок морозива. Двісті E2E ганяють ті самі бізнес-правила, які мали б жити в unit; сюїта йде сорок хвилин і червоніє через таймінги, а не баги.
    • Виглядає як «unit усе покриває», а насправді ніхто не перевірив стики. Сто відсотків unit-покриття, а на проді падає інтеграція модулів — класична тонка талія годинника.
    • Виглядає як «більше E2E — більше впевненості», а насправді навпаки. Кожен зайвий E2E додає часу й флаку, не додаючи покриття, якого немає нижче. Впевненість дає правильний рівень, а не кількість верхніх тестів.
    • Виглядає як «піраміда наказує 70/20/10», а насправді цифри контекстні. Це орієнтир форми, а не норматив; пропорція залежить від архітектури й ризиків.
    • Виглядає як суперечка «API-тест — це unit чи інтеграційний», а насправді ярлик не важить. Важать обсяг системи, швидкість і вартість, а не те, як команда назвала папку.

    Підсумок

    • Форма важливіша за назви: багато швидких дешевих тестів унизу, мало повільних дорогих угорі.
    • Вгору дорожчає одразу за чотирма осями — швидкість зворотного зв'язку, локалізація фейлу, флакі, підтримка — і вони перемножуються.
    • Правило найнижчого рівня: лови дефект там, де це найдешевше; якщо E2E зловив баг, а нижче нічого не впало — дірка на нижньому рівні.
    • Ріжок морозива й пісочний годинник — дві типові деформації; лікуються перенесенням перевірок униз.
    • Піраміда, трофей, Testing Honeycomb — орієнтири під різні контексти, а не догма; запитуй «який найдешевший рівень дає цю впевненість».

    Можливі питання

    • «Що таке піраміда тестування?» — інтерв'юер хоче почути рівні плюс причину форми (вартість), а не завчене «unit — service — UI». Назви шарів без пояснення економіки — слабка відповідь.
    • «Що таке ріжок морозива і чим він поганий?» — перевіряють, чи впізнаєш ти антипатерн і чи можеш назвати симптоми: повільність, флакі, дорога підтримка.
    • «Де в піраміді API-тести і чому їх люблять QA?» — очікують «талія: швидкі, стабільні, без залежності від UI, близькі до логіки».
    • «Скільки має бути E2E-тестів?» — пастка на конкретну цифру. Сильна відповідь: стільки, скільки критичних наскрізних шляхів; решту логіки — вниз.
    • «Піраміда застаріла? Що таке testing trophy?» — перевіряють, чи розумієш ти, що форма залежить від контексту, а не чи знаєш модне слово.

    За всіма цими питаннями інтерв'юер шукає одне: чи мислиш ти вартістю й зворотним зв'язком, чи просто переказуєш картинку. Хто пояснює чому форма саме така, той і на проєкті вибудує сюїту, яку не соромно запускати на кожен коміт.

    Джерела

    Рівні тестування: від функції до кліку користувача

    • Mike Cohn — The Forgotten Layer of the Test Automation Pyramid — першоджерело трьох рівнів і означення середнього шару: «service» тут — не SOA і не HTTP, а «щось, що застосунок робить у відповідь на вхідні дані».
    • ISTQB® CTFL Syllabus v4.0.1 — §2.2.1: канонічні пʼять рівнів тестування (інтеграційний розщеплено на компонентно- і системно-інтеграційний), з якими не варто плутати рівні автотестів.
    • Ham Vocke — The Practical Test Pyramid — розрізнення вузьких і широких інтеграційних тестів і пряме застереження не привʼязуватися до назв шарів (trust: secondary, dec-0311).
    • Martin Fowler — TestPyramid — «наскрізний», «UI» і «клієнтський» — ортогональні характеристики, а не синоніми (trust: secondary, dec-0311).

    Чому вгору дорожчає

    • Mike Cohn — The Forgotten Layer of the Test Automation Pyramid — три названі властивості UI-тестів («brittle, expensive, and time consuming») плюс четверта, яку зазвичай забувають — надлишковість; і аргумент за низ піраміди — точність .
    • Martin Fowler — TestPyramid — та сама трійка «крихкі, дорогі в написанні, довгі у виконанні» і схильність до недетермінізму, який підриває довіру до тестів (trust: secondary, dec-0311).
    • Toby Clemson — Testing Strategies in a Microservice Architecture — механізм, названий загальніше: що грубозернистіший тест, то більше в ньому рухомих частин — звідси і крихкість, і час, і вартість підтримки (trust: secondary, dec-0311).

    Правило найнижчого рівня

    • Martin Fowler — TestPyramid — правило після падіння високорівневого тесту: спершу відтворити баг , бо саме падіння означає ще й діру в нижньому рівні (trust: secondary, dec-0311).
    • Mike Cohn — The Forgotten Layer of the Test Automation Pyramid — модульні тести should бути фундаментом стратегії; UI нагорі не тому, що поганий, а тому, що «we want to do as little of it as possible».

    Антипатерни форми: ріжок морозива і пісочний годинник

    • Software Engineering at Google — Chapter 11: Testing Overview — джерело обох назв («Two antipatterns to be aware of are the "ice cream cone" and the "hourglass"»), їхніх означень, причини пісочного годинника (тісна звʼязаність) і розкладки 80/15/5 із застереженням «a very rough guideline».
    • Martin Fowler — TestPyramid — ріжок морозива як перевернутий розподіл: багато наскрізних при малій кількості нижчих (trust: secondary, dec-0311).

    Testing trophy та інші моделі

    • Kent C. Dodds — The Testing Trophy and Testing Classifications — чотири шари трофея й причина шару static (у світі JavaScript типізація не є даністю); там же — девіз про схожість на реальне користування і цитата Ґільєрмо Рауха (trust: secondary, dec-0710).
    • Martin Fowler — TestPyramid — той самий закид із боку піраміди: розбіжність «більше інтеграційних проти більше юніт» імовірно ілюзорна й породжена різними означеннями «юніт-тесту» (trust: secondary, dec-0311).

    Піраміда як орієнтир, а не догма

    • Ham Vocke — The Practical Test Pyramid — сучасна оцінка самої моделі: піраміда названа надто спрощеною й потенційно оманливою, а винести з неї радять рівно два правила — тести різної гранулярності й менше тестів що вище рівень (trust: secondary, dec-0311).
    • Kent C. Dodds — The Testing Trophy and Testing Classifications — прямий висновок про цифри: суперечка про відсотки рівнів — відволікання (trust: secondary, dec-0710).
    • Toby Clemson — Testing Strategies in a Microservice Architecture — куди зсувається центр ваги в розподіленій системі й чому на вершині стоїть (trust: secondary, dec-0311).

    Пояснення

    «Поясни» працює з власним API-ключем Anthropic: запит іде з вашого браузера прямо до Anthropic.

    Свій API-ключ (BYOK)

    Вставте власний ключ Anthropic — пояснення працюватиме на реальній моделі. Ключ зберігається лише у вашому браузері (localStorage), нікуди не надсилається, крім api.anthropic.com, і не логується.