Піраміда тестування: рівні, вартість, антипатерни
Зміст
Питання «розкажіть про піраміду тестування» звучить майже на кожній співбесіді на AQA — і воно не про завчену картинку з трьох поверхів. Інтерв'юер перевіряє, чи розумієш ти економіку свого набору тестів: скільки їх має бути кожного типу і чому саме така пропорція. Помилишся з формою — і сюїта або повзе по сорок хвилин та червоніє від флаку, або дає фальшиву впевненість, пропускаючи реальні регресії.
Піраміда тестування (testing pyramid) — це не інструмент і не стандарт, а орієнтир для розкладки автотестів за рівнями. Вона відповідає на одне практичне питання: маючи обмежений час на прогін і на підтримку, як розподілити перевірки, щоб набір ловив дефекти якнайдешевше. Ідею популяризував Майк Кон у книзі «Succeeding with Agile» (2009), а канонічним поясненням стала стаття Мартіна Фаулера про TestPyramid. Розберемо не форму заради форми, а причину, чому форма саме така.
Рівні тестування: від функції до кліку користувача
Автотести відрізняються обсягом системи, який кожен із них піднімає, щоб зробити одну перевірку. Що більше застосунку в грі — то повільніше, крихкіше й дорожче. Класично виділяють чотири рівні (у мануальному тестуванні рівні означають трохи інше — компонентний, інтеграційний, системний, приймальний; тут ідеться саме про автоматизовані перевірки).
- Модульні (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-тестів проганяють за секунди, і розробник бачить регресію ще до коміту. Один E2E піднімає реальний браузер, ходить у мережу й чекає на базу — секунди в кращому разі. Повільна сюїта перестає бути захисною сіткою: її просто не запускають на кожен PR.
- Локалізація фейлу. Червоний unit показує пальцем на конкретну функцію. Червоний E2E каже лише «десь на шляху від кліку до оплати щось не так» — і ти витрачаєш час, щоб відтворити й локалізувати, перш ніж почати чинити.
- Флакі (flaky). Що більше рухомих частин — мережа, анімації, таймінги, спільні дані — то більше приводів для нестабільності. E2E флакають на порядок частіше за unit; таксономію й лікування розбираємо в главі про флакі-тести.
- Підтримка. E2E крихкі до будь-якої зміни інтерфейсу: переверстали кнопку — впав локатор. Тому стратегія стабільних селекторів для верхнього рівня критична, але навіть найкращі локатори не роблять E2E дешевими.
Практичний висновок один: дефект, який unit-тест ловить за секунду, з червоного E2E доводиться діставати хвилинами, а то й годинами. Тому чим нижче ти зловив баг — тим дешевше він тобі коштував.
Правило найнижчого рівня
З вартості випливає головна евристика піраміди — правило найнижчого рівня: перевіряй кожен дефект на найнижчому рівні, де його ще можна зловити. Бізнес-правило знижки живе в unit; те, що ендпоінт кошика його застосовує — в API; а E2E лишається один димовий (smoke), який підтверджує, що користувач взагалі доходить до оплати.
Звідси — наслідок, який часто формулюють на співбесідах слідом за Фаулером: якщо 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. Це і є піраміда в дії.
Антипатерни форми: ріжок морозива і пісочний годинник
Коли правило найнижчого рівня ігнорують, піраміда деформується у дві типові фігури.
Ріжок морозива (ice-cream cone) — перевернута піраміда: гора E2E й ручних перевірок угорі, майже нічого внизу. Симптоми знайомі кожному: сюїта проганяється десятки хвилин, регулярно червоніє через таймінги, а не через реальні баги, і кожна зміна UI вимагає ремонту десятків тестів. Виникає, коли QA прикручують у кінці процесу, розробники unit-тестів не пишуть, а команда виправдовується «ми тестуємо через інтерфейс, бо так робить користувач». Це найдорожчий спосіб отримати найменше впевненості.
Пісочний годинник (hourglass) — товстий низ і товстий верх, тонка талія: багато unit і багато E2E, але майже немає API й інтеграційних. Команда перевірила окремі одиниці й наскрізні клікання, але пропустила рівень, де живе більшість інтеграційних багів — стики модулів і контракти сервісів. Дефект стику unit не бачить за визначенням, тож його доводиться ловити дорогим E2E, і талію годинника доводиться постійно роздувати.
| Форма | Розкладка | Головна біль |
|---|---|---|
| Піраміда | багато unit, менше API, мало E2E | здорова: дешево і швидко |
| Ріжок морозива | багато E2E/ручних, мало unit | повільно, флакі, дорога підтримка |
| Пісочний годинник | багато unit і E2E, тонка середина | інтеграційні баги ловляться найдорожчим рівнем |
Лікування обох деформацій те саме: переносити перевірки вниз. Візьми найповільніші, найкрихкіші E2E й спитай, чи не можна той самий інваріант перевірити на API або unit. Зазвичай можна.
Testing trophy та інші моделі
Піраміда — не єдина модель, і сучасні фронтенд-команди часто малюють інакше. Трофей тестування (testing trophy) Кента Доддса має чотири рівні: статичний аналіз (типи, лінтер), unit, інтеграційні (найширший шар) і E2E. Логіка в тому, що для JavaScript-фронтенду інтеграційний тест компонента з його залежностями дає найкраще співвідношення впевненості до вартості — більше, ніж ізольований unit, який мокає пів світу. Девіз трофея — «чим більше твої тести нагадують те, як софтом реально користуються, тим більше впевненості вони дають» (Доддс), а корінь цієї ідеї — відома фраза Ґільєрмо Рауха «Пишіть тести. Не забагато. Здебільшого інтеграційні».
Є й інші орієнтири: Spotify описали модель «стільник» (honeycomb) для мікросервісів, де найбільший шар — інтеграційні тести, а класичних unit і повних E2E мало. Це не спростування піраміди, а переваження середини для контекстів, де ізольований unit дає мало впевненості: тонка логіка, багато стиків, розподілена система. У тестуванні мікросервісів центр ваги закономірно зсувається до контрактних та інтеграційних тестів.
Піраміда як орієнтир, а не догма
Крізь усі моделі проходить один інваріант: дешеві, швидкі, стабільні тести мають переважати, а повільні й дорогі — бути нечисленними й зарезервованими під те, що тільки вони й підтвердять. Це і є суть, а конкретна геометрія залежить від архітектури й ризиків.
Тому не варто ставитися до трикутника як до закону. Насамперед не існує «правильних» цифр: пропорцію 70/20/10 часто цитують, але ні Кон, ні Фаулер її не проголошували — це радше міф, ніж норма. Немає магічного відсотка E2E; є питання «скільки в нас критичних наскрізних шляхів». Для продуктового QA талія піраміди — рівень API — часто найцінніша: тести там швидкі, стабільні, близькі до бізнес-логіки й не залежать від верстки. Для мікросервісів вага зміщується до контрактів. Для CLI-утиліти без інтерфейсу верхнього шару може не бути взагалі.
Правильне запитання не «чи схожа моя сюїта на трикутник», а «який найдешевший рівень дасть мені цю конкретну впевненість». Відповідь на нього — і є стратегія, яку ми доводимо до кінця в главах про анатомію автотесту і побудову автоматизації з нуля.
Типові помилки
- Виглядає як «повне покриття», а насправді ріжок морозива. Двісті E2E ганяють ті самі бізнес-правила, які мали б жити в unit; сюїта йде сорок хвилин і червоніє через таймінги, а не баги.
- Виглядає як «unit усе покриває», а насправді ніхто не перевірив стики. Сто відсотків unit-покриття, а на проді падає інтеграція модулів — класична тонка талія годинника.
- Виглядає як «більше E2E — більше впевненості», а насправді навпаки. Кожен зайвий E2E додає часу й флаку, не додаючи покриття, якого немає нижче. Впевненість дає правильний рівень, а не кількість верхніх тестів.
- Виглядає як «піраміда наказує 70/20/10», а насправді цифри контекстні. Це орієнтир форми, а не норматив; пропорція залежить від архітектури й ризиків.
- Виглядає як суперечка «API-тест — це unit чи інтеграційний», а насправді ярлик не важить. Важать обсяг системи, швидкість і вартість, а не те, як команда назвала папку.
Підсумок
- Форма важливіша за назви: багато швидких дешевих тестів унизу, мало повільних дорогих угорі.
- Вгору дорожчає одразу за чотирма осями — швидкість зворотного зв'язку, локалізація фейлу, флакі, підтримка — і вони перемножуються.
- Правило найнижчого рівня: лови дефект там, де це найдешевше; якщо E2E зловив баг, а нижче нічого не впало — дірка на нижньому рівні.
- Ріжок морозива й пісочний годинник — дві типові деформації; лікуються перенесенням перевірок униз.
- Піраміда, трофей, стільник — орієнтири під різні контексти, а не догма; запитуй «який найдешевший рівень дає цю впевненість».
Що питають на співбесіді
- «Що таке піраміда тестування?» — інтерв'юер хоче почути рівні плюс причину форми (вартість), а не завчене «unit — service — UI». Назви шарів без пояснення економіки — слабка відповідь.
- «Що таке ріжок морозива і чим він поганий?» — перевіряють, чи впізнаєш ти антипатерн і чи можеш назвати симптоми: повільність, флакі, дорога підтримка.
- «Де в піраміді API-тести і чому їх люблять QA?» — очікують «талія: швидкі, стабільні, без залежності від UI, близькі до логіки».
- «Скільки має бути E2E-тестів?» — пастка на конкретну цифру. Сильна відповідь: стільки, скільки критичних наскрізних шляхів; решту логіки — вниз.
- «Піраміда застаріла? Що таке testing trophy?» — перевіряють, чи розумієш ти, що форма залежить від контексту, а не чи знаєш модне слово.
За всіма цими питаннями інтерв'юер шукає одне: чи мислиш ти вартістю й зворотним зв'язком, чи просто переказуєш картинку. Хто пояснює чому форма саме така, той і на проєкті вибудує сюїту, яку не соромно запускати на кожен коміт.
Джерела
- Martin Fowler — TestPyramid — канонічне коротке пояснення концепції.
- Ham Vocke — The Practical Test Pyramid — розгорнута версія з прикладами по рівнях.
- Kent C. Dodds — The Testing Trophy and Testing Classifications — модель trophy й акцент на інтеграційних тестах.
- Mike Cohn, «Succeeding with Agile» (2009) — першоджерело піраміди тестування.
- ISTQB CTFL 4.0 Syllabus, розділ 2.2 «Test Levels and Test Types» — рівні тестування як фундамент, на який лягає піраміда.
Що таке піраміда тестування і навіщо про неї питають на співбесіді?
Це орієнтир, як розкласти автотести за рівнями, щоб набір ловив дефекти якнайдешевше при обмеженому часі на прогін і підтримку. Не інструмент і не стандарт — саме модель пропорцій: багато швидких дешевих перевірок унизу, мало повільних дорогих угорі. Ідею популяризував Майк Кон, а канонічним поясненням стала стаття Мартіна Фаулера. На співбесіді фраза-картинка нікого не цікавить — інтерв'юер хоче почути, що ти розумієш економіку набору: чому саме така форма, а не назви трьох поверхів напам'ять.
Які рівні автотестів виділяють і за чим вони відрізняються?
Класично чотири, і головна вісь між ними — обсяг застосунку, який тест піднімає заради однієї перевірки. Unit ганяє одну функцію в ізоляції, без мережі й бази. Інтеграційні (integration/component) з'єднують кілька одиниць: сервіс із реальною БД або UI-компонент із залежностями. API/сервісні б'ють у застосунок через HTTP без інтерфейсу. E2E проганяють усе через UI, як живий користувач. Що більше системи в грі, то повільніше, крихкіше й дорожче — тому знизу вгору тестів має ставати менше.
Чим unit-тест принципово відрізняється від E2E?
Масштабом того, що він тримає під час перевірки, і, як наслідок, ціною. Unit працює з однією функцією чи класом без жодних зовнішніх залежностей, відпрацьовує за мілісекунди й на червоному одразу тицяє в конкретне місце коду. E2E піднімає реальний браузер, ходить у мережу, чіпає базу й сторонні сервіси — секунди-хвилини на кейс, а на червоному каже лише «десь на шляху зламалося». Тому unit-ів у здоровому наборі тисячі, а E2E — одиниці: перші дешеві й точні, другі дорогі, але єдині підтверджують, що вся система справді стикується.
Чому чим вище рівень, тим дорожчий тест?
Бо вартість росте одразу за чотирма незалежними осями, і вони перемножуються. Перша — швидкість зворотного зв'язку: тисячу unit-ів проженеш за секунди й побачиш регресію ще до коміту, а повільна E2E-сюїта просто перестає запускатися на кожен PR. Друга — локалізація фейлу: unit показує пальцем на функцію, E2E лишає тебе відтворювати проблему. Третя — флакі: більше рухомих частин (мережа, анімації, таймінги) означає більше приводів моргнути. Четверта — підтримка: переверстали кнопку, і локатор верхнього тесту впав. Разом це означає, що баг, спійманий unit-ом за секунду, з червоного E2E доводиться діставати хвилинами.
Що таке правило найнижчого рівня?
Головна евристика піраміди: кожен дефект перевіряй на найнижчому рівні, де його ще реально зловити. Логіка знижки — це unit; те, що ендпоінт кошика цю знижку застосовує — API; а наскрізний шлях користувача до оплати лишається одним димовим E2E. Сенс у тому, що чим нижче зловив баг, тим дешевше він тобі обійшовся — і за прогоном, і за діагностикою. Правило заразом підказує, де НЕ дублювати: одна й та сама перевірка на трьох рівнях — це витрати без нової впевненості.
E2E зловив регресію, а жоден нижчий тест не впав. Що це означає?
Що в тебе дірка на нижньому рівні, а не привід радіти, що E2E «врятував». Якщо баг був у бізнес-логіці, його мав би зловити unit або API-тест — раз вони мовчали, значить, цієї перевірки внизу просто немає. Правильна реакція: додати дешевий тест на потрібному рівні, а E2E лишити для того, що тільки він і підтверджує — цілісність наскрізного шляху. Це прямий наслідок правила найнижчого рівня, і його часто чекають на співбесіді слідом за самою пірамідою.
Де в піраміді API-тести і чому продуктові QA їх так люблять?
Це талія піраміди — рівень між інтеграційними й E2E, де б'ють у застосунок через HTTP чи сервісний шар без інтерфейсу. Люблять за поєднання якостей, якого немає більше ніде: тести швидкі (сотні мілісекунд проти секунд у E2E), стабільні (немає верстки, анімацій і браузерних таймінгів, які флакають), близькі до бізнес-логіки й не ламаються від того, що дизайнер посунув кнопку. Для продуктового QA це часто найцінніший рівень: максимум впевненості за мінімум крихкості. На співбесіді відповідь «талія — швидко, стабільно, без залежності від UI» — саме те, що хочуть почути.
Скільки має бути E2E-тестів у проєкті?
Це пастка на конкретну цифру — правильна відповідь не «десять відсотків», а «стільки, скільки в нас критичних наскрізних шляхів». Магічного відсотка E2E не існує; питання не «яка пропорція», а «які саме сценарії ніхто, крім повного прогону через UI, підтвердити не може». Логін-оплата, реєстрація, ключовий бізнес-флоу — так; двісті E2E на бізнес-правила, які мали б жити в unit — ні. Усе, що можна перевірити нижче, туди й спускай, а нагорі лишай тонкий шар димових перевірок реальних шляхів користувача.
Що таке «ріжок морозива» і чим він поганий?
Це перевернута піраміда: гора E2E й ручних перевірок угорі, майже порожньо внизу. Симптоми впізнає кожен, хто на такому працював — сюїта повзе десятки хвилин, регулярно червоніє через таймінги, а не через реальні баги, і будь-яка зміна інтерфейсу вимагає ремонту десятків тестів. Виникає, коли QA прикручують у кінці процесу, розробники unit-ів не пишуть, а команда виправдовується «ми ж тестуємо через інтерфейс, як користувач». Підсумок парадоксальний: це найдорожчий спосіб отримати найменше впевненості.
А що таке «пісочний годинник» і чим він відрізняється від ріжка?
Це деформація з товстим низом і товстим верхом, але тонкою талією: багато unit і багато E2E, майже нічого на рівні API та інтеграційних. Команда старанно покрила окремі одиниці й наскрізні клікання, але пропустила саме той шар, де живе більшість інтеграційних багів — стики модулів і контракти сервісів. Проблема в тому, що дефект стику unit не бачить за визначенням, тож ловити його доводиться найдорожчим E2E, і талію годинника постійно роздуває. На відміну від ріжка, тут внизу все гаразд — провалена середина.
Як лікувати деформовану піраміду?
Незалежно від форми — переносити перевірки вниз. Береш найповільніші й найкрихкіші E2E-тести і питаєш по кожному: чи можна цей самий інваріант перевірити на API або unit? Здебільшого можна — бізнес-правило спускається в unit, перевірка контракту ендпоінта в API, а на верхньому рівні лишається димовий сценарій шляху. Це знімає і час прогону, і флак, і вартість підтримки одразу. Лікування ріжка морозива й пісочного годинника по суті однакове: центр ваги набору має опуститися ближче до дешевих рівнів.
Чи правда, що піраміда наказує пропорцію 70/20/10?
Ні, і це поширений міф — жодну конкретну цифру ні Кон, ні Фаулер не проголошували. Пропорцію часто цитують, наче норматив, але це радше зручна ілюстрація, ніж закон. Реальна пропорція контекстна: вона залежить від архітектури й ризиків, а не від магічного відсотка. Сильна відповідь на співбесіді якраз розрізняє «орієнтир форми» і «жорсткий норматив»: піраміда каже, що дешевих тестів має бути більше за дорогі, але не диктує точних чисел.
Що таке testing trophy і чим він відрізняється від піраміди?
Це модель Кента Доддса на чотири рівні: статичний аналіз (типи, лінтер) унизу, потім unit, найширшим шаром — інтеграційні, а зверху E2E. Ключова відмінність від класичної піраміди — центр ваги зсунуто на інтеграційні тести, а не на unit. Логіка в тому, що для JavaScript-фронтенду тест компонента разом із його залежностями дає краще співвідношення впевненості до вартості, ніж ізольований unit, який мокає пів світу. Девіз трофея — «чим більше тести нагадують реальне користування, тим більше впевненості вони дають»; корінь ідеї — фраза Рауха «Пишіть тести. Не забагато. Здебільшого інтеграційні».
Піраміда застаріла? Що відповісти, коли її називають догмою минулого?
Не застаріла — просто це орієнтир під конкретний контекст, а не універсальний закон. Крізь усі моделі — піраміду, трофей, стільник (honeycomb) від Spotify — проходить один інваріант: дешеві, швидкі, стабільні тести мають переважати, а повільні й дорогі бути нечисленними й зарезервованими під те, що тільки вони підтвердять. Що змінюється — це де саме центр ваги: для фронтенду він у інтеграційних тестах, для мікросервісів — у контрактних, для CLI-утиліти верхнього шару може не бути взагалі. Питання не «чи схожа моя сюїта на трикутник», а «який найдешевший рівень дасть цю конкретну впевненість».
API-тест — це unit чи інтеграційний? Як правильно?
На практиці ярлик не важить, і суперечка про нього — червоний прапорець на співбесіді. Слово «інтеграційний» кожна команда розуміє по-своєму: хтось зве API-тести інтеграційними, хтось сервісними, а Фаулер узагалі розрізняє «вузькі» й «широкі» інтеграційні. Важить не назва папки, а три речі — який обсяг системи тест піднімає, як швидко відпрацьовує і скільки коштує підтримувати. Правильна поведінка не воювати за термінологію, а домовитися в команді, що на кожному рівні перевіряється — а на співбесіді показати, що ти оцінюєш тест за вартістю, а не за міткою.
Чому не варто дублювати одну й ту саму перевірку на кількох рівнях?
Бо кожен зайвий дубль додає часу прогону й вартості підтримки, не додаючи впевненості, якої немає нижче. Якщо математику знижки вже покрито десятком unit-кейсів, ганяти той самий інваріант ще й через E2E — марна витрата: E2E просто повторить те, що дешево перевірено, зате додасть флаку й крихкості. Правило найнижчого рівня працює в обидва боки: лови дефект якнайнижче — і не піднімай ту саму перевірку вище без потреби. Впевненість дає правильно обраний рівень, а не кількість тестів на верхніх поверхах.
Три кейси про те, як піраміда працює на практиці: розкладка однієї фічі по рівнях за правилом найнижчого рівня, діагностика ріжка морозива в реальному наборі й перенесення перевірки з дорогого E2E у дешевий unit. Скрізь — не форма заради форми, а рішення «який рівень дасть цю впевненість найдешевше».
Кейс 1. Розкласти фічу по рівнях: промокод на кошик
Прийшла задача: промокод SAVE20 дає 20% знижки на замовлення від 500 грн, не сумується з іншими акціями, діє до кінця місяця. Спокуса — написати п'ять E2E, які проходять весь шлях «увести код → побачити суму». Спокусі не піддаємося: розкладаємо перевірки за обсягом системи, який кожна вимагає.
| Що перевіряємо | Найдешевший рівень | Чому саме тут |
|---|---|---|
| 20% рахуються правильно на сумі від 500 | Unit | Чиста математика, залежностей немає — межі, нуль, від'ємне ганяємо десятками кейсів |
Код нижче порогу (499) знижку не дає | Unit | Той самий калькулятор, гранична умова |
| Прострочений код відхиляється | Unit / API | Дата — вхідні дані функції; якщо перевірка на бекенді, то через ендпоінт |
| Ендпоінт кошика реально застосовує знижку | API | Проводка через сервіс без UI: швидко й стабільно |
| Несумісність із іншою акцією | API | Логіка комбінування живе в сервісі, не у верстці |
| Користувач вводить код і бачить нову суму | E2E | Єдиний рівень, що підтверджує цілісність наскрізного шляху |
На виході піраміда в мініатюрі: десяток unit-кейсів на математику й межі, три-чотири API-тести на проводку й несумісність, рівно один димовий E2E на весь шлях. Питання, яке веде розкладку, одне на кожен рядок: чи є нижчий рівень, здатний зловити цей самий дефект? Якщо є — перевірка йде туди.
Кейс 2. Діагноз «ріжок морозива» за симптомами
Заходиш на проєкт, дивишся на CI — і бачиш знайому картину. Ось як відрізнити деформацію від просто «поганого дня» набору.
Suite: checkout-e2e 212 тестів 38 хв ~9% flaky
Suite: cart-unit 14 тестів 3 с 0% flaky
Suite: api 0 тестів — —
Симптоми, що прямо вказують на перевернуту піраміду:
- Прогін десятки хвилин, а тестів переважно нагорі. 212 E2E проти 14 unit і нуля API — центр ваги задертий угору.
- Флак не через баги, а через таймінги. 9% нестабільності на E2E-сюїті означає, що частина червоного — це моргання мережі й анімацій, а не регресії. Такий набір перестають запускати на кожен PR.
- Кожна зміна UI ламає десятки тестів. Переверстали кошик — і половина з 212 локаторів поїхала.
Що з цим робити — не переписувати все за раз, а міряти вартість і спускати найдорожче:
- Відсортуй E2E за часом і за частотою флаку — це список кандидатів на переселення.
- По кожному спитай: цей тест перевіряє наскрізний шлях чи бізнес-правило, замасковане під клікання? Правила — вниз.
- Спочатку піднімай відсутній рівень API: більшість «checkout» перевірок насправді про контракт ендпоінта, а не про верстку.
- Наверху лишай тонкий шар димових сценаріїв реальних шляхів користувача — решту логіки покрито дешевше.
Ключове: мета не «зробити трикутник гарним», а прибрати найповільніші й найкрихкіші перевірки з гарячого шляху зворотного зв'язку.
Кейс 3. Спустити перевірку з E2E у нижчий рівень
Ось типовий «дорогий» тест, який перевіряє бізнес-правило через увесь інтерфейс. Він працює, але піднімає браузер, ходить у мережу й флакає на кожній зміні верстки кошика.
// БУЛО: бізнес-правило знижки перевіряють через повний UI
test('замовлення понад 1000 отримує знижку 15%', async ({ page }) => {
await page.goto('/product/A');
await page.getByRole('button', { name: 'Додати в кошик' }).click();
await page.getByRole('button', { name: 'Додати в кошик' }).click(); // сума > 1000
await page.getByRole('link', { name: 'Кошик' }).click();
await expect(page.getByTestId('cart-total')).toHaveText('1700');
});
Питання за правилом найнижчого рівня: що цей тест насправді перевіряє? Математику знижки — а вона живе в чистій функції. Спускаємо ядро логіки в unit, де немає ні браузера, ні мережі:
// СТАЛО (unit): межі й математика — дешево й точно
import { calculateDiscount } from '../src/pricing';
test('знижка 15% лише понад 1000', () => {
expect(calculateDiscount(1000)).toBe(0); // рівно на межі — без знижки
expect(calculateDiscount(0)).toBe(0); // нуль — без знижки
expect(calculateDiscount(2000)).toBe(300); // 15% від 2000
});
Те, що ендпоінт кошика цю функцію реально викликає й повертає правильний підсумок, підтверджуємо одним API-тестом без UI:
// СТАЛО (API): проводка через сервіс, швидко й стабільно
test('кошик віддає total зі знижкою', async ({ request }) => {
const res = await request.post('/api/cart', {
data: { items: [{ sku: 'A', price: 2000, qty: 1 }] },
});
expect(res.status()).toBe(200);
expect((await res.json()).total).toBe(1700);
});
А E2E лишається — але вже не як перевірка математики, а як димовий доказ, що користувач узагалі доходить до підсумку й бачить його на екрані. Результат перенесення: межі знижки покрито десятком unit-кейсів за мілісекунди, проводку — стабільним API-тестом, а крихкий повільний шлях через браузер звужено до одного сценарію замість перевірки бізнес-правил кліканням.
Рівні тестування
- Можу пояснити, що піраміда — це орієнтир розкладки автотестів за вартістю, а не інструмент чи стандарт.
- Розрізняю чотири рівні за обсягом системи, який кожен піднімає: unit, інтеграційний, API, E2E.
- Розумію, що API-тести б'ють у застосунок через HTTP без інтерфейсу, і чому це найстабільніший з «дорогих» рівнів.
- Можу назвати, що зазвичай валить тест на кожному рівні: логіку одиниці, контракт між модулями, контракт ендпоінта, будь-що на наскрізному шляху.
Чому вгору дорожчає
- Перелічу чотири осі вартості, що ростуть угору: швидкість зворотного зв'язку, локалізація фейлу, флакі, підтримка — і що вони перемножуються.
- Можу пояснити, чому повільна E2E-сюїта перестає бути захисною сіткою (її не запускають на кожен PR).
- Розумію, чому червоний unit точно показує на функцію, а червоний E2E лишає відтворювати й локалізувати.
- Можу обґрунтувати, чому E2E флакають на порядок частіше за unit і від чого саме.
Правило найнижчого рівня
- Можу сформулювати правило: лови кожен дефект на найнижчому рівні, де його ще реально зловити.
- Розумію наслідок: якщо E2E зловив баг, а нижче нічого не впало — це дірка на нижньому рівні, а не привід радіти.
- Знаю, чому не варто дублювати одну перевірку на трьох рівнях (вартість без нової впевненості).
- Можу показати один інваріант (знижку) на трьох рівнях: unit — математика, API — проводка через ендпоінт, E2E — димовий шлях.
Антипатерни форми
- Розрізняю ріжок морозива (перевернута піраміда: гора E2E внизу порожньо) і його симптоми: повільність, флакі, дорога підтримка.
- Розрізняю пісочний годинник (товсті низ і верх, тонка талія) і розумію, що провалено рівень інтеграційних багів.
- Знаю універсальне лікування обох деформацій: переносити перевірки вниз, з E2E на API/unit.
Моделі й контекст
- Можу описати testing trophy Доддса (статичний аналіз, unit, інтеграційні найширші, E2E) і чому центр ваги там на інтеграційних.
- Знаю модель «стільник» (honeycomb) для мікросервісів і що вага там зміщена до інтеграційних та контрактних тестів.
- Розумію, чому 70/20/10 — міф, а не норматив, і що пропорція залежить від архітектури й ризиків.
- Можу відповісти на «скільки E2E»: стільки, скільки критичних наскрізних шляхів, а не фіксований відсоток.
- Розумію, чому суперечка «API-тест — unit чи інтеграційний» безплідна: важить обсяг, швидкість і вартість, а не ярлик.
За чим передусім відрізняються рівні автотестів у піраміді?
Питання
Що таке піраміда тестування?