Піраміда тестування: рівні, вартість, антипатерни
Зміст
Питання «розкажіть про піраміду тестування» — не про завчену картинку з трьох поверхів. Інтерв'юер перевіряє, чи розумієш ти економіку свого набору тестів: скільки їх має бути кожного типу і чому саме така пропорція. Помилишся з формою — і або повзе по сорок хвилин та червоніє від , або дає фальшиву впевненість, пропускаючи реальні .
(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-тестів проганяють за секунди, і розробник бачить регресію ще до . Один 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. Це і є піраміда в дії.
Антипатерни форми: ріжок морозива і пісочний годинник
Коли правило найнижчого рівня ігнорують, піраміда деформується у дві типові фігури. Обидві мають назви, і назви ці не наші: розділ про тестування книги «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 виростає шар 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).
- 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).
Відео до теми
Що таке піраміда тестування і навіщо про неї питають на співбесіді?
Це орієнтир, як розкласти автотести за рівнями, щоб набір ловив дефекти якнайдешевше при обмеженому часі на прогін і підтримку. Не інструмент і не стандарт — саме модель пропорцій: багато швидких дешевих перевірок унизу, мало повільних дорогих угорі. Ідею популяризував Майк Кон, а канонічним поясненням стала стаття Мартіна Фаулера. На співбесіді фраза-картинка нікого не цікавить — інтерв'юер хоче почути, що ти розумієш економіку набору: чому саме така форма, а не назви трьох поверхів напам'ять.
Які рівні автотестів виділяють і за чим вони відрізняються?
Класично чотири, і головна вісь між ними — обсяг застосунку, який тест піднімає заради однієї перевірки. 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-ів не пишуть, а команда виправдовується «ми ж тестуємо через інтерфейс, як користувач». Підсумок парадоксальний: це найдорожчий спосіб отримати найменше впевненості.
А що таке «пісочний годинник» і чим він відрізняється від ріжка?
Це деформація з товстим низом і товстим верхом, але тонкою талією, і назва не наша — так її називає розділ про тестування книги «Software Engineering at Google»: «many end-to-end tests and many unit tests but few integration tests». Команда старанно покрила окремі одиниці й наскрізні клікання, але пропустила саме той шар, де живе більшість інтеграційних багів — стики модулів і контракти сервісів. Проблема в тому, що дефект стику unit не бачить за визначенням, тож ловити його доводиться найдорожчим E2E; джерело формулює це як падіння, які «could have been caught quicker and more easily with a suite of medium-scope tests». На відміну від ріжка, тут внизу все гаразд — провалена середина, і сам ріжок джерело вважає важчим випадком. Найцінніше в означенні — названа причина: «The hourglass pattern occurs when tight coupling makes it difficult to instantiate individual dependencies in isolation». Тобто це симптом звʼязаності продуктового коду, а не лише прогалина в плані .
Як лікувати деформовану піраміду?
Незалежно від форми — переносити перевірки вниз. Береш найповільніші й найкрихкіші E2E-тести і питаєш по кожному: чи можна цей самий інваріант перевірити на API або unit? Здебільшого можна — бізнес-правило спускається в unit, перевірка контракту ендпоінта в API, а на верхньому рівні лишається смоук-сценарій шляху. Це знімає і час прогону, і флак, і вартість підтримки одразу. Лікування ріжка морозива й по суті однакове: центр ваги набору має опуститися ближче до дешевих рівнів. Одна поправка для годинника, яка випливає з джерела: якщо тонка талія виникла через тісну звʼязаність коду (а джерело називає саме цю причину), то додати не вийде, доки окрему залежність не можна підняти в ізоляції — тобто частина лікування лежить у продуктовому коді, а не в сюїті.
Чи правда, що піраміда наказує пропорцію 70/20/10?
Ні, і це поширений міф — жодну конкретну цифру ні Кон, ні Фаулер не проголошували. Пропорцію часто цитують, наче норматив, але це радше зручна ілюстрація, ніж закон. Реальна пропорція контекстна: вона залежить від архітектури й , а не від магічного відсотка. Сильна відповідь на співбесіді якраз розрізняє «орієнтир форми» і «жорсткий норматив»: піраміда каже, що дешевих тестів має бути більше за дорогі, але не диктує точних чисел.
Що таке testing trophy і чим він відрізняється від піраміди?
Це модель Кента Доддса на чотири рівні: (типи, лінтер) унизу, потім unit, найширшим шаром — інтеграційні, а зверху E2E. Ключова відмінність від класичної піраміди — центр ваги зсунуто на інтеграційні тести, а не на unit. Логіка в тому, що для JavaScript-фронтенду тест компонента разом із його залежностями дає краще співвідношення впевненості до вартості, ніж ізольований unit, який пів світу. Девіз трофея — «чим більше тести нагадують реальне користування, тим більше впевненості вони дають»; корінь ідеї — фраза Рауха «Пишіть тести. Не забагато. Здебільшого інтеграційні».
Піраміда застаріла? Що відповісти, коли її називають догмою минулого?
Не застаріла — просто це орієнтир під конкретний контекст, а не універсальний закон. Крізь усі моделі — піраміду, трофей, згадуваний Testing Honeycomb — проходить один інваріант: дешеві, швидкі, стабільні тести мають переважати, а повільні й дорогі бути нечисленними й зарезервованими під те, що тільки вони підтвердять. Що змінюється — це де саме центр ваги: для фронтенду він у інтеграційних тестах, для мікросервісів — у контрактних, для 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) і чому центр ваги там на інтеграційних.
- Знаю назву
Testing Honeycombі що опису її форми в джерелах розділу немає; для мікросервісів орієнтуюся на контрактні та . - Розумію, чому 70/20/10 — міф, а не норматив, і що пропорція залежить від архітектури й .
- Можу відповісти на «скільки E2E»: стільки, скільки критичних наскрізних шляхів, а не фіксований відсоток.
- Розумію, чому суперечка «API-тест — unit чи інтеграційний» безплідна: важить обсяг, швидкість і вартість, а не ярлик.
Квіз
Перед стартом
- Питань: 12
- Поріг «зараховано»: ≥70% правильних відповідей.
- Результат впливає на прогрес; завалені питання підуть у чергу повторення.
- Квіз впливає на компліт теми: тема стає «пройдено», лише коли прочитано теорію І квіз складено на ≥70%.
Питання
Що таке піраміда тестування?

