1. Рівень тесту й його роль — різні речі
Після вибору між unit, integration та API робота тільки починається. Далі потрібно вирішити, чи потрібен тут повний шлях користувача і яку роль тест зіграє в наборі. Коли розробник уперше чує поруч слова unit, integration, API, smoke, regression і E2E, виникає природне бажання скласти все в одну коробку й підписати «види тестів». Не складайте. Тут дві різні осі: на якому рівні ви перевіряєте систему і навіщо.
Найпростіше запамʼятати так: unit, integration, API і E2E — про глибину та межу перевірки, а smoke і regression — про роль тесту в наборі. Через це один і той самий тест часто сидить на обох осях одночасно: короткий E2E оформлення замовлення заодно працює вашим smoke. А regression після бага з поверненням грошей буває і unit, і integration, і API, і E2E — залежно не від бага, а від того, де збій ловиться найкраще.
Цю таблицю тримайте перед очима, коли збираєте test strategy для Commerce OS:
| Вісь | Категорія | На яке питання відповідає | Приклад у Commerce OS |
|---|---|---|---|
| Level / scope | |
Чи проходить реальний шлях користувача цілком? | оператор підтверджує повернення через інтерфейс |
| Suite role | |
Чи система взагалі жива після зміни? | сервіс піднявся, список замовлень відкривається |
| Suite role | |
Чи не повернеться цей конкретний баг? | повторний refund не створює дубліката |
До осі level / scope належать і unit, integration, API; тут до них додано найширший варіант — повний шлях користувача. З цієї таблиці добре видно головне: ні широкий охоплення, ні особлива роль тесту не означають «перевірити все підряд». Роздуєте smoke до двадцяти сценаріїв — він перестане бути smoke. Напишете E2E на кожну кнопку — отримаєте не страховку, а спосіб сповільнити собі життя. Не зʼявиться regression після виправленого бага — команда сподівається на памʼять, настрій і фазу Місяця.
2. Smoke: датчик диму, а не експертиза будівлі
Smoke потрібен не для повноти, а для швидкого сигналу, що система жива. Це датчик диму, а не експертиза будівлі: він показує, що після свіжої зміни все не розсипалося просто на вході. У Commerce OS це цінно — одразу видно, що сервіс піднімається, критичний endpoint відповідає, основний шлях не загинув.
Якісний smoke майже завжди короткий, швидкий і нудний. І це комплімент. Хвилина-півтори, один-два ключові кроки, зрозумілий червоний сигнал — завдання виконано. А smoke, який розгортається в героїчний роман із входом, створенням замовлення, промокодом, поверненням, листом на пошту й танцем в адмінці, — це спроба запхати весь проєкт в одну кнопку «перевірити все». Такі кнопки ламаються ефектно й марно.
Простий smoke для Commerce OS:
#!/usr/bin/env bash
set -euo pipefail
curl -fsS "$BASE/actuator/health" >/dev/null # Бекенд відповідає 200 OK
curl -fsS "$BASE/api/orders?page=0&size=1" >/dev/null # Список замовлень відкривається
echo "Smoke пройдено" # Smoke пройдено
Тут немає краси, зате є сенс: застосунок живий, перегляд замовлень відкривається. Якщо такий тест упав, далі немає сенсу міркувати про тонкі нюанси інтерфейсу — спочатку поверніть системі пульс.
Дуже корисно заздалегідь вирішити, які сценарії взагалі гідні статусу smoke. У Commerce OS такими кандидатами зазвичай стають перевірка здоровʼя сервісу, відкриття списку замовлень, базовий перегляд черги звернень підтримки, отримання ключових метрик на дашборді. Їх має бути небагато. Smoke — не каталог функцій, а мінімальний набір «сигналів життя».
Саме тут добре видно, чому Claude Code не можна ставити на пʼєдестал із табличкою «сам знає, що тестувати». Він запропонує каркас скрипта, нагадає про health-check, збере bash-файл за шаблонами проєкту. Але критичний шлях обираєте ви — лише ви знаєте, що важливіше після конкретної зміни: список замовлень, черга повернень чи екран підтримки. Інакше отримаєте «розумний» smoke, який перевіряє другорядну довідку й ігнорує бізнес-ризик.
3. Regression: вчорашній біль як страховка
Regression — страховка від учорашнього болю, а не абстрактна перевірка на майбутнє. Найпрактичніший і найбільш дорослий тип перевірки: він не філософствує про якість архітектури, а ставить одне питання — чи повертається конкретний баг, який уже траплявся з нами. Відповідь «можливо» означає, що тесту ще немає або він слабкий.
На практиці regression найкраще народжується з уже знайомого bugfix-циклу: спочатку відтворили проблему, потім написали тест, що падає, потім зробили мінімальне виправлення, потім переконалися, що тест став зеленим. У цьому місці тест перестає бути абстрактною «перевіркою на майбутнє» і стає зафіксованою памʼяттю команди — не тією, що живе в головах і зникає після відпустки, а тією, що лежить у репозиторії та чесно ламає збирання, якщо баг вирішив повернутися.
Схема проста:
flowchart TD
A[Знайшли баг] --> B[Відтворили]
B --> C[Написали тест, що падає, для regression]
C --> D[Виправили проблему мінімально]
D --> E[Тест став зеленим]
E --> F[Баг не повертається мовчки]
Уявімо, що в Commerce OS був неприємний баг: повторний запит на повернення по одному й тому самому замовленню створював дві записи замість однієї. Якщо ви просто виправили сервіс і зраділи, це половина роботи. Друга половина — закріпити виправлення тестом.
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
@Test
void repeatRefundDoesNotCreateDuplicateRecord() {
refundService.request(orderId, requestId);
refundService.request(orderId, requestId);
assertEquals(1, refundRepository.countByOrderId(orderId)); // Дубліката не зʼявилося
}
Цей приклад навмисно невеликий — і саме в цьому його сила. Хороший regression зазвичай не намагається одночасно довести все на світі, він бʼє в один конкретний біль, який уже стався, і дисциплінує: змушує сформулювати баг не як «щось дивне з refund», а як чітку відтворювану поведінку.
Тут варто памʼятати важливий нюанс: regression — це не окремий рівень тестування. Він може бути unit, якщо баг жив у чистій бізнес-логіці. Integration, якщо проблема була на межі сервісу й бази. Навіть E2E, якщо помилка вилізла лише в повному сценарії. Слово regression говорить не про глибину, а про роль: ми захищаємося від уже відомої неприємності.
Саме тому regression рідко буває зайвим. Якщо баг був реальним, болючим і вже дійшов до користувача, залишити його без тесту — все одно що закрити дірку у вікні газетою й сподіватися, що зима не прийде. Оптимізм, звісно, річ приємна, але репозиторій від нього теплішим не стає.
4. E2E: шлях користувача, а не кнопок
E2E здаються дуже спокусливими, тому що схожі на справжню поведінку користувача. Натиснули кнопку, відкрилася сторінка, вибрали замовлення, підтвердили, побачили результат. Після сухуватих unit та integration це виглядає майже як кіно: систему перевіряють так, як нею користуються в житті. Але в цього кіно дорогий продакшн: E2E повільніші, крихкіші, вимагають обережного вибору сценаріїв. Тому хороший інженер любить їх без фанатизму.
Правило: E2E залишайте для дійсно критичних користувацьких шляхів — не для кожного сортування, іконки та декоративної кнопки, а для сценаріїв, де збій дорого коштує бізнесу або користувачу. У Commerce OS це оформлення замовлення, підтвердження повернення оператором, вхід в адмінку, робота з критичним зверненням підтримки.
Дивіться на це як на таблицю рішень:
| Сценарій у Commerce OS | Залишаємо як E2E? | Чому |
|---|---|---|
| Користувач оформлює замовлення | Так | Це прямий бізнес-критичний шлях |
| Оператор підтверджує повернення | Так | Помилка тут бʼє по грошах і підтримці |
| Сортування списку замовлень за колонкою | Скоріше ні | Часто достатньо API/integration-перевірки |
| Колір кнопки промокоду | Ні | Це не E2E-задача, а питання UI-перевірки |
| Переход по екрану метрик | Іноді | Тільки якщо екран критичний для операційного рішення |
Такий відбір дуже тверезить: він не дає перетворити E2E-набір на музей користувацьких бажань. І це особливо важливо для новачка, бо після першого вдалого Playwright-тесту приходить небезпечна думка: «зараз я такими накрию все». Накриєте — але набір почне падати через анімації, час відповіді, нестабільні селектори. І команда ставитиметься до червоних E2E як до будильника в суботу: роздратовано й без поваги.
Невеликий E2E для Commerce OS:
import { test, expect } from "@playwright/test";
test("оператор підтверджує повернення з черги", async ({ page }) => {
await page.goto("/support/refunds");
await page.getByText("Замовлення #1024").click();
await page.getByRole("button", { name: "Підтвердити повернення" }).click();
await expect(page.getByText("Повернення підтверджено")).toBeVisible(); // Оператор бачить результат
});
Тут добре те, що сценарій зрозумілий навіть без знання всього проєкту, і якщо він упав — одразу ясно, який шлях зламався. А десятки E2E по пʼятнадцять кроків перетворюють падіння на археологічну експедицію: всі йдуть дивитися, де саме загинув цей динозавр.
Ще один важливий момент: E2E-перевірка не зобовʼязана доводити всі деталі внутрішньої логіки. Її справа — пройти шлях користувача й показати, що інтерфейс і основні звʼязки працюють. Внутрішню арифметику, окремі сервіси та контракти все одно дешевше страхувати локальнішими тестами. Інакше ви починаєте використовувати молоток там, де давно потрібен акуратний ключ.
5. Flaky: бреше частіше, ніж допомагає
Flaky-тест руйнує довіру сильніше за чесне падіння. Він то червоний, то зелений на одному й тому самому коді. Чесний зламаний тест говорить: «У вас проблема». Flaky говорить: «Може, проблема, а може, й ні, давайте ворожити». Після кількох таких епізодів команда ставиться до всього набору як до нервового родича: слухати треба, але вірити без перевірки небезпечно.
Джерела flaky зазвичай доволі прозаїчні: залежність від поточного часу, випадкових значень, нестабільної мережі, порядку виконання, повільних анімацій, зовнішніх сервісів, надто крихких UI-селекторів. В E2E це частий гість — там більше рухомих частин. Але flaky заведеться і в regression, і в smoke, якщо завʼязати перевірку на зовнішній сервіс із мінливим настроєм.
Дуже поганий і дуже популярний анти-патерн тут називається «rerun until green»: тест червоний, але ми не розбираємося, просто запускаємо ще раз. Позеленів із другої-третьої спроби — робимо вигляд, що життя прекрасне. На короткій дистанції це здається зручним, на довгій — це спосіб накопичити тихий хаос: нестабільний тест перестає бути тестом і перетворюється на лотерею.
У Workflow Kit так фіксуйте прямо в проєктних правилах:
## Політика flaky-тестів
- Не робимо «rerun until green».
- Якщо тест падає без зміни коду, спочатку шукаємо джерело нестабільності.
- Усе, що залежить від часу, мережі й випадковості, ізолюємо або фіксуємо.
Це хороший приклад того, як CLAUDE.md допомагає не лише писати код, а й тримати інженерну дисципліну. Claude Code запропонує виправити нестабільний селектор, помітить привʼязку до системного часу, підкаже підмінити зовнішній API контрольованою фікстурою. Але рішення знову людське: визнайте, що червоний flaky — це не «майже зелений», а повноцінний ризик.
Для Commerce OS особливо важливо стежити, щоб широкі тести не залежали від випадкових факторів на кшталт реального платіжного провайдера, плаваючої дати чи нестабільної пошти. Інакше отримаєте дивну ситуацію: E2E наче перевіряє повернення грошей, а половина падінь — через те, що тестовий провайдер відповів на секунду пізніше. Це вже не доказ поведінки, а метеозведення.
6. Claude Code у широких перевірках: не оракул
На широких перевірках Claude Code особливо корисний як прискорювач рутинної частини: накине smoke-скрипт, каркас Playwright-тесту, допоможе вибрати точку для regression, розбере лог упалого E2E, пояснить, чому баг краще страхувати integration-тестом, а не браузерним сценарієм. Але якщо дати йому занадто багато свободи, він із тією ж легкістю напише десять красивих і майже марних тестів просто тому, що їх легко згенерувати.
Тому добра практика — спочатку просити Claude допомогти з дизайном, а не одразу з кодом. Наприклад так:
Подивися на поточний PR і TEST_STRATEGY.md.
Запропонуй:
1) один smoke-сценарій,
2) чи потрібен тут E2E,
3) який regression-тест є обовʼязковим після фікса.
Код поки не пиши.
Такий запит дуже корисний, бо утримує правильний порядок мислення: спочатку роль перевірки, потім код. Інакше все закінчується звичною AI-магією — модель приносить Playwright на сорок рядків, а ви потім гадаєте, навіщо він зʼявився.
Claude особливо добрий ще й у розборі вже наявних падінь. За логами пояснить, що smoke упав через неправильний URL, а не через бізнес-логіку. Помітить, що regression нічого не доводить, якщо проходить і на старій поведінці. Вкаже, що E2E-сценарій надто широкий і перевіряє одразу три незалежні шляхи. Але фінальна робота залишається за вами: прочитати лог, подивитися screenshot, вирішити, чи взагалі має існувати сценарій і чи виправдовує він свою вартість.
Широкі перевірки коштують довіри, а не кількості. Smoke варто тримати коротким — хвилинний сигнал, що застосунок живий. E2E залишати на кілька критичних шляхів, де збій бʼє по грошах або користувачеві. Regression заводити на кожен баг, який уже дійшов до продакшну, щоб він не повернувся мовчки. А довіра до цього набору тримається на одному: жоден тест у ньому не має бути flaky — інакше червоний сигнал перетворюється на лотерею, і вся страховка знецінюється. Claude Code прискорює збирання кожного з цих трьох типів, але роль і вартість перевірки обираєте ви. Далі завдання — не зіпсувати цю страховку шумними даними, випадковими граничними випадками й слабкими асерціями.
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ