🧨 Руйнівники IT-міфів: "End-to-End тести мають перевіряти бізнес-логіку бази даних"
Привіт, екіпаж! Сьогодні дістаємо з шафи міф, який змушує ваші E2E-тести працювати годинами і падати через кожну дрібницю. Ця помилка особливо актуальна для тих, хто автоматизує складні системи на сучасних стеках (як-от NestJS + PostgreSQL). ☕️
❌ Міф: "У моєму E2E-тесті користувач проходить співбесіду в 'QA Co-pilot'. В кінці я повинен написати запит до PostgreSQL, дістати його профіль і перевірити, чи правильно ORM зберіг score та history_id у таблицях."
💥 Реальність (Бум!):
End-to-End (UI) тести не повинні лазити в базу даних. Їхня мета — перевірити, чи працює система з точки зору кінцевого користувача. Користувач не має доступу до вашої PostgreSQL.
Він бачить лише те, що відображається в браузері (наприклад, графік результатів або повідомлення "Співбесіду пройдено").
✅ Як має бути (Здоровий підхід):
Золоте правило: Тестуйте систему через ті самі інтерфейси, якими користується клієнт. UI-тести — для UI. API-тести — для логіки БД. Не змішуйте їх у монстрів!
А ваші E2E-тести ходять у базу даних? 👇
🔥 — Ні, перевіряю тільки те, що бачить юзер на екрані!
👀 — Грішу... Підключаюсь до БД, щоб бути на 100% впевненим.
🤯 — Стоп, тобто якщо кнопка натиснулась, мені не треба перевіряти, куди полетіли дані?!
Привіт, екіпаж! Сьогодні дістаємо з шафи міф, який змушує ваші E2E-тести працювати годинами і падати через кожну дрібницю. Ця помилка особливо актуальна для тих, хто автоматизує складні системи на сучасних стеках (як-от NestJS + PostgreSQL). ☕️
❌ Міф: "У моєму E2E-тесті користувач проходить співбесіду в 'QA Co-pilot'. В кінці я повинен написати запит до PostgreSQL, дістати його профіль і перевірити, чи правильно ORM зберіг score та history_id у таблицях."
💥 Реальність (Бум!):
End-to-End (UI) тести не повинні лазити в базу даних. Їхня мета — перевірити, чи працює система з точки зору кінцевого користувача. Користувач не має доступу до вашої PostgreSQL.
Він бачить лише те, що відображається в браузері (наприклад, графік результатів або повідомлення "Співбесіду пройдено").
Якщо ви додаєте в E2E-тест прямі запити до БД (використовуючи Prisma чи pg), ви порушуєте межі абстракції.
🔹Подвійна робота: Перевірка правильності збереження даних у таблицях — це зона відповідальності Integration-тестів на бекенді (на рівні ваших NestJS сервісів та контролерів).
🔹Крихкість: Якщо бекендер змінить назву колонки зі score на total_points, ваш E2E-тест впаде, хоча візуально в Angular-додатку все продовжує працювати ідеально!
✅ Як має бути (Здоровий підхід):
🔹E2E (Playwright/Cypress): Користувач натискає "Завершити", чекає на редирект, бачить текст "Твій результат: 85%". Крапка. Ніяких підключень до БД.
🔹API/Integration (Бекенд): Тест викликає ендпоінт POST /api/interviews/finish, а потім перевіряє через бекенд-інфраструктуру, що дані коректно лягли в PostgreSQL.
Золоте правило: Тестуйте систему через ті самі інтерфейси, якими користується клієнт. UI-тести — для UI. API-тести — для логіки БД. Не змішуйте їх у монстрів!
А ваші E2E-тести ходять у базу даних? 👇
🔥 — Ні, перевіряю тільки те, що бачить юзер на екрані!
👀 — Грішу... Підключаюсь до БД, щоб бути на 100% впевненим.
🤯 — Стоп, тобто якщо кнопка натиснулась, мені не треба перевіряти, куди полетіли дані?!
❤1
🧠 Задача з Senior співбесіди: "Повільний бекенд і швидкі пальці"
Умови задачі:
Питання на співбесіді:
Як 100% надійно автоматизувати перевірку цього багу в E2E-тестах (Playwright/Cypress)?
❌ Відповідь рівня Junior (Хибний шлях):
✅ Відповідь рівня Senior (Підхід Архітектора):
Ми використаємо Network Interception (Мокування мережі), щоб штучно створити цей Race Condition (стан гонитви) прямо в тесті.
Як би ви відповіли на це питання? 👇
🔥 — Ізі, постійно мокаю мережу для таких кейсів!
👀 — Чесно? Ніколи не тестував Race Conditions...
🤯 — А що, можна ставити запити "на паузу" під час тесту?!
Умови задачі:
У вас є поле пошуку (Autocomplete). Коли юзер вводить текст, фронтенд відправляє запити на бекенд.
1️⃣ Юзер вводить літеру "M". Летить запит GET /search?q=M.
2️⃣ Юзер одразу дописує "A". Летить другий запит GET /search?q=MA.
3️⃣ Мережа нестабільна: другий запит (MA) відпрацював швидко (за 100 мс). А перший (M) "завис" і повернув відповідь аж через 500 мс.
Результат на UI: Фронтенд спочатку показав правильні результати для "MA", а потім їх перезаписала запізніла відповідь для "M". Користувач бачить неактуальні дані.
Питання на співбесіді:
Як 100% надійно автоматизувати перевірку цього багу в E2E-тестах (Playwright/Cypress)?
❌ Відповідь рівня Junior (Хибний шлях):
"Я просто напишу текст дуже швидко fill('MA') або поставлю sleep(500) між натисканнями клавіш і буду сподіватися, що злову цей лаг мережі".
Чому це провал: Тест буде "моргаючим" (flaky). Ви не контролюєте реальну мережу в CI/CD.
✅ Відповідь рівня Senior (Підхід Архітектора):
Ми використаємо Network Interception (Мокування мережі), щоб штучно створити цей Race Condition (стан гонитви) прямо в тесті.
Алгоритм (на прикладі Playwright):
1️⃣ Перехоплюємо запити (через page.route()).
2️⃣ Коли летить запит q=M — ми ставимо його "на паузу" і не віддаємо відповідь.
3️⃣ Коли летить запит q=MA — ми миттєво повертаємо мок-відповідь ["Macbook", "MacMini"].
4️⃣ ТІЛЬКИ ПІСЛЯ ЦЬОГО ми "відпускаємо" перший запит q=M із запізнілою відповіддю ["Mouse", "Monitor"].
5️⃣ Перевіряємо UI: на екрані має залишитись результат "Macbook" (фронтенд має ігнорувати старі запити).
Як би ви відповіли на це питання? 👇
🔥 — Ізі, постійно мокаю мережу для таких кейсів!
👀 — Чесно? Ніколи не тестував Race Conditions...
🤯 — А що, можна ставити запити "на паузу" під час тесту?!
🕵️♂️ Інженерний детектив: Справа про "Раптовий логаут" (або Гонка Refresh-токенів)
Привіт, екіпаж! Середа — час розслідувань. Сьогодні розбираємо баг, який регулярно доводить користувачів до сказу, але розробники часто закривають його з резолюцією "Не відтворюється на моїй машині". ☕️
Сцена злочину (Симптоми):
❌ Хибний слід:
🔍 Що сталося насправді (Race Condition в HTTP-інтерцепторі):
✅ Вирок та Рішення (Як це має перевіряти QA):
Архітектурно ця проблема фікситься на клієнті (наприклад, через RxJS-оператори в Angular або черги в Axios). Коли фронтенд ловить перший 401, він має "заморозити" всі інші запити, оновити токен ОДИН раз, і тільки потім випустити чергу запитів зі свіжим ключем.
Як QA миттєво зловити цей баг:
А вас часто раптово розлогінює в додатках? 👇
🔥 — Тепер зрозуміло! У нас ідеальна черга запитів, перевіряв особисто.
👀 — Чесно? Я думав, це просто бекенд перезавантажився...
🤯 — Пішов псувати свій токен у DevTools, сподіваюсь, ми це пофіксили!
Привіт, екіпаж! Середа — час розслідувань. Сьогодні розбираємо баг, який регулярно доводить користувачів до сказу, але розробники часто закривають його з резолюцією "Не відтворюється на моїй машині". ☕️
Сцена злочину (Симптоми):
Користувач відкриває ваш додаток вранці. Сторінка починає завантажуватись, але замість звичного дашборду його раптово викидає на екран логіну з повідомленням "Сесія закінчилася". Юзер дивується, адже він ставив галочку "Запам'ятати мене" на 30 днів. Він логіниться знову — і все працює ідеально.
❌ Хибний слід:
Джун-тестувальник іде дивитися конфіги бекенду і бачить, що час життя Refresh-токена дійсно 30 днів. Він намагається зловити баг руками, але не може чекати місяць.
Заводить тікет зі статусом "Плаваюча бага: злітає авторизація", який зависає в беклозі на півроку.
🔍 Що сталося насправді (Race Condition в HTTP-інтерцепторі):
Коли ви відкриваєте сучасний додаток, фронтенд зазвичай робить кілька запитів одночасно (наприклад: отримати профіль, стрічку новин, лічильник сповіщень).
Ваш швидкий Access-токен (який живе 15 хвилин) "протух" за ніч. Усі 3 паралельні запити одночасно б'ються об бекенд і отримують помилку 401 Unauthorized.
Що робить "наївний" фронтенд? Він миттєво відправляє ТРИ паралельні запити на POST /refresh, щоб оновити сесію.
Бекенд (з міркувань безпеки) обробляє перший запит, видає свіжі токени і назавжди анулює старий Refresh-токен.
Але щойно бекенд отримує другий і третій запити з тим самим старим Refresh-токеном, він розцінює це як спробу крадіжки сесії хакером! Захист спрацьовує, база даних повністю вбиває сесію, і сервер відправляє жорсткий 401. Фронтенд слухняно викидає юзера на екран логіну.
✅ Вирок та Рішення (Як це має перевіряти QA):
Архітектурно ця проблема фікситься на клієнті (наприклад, через RxJS-оператори в Angular або черги в Axios). Коли фронтенд ловить перший 401, він має "заморозити" всі інші запити, оновити токен ОДИН раз, і тільки потім випустити чергу запитів зі свіжим ключем.
Як QA миттєво зловити цей баг:
Вам не треба чекати 15 хвилин. Зайдіть у додаток, відкрийте DevTools -> Application -> LocalStorage. Знайдіть свій Access-токен і просто видаліть з нього пару літер. Далі оновіть сторінку або перейдіть на "важку" вкладку, яка робить багато запитів. Якщо вас викинуло на екран логіну — Інтерцептор не вміє ставити запити в чергу!
А вас часто раптово розлогінює в додатках? 👇
🔥 — Тепер зрозуміло! У нас ідеальна черга запитів, перевіряв особисто.
👀 — Чесно? Я думав, це просто бекенд перезавантажився...
🤯 — Пішов псувати свій токен у DevTools, сподіваюсь, ми це пофіксили!
📝 Ультимативна Шпаргалка: Test Doubles (або Перестаньте називати все "Моками")
Скільки разів на дейліках ви чули фразу: "Я тут замокАв базу даних і платіжку"? У 90% випадків інженери використовують термін "Mock" неправильно, називаючи так будь-яку підміну реального об'єкта.
Ось класифікація Test Doubles (Тестових дублерів) від Мартіна Фаулера. Збережіть, щоб сяяти на код-рев'ю та співбесідах. ☕️
1️⃣ Dummy (Лялька-манекен)
2️⃣ Stub (Заглушка)
3️⃣ Spy (Шпигун)
4️⃣ Mock (Справжній Мок)
5️⃣ Fake (Фальшивка)
Золоте правило: Якщо ви просто повертаєте статику — це Stub. Якщо перевіряєте кількість викликів — це Spy. Не називайте заглушки моками!
Зізнавайтесь, називаєте все підряд "моками"? 👇
🔥 — Тепер буду душнити на код-рев'ю і виправляти колег!
👀 — Я все життя називав це моками і буду називати далі...
🤯 — Fake та Stub — це не одне й те саме?!
Скільки разів на дейліках ви чули фразу: "Я тут замокАв базу даних і платіжку"? У 90% випадків інженери використовують термін "Mock" неправильно, називаючи так будь-яку підміну реального об'єкта.
Ось класифікація Test Doubles (Тестових дублерів) від Мартіна Фаулера. Збережіть, щоб сяяти на код-рев'ю та співбесідах. ☕️
1️⃣ Dummy (Лялька-манекен)
Що це: Об'єкти, які передаються у функцію просто для того, щоб компілятор чи TypeScript не лаявся. Вони НІКОЛИ не використовуються всередині.
Приклад: Ви тестуєте метод createUser(name, email, avatarUrl). Для тесту вам важливе тільки ім'я, тому замість avatarUrl ви передаєте null або порожній рядок. Це — Dummy.
2️⃣ Stub (Заглушка)
Що це: Тупа підміна, яка завжди повертає захардкоджену відповідь, незалежно від вхідних даних.
Приклад: Ви перехоплюєте запит GET /profile і налаштовуєте його так, щоб він завжди повертав {"role": "admin"}. Він не перевіряє, які заголовки ви йому передали — він просто віддає статику.
3️⃣ Spy (Шпигун)
Що це: Той самий Stub, але він уміє запам'ятовувати, ЩО з ним робили.
Приклад: Ви підмінили функцію відправки email. Після тесту ви запитуєте Шпигуна: "Скільки разів тебе викликали?" або "З якими аргументами до тебе зверталися?". Ідеально для перевірки сайд-ефектів (наприклад, чи відправилась аналітика після кліку).
4️⃣ Mock (Справжній Мок)
Що це: Найрозумніший дублер. У нього заздалегідь "запрограмовані" очікування (Expectations).
Приклад: Ви кажете йому: "Якщо тебе викличуть з ID=5, поверни 200 OK. Якщо з ID=99 — викинь помилку 404. А якщо тебе взагалі не викличуть — завали тест!". Мок сам керує логікою проходження тесту.
5️⃣ Fake (Фальшивка)
Що це: Робоча імплементація, яка робить те саме, що й продакшен-код, але зрізає кути для швидкості.
Приклад: Замість реальної PostgreSQL ви піднімаєте In-Memory базу (наприклад, SQLite), яка зберігає дані в оперативці. Вона реально працює, зберігає і видаляє, але на проді її використовувати не можна.
Золоте правило: Якщо ви просто повертаєте статику — це Stub. Якщо перевіряєте кількість викликів — це Spy. Не називайте заглушки моками!
Зізнавайтесь, називаєте все підряд "моками"? 👇
🔥 — Тепер буду душнити на код-рев'ю і виправляти колег!
👀 — Я все життя називав це моками і буду називати далі...
🤯 — Fake та Stub — це не одне й те саме?!
🤝1
💩 Код з душком: Спільні тестові дані (або Прокляття testuser@gmail.com)
Привіт, екіпаж! П'ятниця — час вивітрювати антипатерни. Сьогодні говоримо про звичку, яка працює на локальному комп'ютері, але перетворює CI/CD пайплайн на суцільне мінне поле. ☕️
Знайдіть проблему в цьому підході:
Чому цей код тхне:
✅ Як це виглядає після код-рев'ю Senior-інженера:
Тести мають дотримуватись принципу F.I.R.S.T. (зокрема, бути Isolated/Ізольованими). Кожен тест повинен жити у своєму вакуумі і генерувати власні унікальні дані.
Золоте правило: Ніколи не хардкодьте імейли чи ID сутностей у тестах. Використовуйте патерн Data Factory / API-генерацію. Тест має сам створити собі "пісочницю", погратися в ній і (бажано) прибрати за собою.
А ваші тести б'ються за один імейл? 👇
🔥 — Ні, у нас API генерує унікальні дані для кожного прогону!
👀 — Грішу... У мене є файл test-users.json на 5 акаунтів...
🤯 — То ось чому мої паралельні тести вічно падають!
Привіт, екіпаж! П'ятниця — час вивітрювати антипатерни. Сьогодні говоримо про звичку, яка працює на локальному комп'ютері, але перетворює CI/CD пайплайн на суцільне мінне поле. ☕️
Знайдіть проблему в цьому підході:
// ❌ Антипатерн "Гуртожиток" (Shared State)
test.beforeEach(async ({ page }) => {
// Усі тести у сьюті логіняться під ОДНИМ юзером
await loginPage.login('qa-test@company.com', 'Qwerty1234');
});
test('Користувач може видалити товар з кошика', async ({ page }) => {
await cartPage.deleteItem();
await expect(page.locator('.empty-cart')).toBeVisible();
});
Чому цей код тхне:
Коли ви запускаєте ці тести по одному на своєму ноуті — все ідеально. Але щойно ви налаштовуєте паралельний запуск (наприклад, workers: 4 у Playwright), починається хаос.
Один тест намагається покласти товар у кошик, а паралельний тест ТІЄЇ Ж СЕКУНДИ натискає кнопку "Очистити кошик" (бо вони сидять під одним акаунтом!). Тести починають вбивати один одного. З'являються сотні "плаваючих" (flaky) багів.
✅ Як це виглядає після код-рев'ю Senior-інженера:
Тести мають дотримуватись принципу F.I.R.S.T. (зокрема, бути Isolated/Ізольованими). Кожен тест повинен жити у своєму вакуумі і генерувати власні унікальні дані.
// 🚀 Ідеально чистий код (Data Isolation)
test.beforeEach(async ({ page, api }) => {
// Динамічно створюємо УНІКАЛЬНОГО юзера для КОЖНОГО тесту через API
const testUser = await api.createUser();
await loginPage.login(testUser.email, testUser.password);
});
test('Користувач може видалити товар з кошика', async ({ page }) => {
// Тест працює у повній ізоляції. Ніхто інший не чіпає цей кошик.
await cartPage.deleteItem();
await expect(page.locator('.empty-cart')).toBeVisible();
});
Золоте правило: Ніколи не хардкодьте імейли чи ID сутностей у тестах. Використовуйте патерн Data Factory / API-генерацію. Тест має сам створити собі "пісочницю", погратися в ній і (бажано) прибрати за собою.
А ваші тести б'ються за один імейл? 👇
🔥 — Ні, у нас API генерує унікальні дані для кожного прогону!
👀 — Грішу... У мене є файл test-users.json на 5 акаунтів...
🤯 — То ось чому мої паралельні тести вічно падають!
📡 Tech Radar: Головне за тиждень (15.06 – 21.06.2026) від QA Co-pilot
Привіт, екіпаж! Новий тиждень — нові інструменти, які знімають рутину і дозволяють сфокусуватися на архітектурі. Сьогоднішній випуск особливо сподобається тим, хто розробляє сучасні мікросервіси та трендові Telegram-додатки. Поїхали! ☕️
📱Playwright TMA Bridge: Нативна підтримка Telegram Mini Apps
🗄 Prisma "Isolated Testing": Ізоляція баз даних з коробки
⚡️Bruno CLI 2.0: Автоматичні Markdown-звіти
Що з цього піде у ваш беклог на впровадження? 👇
🔥 — TMA Bridge! Якраз робимо апку для Телеграму, E2E-тести дуже потрібні.
👀 — Prisma з її ізоляцією — це просто мрія для нашого NestJS-бекенду.
🤯 — Bruno CLI: пішов зносити Postman і налаштовувати CI/CD!
Привіт, екіпаж! Новий тиждень — нові інструменти, які знімають рутину і дозволяють сфокусуватися на архітектурі. Сьогоднішній випуск особливо сподобається тим, хто розробляє сучасні мікросервіси та трендові Telegram-додатки. Поїхали! ☕️
📱Playwright TMA Bridge: Нативна підтримка Telegram Mini Apps
Тестування Telegram Mini Apps завжди було милицею: доводилося вручну мокати об'єкт window.Telegram.WebApp та його івенти, щоб запустити апку в звичайному браузері без клієнта Telegram.
Спільнота випустила офіційний плагін-мост. Тепер ви просто підключаєте його в playwright.config.ts, і ваші E2E-тести отримують повний контроль над "віртуальним" Telegram-оточенням (можна емілювати натискання Main Button, Back Button, зміну теми на темну/світлу та Haptic Feedback).
🗄 Prisma "Isolated Testing": Ізоляція баз даних з коробки
Для тих, хто сидить на стеку NestJS + PostgreSQL + Prisma. Організація паралельного тестування завжди впиралася в конфлікти даних. Раніше треба було писати складні скрипти для створення окремих схем під кожного воркера.
У новому апдейті Prisma додала експериментальну фічу prisma_test_environment. Вона автоматично піднімає ізольовану транзакцію або окрему схему (schema) у PostgreSQL для кожного тест-кейсу і миттєво відкочує (rollback) її після завершення.
Результат: Ваші паралельні API-тести більше ніколи не перетнуться.
⚡️Bruno CLI 2.0: Автоматичні Markdown-звіти
Ми вже "смажили" Postman за його неповороткість. Тим часом опенсорсний Bruno стрімко захоплює ринок. У новій версії їхнього CLI з'явилася нативна генерація красивих Markdown-звітів (з підтримкою Glassmorphism-дизайну у HTML-експорті).
Результат: Ви просто додаєте один прапорець у GitHub Actions, і після прогону API-тестів у ваш Pull Request автоматично публікується стильний і зрозумілий коментар зі статусом усіх ендпоінтів.
Що з цього піде у ваш беклог на впровадження? 👇
🔥 — TMA Bridge! Якраз робимо апку для Телеграму, E2E-тести дуже потрібні.
👀 — Prisma з її ізоляцією — це просто мрія для нашого NestJS-бекенду.
🤯 — Bruno CLI: пішов зносити Postman і налаштовувати CI/CD!
🤯1
⚔️ Битва підходів: Підготовка стану (UI-логін проти API-ін'єкції)
Сьогоднішня битва присвячена найчастішій дії в автотестах — авторизації. Уявіть, що у вас 100 тест-кейсів, і для кожного юзер має бути залогінений. Як ми це робимо? ☕️
🥊 Підхід 1: UI-авторизація (Як роблять новачки)
🥊 Підхід 2: API-ін'єкція стану (Рівень Архітектора)
⚖️ Вердикт QA Co-pilot:
Сьогоднішня битва присвячена найчастішій дії в автотестах — авторизації. Уявіть, що у вас 100 тест-кейсів, і для кожного юзер має бути залогінений. Як ми це робимо? ☕️
🥊 Підхід 1: UI-авторизація (Як роблять новачки)
У блоці beforeEach тест відкриває сторінку /login, знаходить поле email, вводить текст, знаходить password, вводить пароль, натискає кнопку і чекає на редирект.
Плюси: Тест на 100% імітує дії реального користувача.
Мінуси: Це катастрофічно повільно. Якщо UI-логін займає 3 секунди, на 100 тестах ви втрачаєте 5 хвилин просто на введення паролів. А якщо фронтендери змінять дизайн або додадуть капчу — впадуть усі 100 тестів одночасно.
🥊 Підхід 2: API-ін'єкція стану (Рівень Архітектора)
Ви взагалі не відкриваєте сторінку логіну. Ви робите один швидкий HTTP-запит POST /api/auth, отримуєте JWT-токен або Cookie, і напряму "впорскуєте" його в контекст браузера.
Плюси: Авторизація займає 50 мілісекунд замість 3 секунд. Тести повністю ізольовані від змін в UI екрану логіну.
Мінуси: Виникає страх "А раптом сама форма логіну зламалася, а ми це пропустили?".
⚖️ Вердикт QA Co-pilot:
Запам'ятайте золоте правило автоматизації: Тестуйте UI логіну тільки в тестах на логін.
Вам потрібен рівно ОДИН тест, який чесно проходить через UI форму авторизації (перевіряє валідацію, повідомлення про помилки тощо).
Для всіх інших 99 тестів (кошик, профіль, пошук) екран логіну — це просто перешкода. Використовуйте API-ін'єкцію (у Playwright це робиться через request.post та browserContext.addCookies()), щоб миттєво підготувати стан і тестувати саме те, що треба.
🤝1
🧨 Руйнівники IT-міфів: "Справжній E2E-тест має клікати по реальному Google Auth чи Stripe"
Привіт, екіпаж! Сьогодні розбираємо міф, через який автотести обростають "милицями", падають на CAPTCHA і блокуються сторонніми сервісами. ☕️
❌ Міф: "Ми ж пишемо End-to-End! Тому мій Playwright-тест повинен чесно відкрити вікно авторизації Google, ввести реальний логін і пароль, або відкрити справжній віджет Stripe і ввести тестову картку".
💥 Реальність (Бум!):
✅ Як має бути (Підхід Архітектора):
Золоте правило: E2E-тест гарантує, що ваші мікросервіси спілкуються між собою. Чужі мікросервіси хай тестують їхні власні QA.
А ваші тести намагаються обдурити Google чи платіжки? 👇
🔥 — Ніколи! Мокаємо сторонні сервіси або юзаємо API-токени.
👀 — Грішу... Мої тести реально вводять логін і пароль у попапі Google.
🤯 — То ось чому мої тести періодично падають з помилкою Access Denied!
Привіт, екіпаж! Сьогодні розбираємо міф, через який автотести обростають "милицями", падають на CAPTCHA і блокуються сторонніми сервісами. ☕️
❌ Міф: "Ми ж пишемо End-to-End! Тому мій Playwright-тест повинен чесно відкрити вікно авторизації Google, ввести реальний логін і пароль, або відкрити справжній віджет Stripe і ввести тестову картку".
💥 Реальність (Бум!):
Ви не працюєте в Google і не працюєте в Stripe.
Ви не повинні тестувати чужий UI.
🔹CAPTCHA вас знищить: Алгоритми безпеки Google, Facebook або платіжних систем миттєво розпізнають автоматизований браузер (headless mode). Рано чи пізно ваш тест впреться у нескінченну капчу або блокування IP-адреси CI/CD сервера.
🔹Нестабільність (Flakiness): Сторонній сервіс може проводити А/В тестування свого UI, змінити селектори або просто мати мікро-збій. Ваш тест впаде, хоча ВАШ додаток працює ідеально.
✅ Як має бути (Підхід Архітектора):
Тестуйте лише кордони свого додатку. Вас цікавить лише те, як ваша система реагує на успішну чи неуспішну відповідь від стороннього сервісу.
🔹Для авторизації (OAuth): Замість кліків по UI Google, зробіть один API-запит до вашого бекенду, щоб згенерувати тестовий JWT-токен, і покладіть його в localStorage браузера (ми розбирали це в "Битві підходів"). Або використовуйте page.route(), щоб замокати відповідь від провайдера авторизації.
🔹Для платежів: Якщо ви змушені клікати по віджету Stripe — використовуйте їхні офіційні тестові API-ключі або спеціальні інструменти CLI (Stripe CLI) для тригерингу вебхуків про успішну оплату, минаючи UI.
Золоте правило: E2E-тест гарантує, що ваші мікросервіси спілкуються між собою. Чужі мікросервіси хай тестують їхні власні QA.
А ваші тести намагаються обдурити Google чи платіжки? 👇
🔥 — Ніколи! Мокаємо сторонні сервіси або юзаємо API-токени.
👀 — Грішу... Мої тести реально вводять логін і пароль у попапі Google.
🤯 — То ось чому мої тести періодично падають з помилкою Access Denied!
🕵️♂️ Інженерний детектив: Справа про "Привида з бази" (або Пастка Soft Delete)
Привіт, екіпаж! Середа — час нашого розслідування. Сьогодні шукаємо архітектурний баг, який змушує сервер сипати помилками, а клієнтів — писати гнівні листи в саппорт. ☕️
Сцена злочину (Симптоми):
❌ Хибний слід:
🔍 Що сталося насправді (Ілюзія видалення):
✅ Вирок та Рішення (Підхід Архітектора):
Як QA миттєво зловити цей баг:
А як у вас працює видалення? 👇
🔥 — Завжди перевіряю ре-крейт, привиди в базі не пройдуть!
👀 — Зазвичай просто тисну "Видалити", перевіряю список і йду далі...
🤯 — Так ось чому наші юзери не можуть зареєструватись під старим email!
Привіт, екіпаж! Середа — час нашого розслідування. Сьогодні шукаємо архітектурний баг, який змушує сервер сипати помилками, а клієнтів — писати гнівні листи в саппорт. ☕️
Сцена злочину (Симптоми):
Ви тестуєте звичайний CRUD. Створюєте новий проєкт (або акаунт) з унікальною назвою MySuperApp. Все працює. Потім ви вирішуєте його видалити. UI слухняно показує тост "Успішно видалено", список проєктів порожній.
Але щойно ви намагаєтесь створити проєкт із назвою MySuperApp заново — бекенд "вибухає" і повертає 500 Internal Server Error (або 409 Conflict). Юзер в шоці: «Чому ім'я зайняте, якщо я його щойно видалив?!».
❌ Хибний слід:
Джун-тестувальник заводить тікет. Розробник дивиться в логи бази даних, бачить порушення унікальності і просто додає на UI красиву помилку: "Таке ім'я вже існує". Але бізнес-проблему це не вирішує — користувач все ще не може використати своє законне ім'я.
🔍 Що сталося насправді (Ілюзія видалення):
Майже всі сучасні бекенди (особливо на NestJS + Prisma/TypeORM) використовують Soft Delete (м'яке видалення). Коли ви тиснете "Видалити", рядок фізично не зникає з PostgreSQL. Йому просто ставиться позначка deleted_at: "2026-06-24", і бекенд починає ігнорувати його у вибірках.
Але архітектор забув оновити Unique Constraint (унікальний індекс) у самій базі! База даних "бачить" видалений рядок і жорстко блокує створення нового з таким самим іменем.
✅ Вирок та Рішення (Підхід Архітектора):
Бекендер має змінити індекс у базі на Partial Index (унікальність діє тільки там, де deleted_at IS NULL), або ж логіка бекенду має знаходити "м'яко видалений" запис і "відновлювати" його замість створення нового.
Як QA миттєво зловити цей баг:
Ніколи не закінчуйте тест на видалення простою перевіркою, що елемент зник з UI.
Золоте правило: Видалив? Спробуй створити точно такий самий ще раз! Тільки успішний ре-крейт гарантує, що Soft Delete реалізовано правильно.
А як у вас працює видалення? 👇
🔥 — Завжди перевіряю ре-крейт, привиди в базі не пройдуть!
👀 — Зазвичай просто тисну "Видалити", перевіряю список і йду далі...
🤯 — Так ось чому наші юзери не можуть зареєструватись під старим email!
👍1
📝 Ультимативна Шпаргалка: Ієрархія локаторів (від крихких до куленепробивних)
Скільки разів ваші автотести ставали "червоними" просто тому, що фронтендер змінив колір кнопки або обгорнув її в новий <div>?
Сьогодні розбираємо піраміду локаторів для Playwright/Cypress. Збережіть це, щоб більше ніколи не витрачати години на фікси впавших селекторів. ☕️
🔴 Рівень 1: Шлях у нікуди (CSS класи та XPath)
🟡 Рівень 2: Ілюзія надійності (ID елементів)
🟢 Рівень 3: Золотий стандарт тестувальника (Data-атрибути)
👑 Рівень 4: Рівень Архітектора (Accessibility / ARIA)
Золоте правило: Якщо ви не можете знайти елемент через getByRole або getByText, вимагайте у розробників додати data-testid. Ніколи не прив'язуйтесь до CSS-класів!
За що чіпляються ваші тести? 👇
🔥 — Тільки data-testid або getByRole, мої тести куленепробивні!
👀 — Пишу через класи (.button-active), страждаю під час кожного релізу.
🤯 — Щойно скопіював XPath із DevTools на 5 рядків... Допоможіть!
Скільки разів ваші автотести ставали "червоними" просто тому, що фронтендер змінив колір кнопки або обгорнув її в новий <div>?
Сьогодні розбираємо піраміду локаторів для Playwright/Cypress. Збережіть це, щоб більше ніколи не витрачати години на фікси впавших селекторів. ☕️
🔴 Рівень 1: Шлях у нікуди (CSS класи та XPath)
Приклади: xpath=/html/body/div[2]/button або .btn.btn-primary.mt-2
Чому це сміття: Вони прив'язані до візуального стилю або структури DOM. Верстальник перейменував клас або додав обгортку — ваш тест вмер. Використовувати тільки під дулом пістолета.
🟡 Рівень 2: Ілюзія надійності (ID елементів)
Приклади: #submit-order-btn
Чому це не ідеал: Звучить добре, але в сучасних фреймворках ID часто генеруються динамічно, щоб уникнути конфліктів. Плюс, розробники часто забувають робити їх унікальними на сторінці.
🟢 Рівень 3: Золотий стандарт тестувальника (Data-атрибути)
Приклади: [data-testid="submit-order"] або [data-qa="login-button"]
Чому це круто: Ці атрибути додаються виключно для QA. Дизайнер може повністю перемалювати сторінку, змінити теги та стилі, але data-testid залишиться на місці. Це ваш залізобетонний контракт із фронтенд-командою.
👑 Рівень 4: Рівень Архітектора (Accessibility / ARIA)
Приклади: getByRole('button', { name: 'Оплатити' })
Чому це топ: Це підхід сучасного Playwright. Користувач не шукає очима data-testid. Він шукає сутність "Кнопка" з текстом "Оплатити". Тестуючи через Accessibility (ролі), ви перевіряєте не просто наявність елемента, а те, чи правильно він доступний для користувачів (і скрінрідерів).
Золоте правило: Якщо ви не можете знайти елемент через getByRole або getByText, вимагайте у розробників додати data-testid. Ніколи не прив'язуйтесь до CSS-класів!
За що чіпляються ваші тести? 👇
🔥 — Тільки data-testid або getByRole, мої тести куленепробивні!
👀 — Пишу через класи (.button-active), страждаю під час кожного релізу.
🤯 — Щойно скопіював XPath із DevTools на 5 рядків... Допоможіть!
🔥 Прожарка інструментів: Apache JMeter (або Динозавр, який відмовляється вимирати)
Привіт, екіпаж! Четвер — час розпалювати гриль. Сьогодні на нашу решітку потрапляє справжній мастодонт. Інструмент, який вимагають у кожній другій вакансії, але від якого смикається око в будь-якого сучасного інженера. Зустрічайте — Apache JMeter. ☕️
🟢 Як нам це продають:
"Абсолютний індустріальний стандарт навантажувального тестування! Безкоштовний, перевірений часом, підтримує всі протоколи світу. Відкрив інтерфейс, наклікав запити — і поклав сервер!"
🥩 Прожарка (Сувора реальність):
⚖️ Вердикт QA Co-pilot:
А чим ви тестуєте свої сервери на міцність? 👇
🔥 — Давно переїхали на K6, пишу тести кодом і кайфую!
👀 — Сиджу на JMeter... Страждаю, але компанія не дає змінити інструмент.
🤯 — А що, навантаження можна писати просто в TS?!
Привіт, екіпаж! Четвер — час розпалювати гриль. Сьогодні на нашу решітку потрапляє справжній мастодонт. Інструмент, який вимагають у кожній другій вакансії, але від якого смикається око в будь-якого сучасного інженера. Зустрічайте — Apache JMeter. ☕️
🟢 Як нам це продають:
"Абсолютний індустріальний стандарт навантажувального тестування! Безкоштовний, перевірений часом, підтримує всі протоколи світу. Відкрив інтерфейс, наклікав запити — і поклав сервер!"
🥩 Прожарка (Сувора реальність):
1️⃣ XML-пекло (Гітхаб плаче): JMeter зберігає ваші сценарії у .jmx файлах, які під капотом є гігантськими нечитабельними XML-простирадлами. Спробуйте зробити нормальне Code Review такого тесту в Pull Request або (не дай Боже) вирішити Merge-конфлікт. Це просто фізично неможливо.
2️⃣ Програмування мишкою: Замість того, щоб написати звичний if чи цикл for у коді, ви змушені шукати в меню "If Controller", "Loop Controller" і складати логіку з візуальних блоків, як у дитячому Scratch. Гнучкість прямує до нуля.
3️⃣ Апетит Java-машини: Інтерфейс виглядає так, ніби пережив нульові, і при цьому жере оперативну пам'ять як не в собі. А якщо ви забудетесь і запустите серйозне навантаження прямо з GUI-режиму — ваш ноутбук швидко перетвориться на обігрівач для кімнати.
⚖️ Вердикт QA Co-pilot:
JMeter — це легенда, і ми маємо його поважати. Але його візуальна архітектура безнадійно застаріла. Сучасний стандарт — це підхід "Testing as Code".
Інструменти на кшталт K6 від Grafana дозволяють писати тести на чистому TypeScript чи JavaScript, зберігати їх поруч із вашим бекендом (тим самим NestJS) і легко вбудовувати в CI/CD без жодного болю з XML.
А чим ви тестуєте свої сервери на міцність? 👇
🔥 — Давно переїхали на K6, пишу тести кодом і кайфую!
👀 — Сиджу на JMeter... Страждаю, але компанія не дає змінити інструмент.
🤯 — А що, навантаження можна писати просто в TS?!
🩻 Рентген співбесіди: "Бекенду ще немає, але треба тестувати"
Питання від ліда:
"Фронтендери (наприклад, на вашому улюбленому Angular) вже все зверстали. Але бекенд (на NestJS) ще навіть не починав писати API. У нас є тільки узгоджений Swagger-файл. Як QA гарантувати, що коли вони нарешті зійдуться на тестовому середовищі, все не вибухне?"
❌ Відповідь рівня Junior (Хибний шлях):
"Я просто напишу моки (фейкові дані) для E2E-тестів у Playwright і буду тестувати фронтенд ізольовано. А коли бекенд буде готовий — проженемо реальні тести."
Чому це провал: Ваші моки дуже швидко застаріють. Якщо через тиждень бекендер вирішить перейменувати поле userId на user_id, ваші фронтенд-тести залишаться зеленими (бо моки старі), а на продакшені все впаде.
✅ Відповідь рівня Senior (Підхід Архітектора):
"Нам потрібне Contract Testing (Контрактне тестування), наприклад, через інструмент Pact."
Золоте правило: E2E-тести занадто повільні для перевірки інтеграції двох команд. Контрактні тести — це автоматизований "договір" між клієнтом і сервером, який не дозволяє їм зламати один одного.
А як ви тестуєте розірвані команди? 👇
🔥 — Pact — наш бро! Ловимо невідповідності ще в пайплайнах.
👀 — Пишемо E2E з моками і молимось, щоб бекенд нічого не змінив.
🤯 — Стоп, тобто фронтенд може диктувати бекенду, які дані повертати?!
Питання від ліда:
"Фронтендери (наприклад, на вашому улюбленому Angular) вже все зверстали. Але бекенд (на NestJS) ще навіть не починав писати API. У нас є тільки узгоджений Swagger-файл. Як QA гарантувати, що коли вони нарешті зійдуться на тестовому середовищі, все не вибухне?"
❌ Відповідь рівня Junior (Хибний шлях):
"Я просто напишу моки (фейкові дані) для E2E-тестів у Playwright і буду тестувати фронтенд ізольовано. А коли бекенд буде готовий — проженемо реальні тести."
Чому це провал: Ваші моки дуже швидко застаріють. Якщо через тиждень бекендер вирішить перейменувати поле userId на user_id, ваші фронтенд-тести залишаться зеленими (бо моки старі), а на продакшені все впаде.
✅ Відповідь рівня Senior (Підхід Архітектора):
"Нам потрібне Contract Testing (Контрактне тестування), наприклад, через інструмент Pact."
Алгоритм:
1️⃣ Фронтенд пише тест, який генерує "Контракт" (JSON-файл). У ньому чітко написано: "Я відправляю запит X і очікую отримати поле userId у форматі числа".
2️⃣ Цей контракт автоматично завантажується в Pact Broker (спільний репозиторій).
3️⃣ Коли бекендер пише свій код, його CI/CD пайплайн автоматично стягує цей контракт і перевіряє свій API проти нього.
4️⃣ Якщо бекендер перейменував поле або змінив тип даних — його пайплайн падає ще до деплою.
Золоте правило: E2E-тести занадто повільні для перевірки інтеграції двох команд. Контрактні тести — це автоматизований "договір" між клієнтом і сервером, який не дозволяє їм зламати один одного.
А як ви тестуєте розірвані команди? 👇
🔥 — Pact — наш бро! Ловимо невідповідності ще в пайплайнах.
👀 — Пишемо E2E з моками і молимось, щоб бекенд нічого не змінив.
🤯 — Стоп, тобто фронтенд може диктувати бекенду, які дані повертати?!
💩 Код з душком: Жорсткі паузи (або Злочин під назвою sleep())
Привіт, екіпаж! Вивітрюємо антипатерни. Сьогодні говоримо про найпопулярніший (і найгірший) спосіб "полагодити" нестабільний (flaky) тест, який періодично падає через повільний рендер або навантажену мережу. ☕️
Знайдіть проблему в цьому коді:
Чому цей код тхне:
✅ Як це виглядає після код-рев'ю Senior-інженера:
Жорсткі паузи суворо заборонені. Тест повинен чекати рівно стільки, скільки потрібно, спираючись на події системи, а не на годинник.
Золоте правило: Забудьте про sleep() або waitForTimeout(). Чекайте на зникнення лоадера-спінера (waitFor('hidden')), на зміну стану елемента або на конкретний мережевий запит. Зробіть свої тести "розумними", і ваш пайплайн скаже вам дякую.
А як ви боретеся з flaky-тестами? 👇
🔥 — Тільки динамічне очікування API чи подій інтерфейсу!
👀 — Іноді ставлю sleep(2000) і сподіваюсь, що на рев'ю не помітять...
🤯 — А що, можна перехоплювати API прямо з фронта, щоб не чекати?!
Привіт, екіпаж! Вивітрюємо антипатерни. Сьогодні говоримо про найпопулярніший (і найгірший) спосіб "полагодити" нестабільний (flaky) тест, який періодично падає через повільний рендер або навантажену мережу. ☕️
Знайдіть проблему в цьому коді:
// ❌ Антипатерн "Почекун" (Hardcoded Sleep)
test('Завантаження списку транзакцій', async ({ page }) => {
await page.click('#load-transactions');
// Сервер іноді тупить, тому почекаємо 5 секунд "про всяк випадок"
await page.waitForTimeout(5000);
await expect(page.locator('.transaction-item')).toHaveCount(10);
});
Чому цей код тхне:
Це класична ситуація "програш за обома фронтами" (lose-lose).
1️⃣ Крадіжка часу: Якщо бекенд відповів миттєво (за 100 мс), ваш тест все одно стоятиме як укопаний ще 4.9 секунди. Помножте це на 500 тестів, і ваш CI/CD пайплайн перетвориться на нескінченне очікування.
2️⃣ Нульова надійність: Якщо бекенд під навантаженням і відповів за 5.1 секунди — ваш тест гарантовано впаде, незважаючи на паузу.
✅ Як це виглядає після код-рев'ю Senior-інженера:
Жорсткі паузи суворо заборонені. Тест повинен чекати рівно стільки, скільки потрібно, спираючись на події системи, а не на годинник.
// 🚀 Ідеально чистий код (Динамічне очікування мережі)
test('Завантаження списку транзакцій', async ({ page }) => {
// 1. Готуємо "пастку" для запиту до того, як клікнемо
const responsePromise = page.waitForResponse('**/api/transactions');
// 2. Робимо дію
await page.click('#load-transactions');
// 3. Чекаємо на відповідь. Тест піде далі в ту саму мілісекунду, коли вона прийде!
await responsePromise;
await expect(page.locator('.transaction-item')).toHaveCount(10);
});
Золоте правило: Забудьте про sleep() або waitForTimeout(). Чекайте на зникнення лоадера-спінера (waitFor('hidden')), на зміну стану елемента або на конкретний мережевий запит. Зробіть свої тести "розумними", і ваш пайплайн скаже вам дякую.
А як ви боретеся з flaky-тестами? 👇
🔥 — Тільки динамічне очікування API чи подій інтерфейсу!
👀 — Іноді ставлю sleep(2000) і сподіваюсь, що на рев'ю не помітять...
🤯 — А що, можна перехоплювати API прямо з фронта, щоб не чекати?!
❤1
⚔️ Битва підходів: Cucumber (BDD) проти Чистого Коду
П'ятниця — час для найкривавіших архітектурних холіварів. Сьогодні на арені зійшлися два табори, суперечки між якими змушують інженерів звільнятися, а менеджерів — хапатися за серце. Розбираємо тестування через текст проти тестування через код. ☕️
🥊 Підхід 1: BDD / Gherkin (Свідки Cucumber)
🥊 Підхід 2: Code-First (Чистий Playwright/Cypress на TypeScript)
⚖️ Вердикт QA Co-pilot:
А в якому таборі ви? 👇
🔥 — Пишу на чистому TS/JS, ніяких "огірків" у моєму коді!
👀 — У нас BDD... Код перетворився на кашу з декораторів, допоможіть.
🤯 — А що, бізнес реально мав читати наші тести?!
П'ятниця — час для найкривавіших архітектурних холіварів. Сьогодні на арені зійшлися два табори, суперечки між якими змушують інженерів звільнятися, а менеджерів — хапатися за серце. Розбираємо тестування через текст проти тестування через код. ☕️
🥊 Підхід 1: BDD / Gherkin (Свідки Cucumber)
Ви пишете тести людською мовою через ключові слова Given / When / Then.
🔹Як це нам продають: "Бізнес, продакти та мануальні тестувальники зможуть самі читати і навіть писати автотести! У нас буде жива документація!"
🔹Сувора реальність: Це гігантський милиця. Під кожен текстовий рядок When я натискаю кнопку "Купити" AQA-інженер змушений писати мапінг у коді (регулярки або декоратори). Рефакторинг перетворюється на пекло: ви змінили текст кроку — код впав. І найголовніше — за кілька років роботи бізнес жодного разу не відкриє ваш репозиторій, щоб почитати ці тести.
🥊 Підхід 2: Code-First (Чистий Playwright/Cypress на TypeScript)
Ви викидаєте текстовий прошарок і пишете сценарії одразу кодом, використовуючи паттерни на кшталт Page Object або Action Classes.
🔹Переваги: Максимальна швидкість розробки. Строга типізація (TypeScript), миттєвий автокомпліт в IDE, рефакторинг однією кнопкою (F2). Ваші тести гнучкі та легко підтримуються.
🔹Недоліки: Менеджер не зрозуміє, що написано у файлі checkout.spec.ts.
⚖️ Вердикт QA Co-pilot:
BDD — це красива ілюзія, яка добре звучить на конференціях, але на практиці додає 50% оверхеду до часу розробки і підтримки. Для переважної більшості проєктів це абсолютно зайвий баласт.
Здоровий підхід: Пишіть тести чистим кодом, а для менеджерів та мануальників генеруйте красиві HTML-звіти (наприклад, через Allure), де назва тесту test('User can buy item') перетворюється на зрозумілий читабельний рядок. Не робіть інженерів заручниками текстових парсерів.
А в якому таборі ви? 👇
🔥 — Пишу на чистому TS/JS, ніяких "огірків" у моєму коді!
👀 — У нас BDD... Код перетворився на кашу з декораторів, допоможіть.
🤯 — А що, бізнес реально мав читати наші тести?!
❤1
📡 Tech Radar: Головне для QA цього тижня
Привіт, екіпаж! Понеділок — час оновлювати тулбокс. Два інструменти, які зараз активно обговорюють у ком'юніті:
Що затестите першим? 👇
🔥 — Playwright UI Mode!
👀 — Faker.js Seed, давно шукав(ла) це.
Привіт, екіпаж! Понеділок — час оновлювати тулбокс. Два інструменти, які зараз активно обговорюють у ком'юніті:
1️⃣ Playwright UI Mode (Time-Travel Debugging): Якщо ви досі дебажите тести через console.log або повільний інспектор — зупиніться. Запуск із прапорцем --ui відкриває повноцінне вікно, де ви можете буквально "відмотати" час назад. Ви бачите DOM, network та console на кожній мілісекунді виконання тесту.
2️⃣ Faker.js — Deterministic Seeds: Всі люблять генерувати фейкові імена та емейли. Але що робити, коли тест впав, а рандомні дані змінилися при перезапуску? Faker додав можливість задавати faker.seed(123). Тепер ваші дані рандомні, але однакові при кожному прогоні. Ідеально для відтворення плаваючих багів!
Що затестите першим? 👇
🔥 — Playwright UI Mode!
👀 — Faker.js Seed, давно шукав(ла) це.
⚔️ Битва підходів: Візуальні тести (Pixel-Perfect) проти DOM-Асертів
Ще одна тема понеділка! Як перевірити, що ваш Angular-компонент виглядає правильно?
🥊 Підхід 1: Visual Regression (Скріншоти)
🥊 Підхід 2: DOM-Асерти (Логічні перевірки)
Вердикт QA Co-pilot: Скріншотні тести мають право на життя ТІЛЬКИ в Storybook для ізольованих UI-компонентів. В End-to-End тестах використовуйте виключно DOM-перевірки.
А як ви шукаєте візуальні баги? 👇
🔥 — Тільки DOM, не хочу розбирати впавші скріншоти.
👀 — Робимо скріншоти, але боляче...
Ще одна тема понеділка! Як перевірити, що ваш Angular-компонент виглядає правильно?
🥊 Підхід 1: Visual Regression (Скріншоти)
Ви робите скріншот сторінки і порівнюєте його з еталоном.
Мінус: Тести стають надзвичайно крихкими (flaky). Дизайнер змінив тінь на кнопці на 1 піксель, або змінився рендер шрифтів у CI/CD на Linux — і всі тести "почервоніли".
🥊 Підхід 2: DOM-Асерти (Логічні перевірки)
Ви перевіряєте лише те, що важливо: expect(btn).toHaveClass(/primary/) та expect(btn).toBeVisible().
Плюс: Куленепробивна стабільність. Колір кнопки вас не обходить, головне — збережена бізнес-логіка і правильні CSS-класи.
Вердикт QA Co-pilot: Скріншотні тести мають право на життя ТІЛЬКИ в Storybook для ізольованих UI-компонентів. В End-to-End тестах використовуйте виключно DOM-перевірки.
А як ви шукаєте візуальні баги? 👇
🔥 — Тільки DOM, не хочу розбирати впавші скріншоти.
👀 — Робимо скріншоти, але боляче...
👍1
🧨 Руйнівники IT-міфів: "100% Code Coverage означає відсутність багів"
Привіт, екіпаж! Сьогодні б'ємо по найпопулярнішому менеджерському міфу.
❌ Міф: "Наш SonarQube показує 100% покриття коду тестами. Ми можемо спокійно релізити, багів немає!"
💥 Реальність:
✅ Як має бути:
Ганяєтесь за 100% покриттям? 👇
🔥 — Ні, фокусуємось на критичних бізнес-сценаріях!
👀 — У нас KPI на 80% покриття, пишемо тести заради цифри...
Привіт, екіпаж! Сьогодні б'ємо по найпопулярнішому менеджерському міфу.
❌ Міф: "Наш SonarQube показує 100% покриття коду тестами. Ми можемо спокійно релізити, багів немає!"
💥 Реальність:
Code Coverage показує лише те, які рядки коду виконувалися під час тестів. Але він НЕ гарантує, що ви перевірили результат!
Ви можете написати тест, який викликає функцію calculateSalary(), і не написати жодного expect(). Покриття буде 100%, а функція може повертати null.
✅ Як має бути:
Справжній показник надійності — це Mutation Testing (наприклад, Stryker). Цей інструмент штучно вносить баги у ваш код (міняє + на -) і перевіряє, чи впадуть ваші тести. Якщо не впали — ваші тести сліпі, незалежно від відсотків покриття!
Ганяєтесь за 100% покриттям? 👇
🔥 — Ні, фокусуємось на критичних бізнес-сценаріях!
👀 — У нас KPI на 80% покриття, пишемо тести заради цифри...
🧠 Задача з Senior співбесіди: "Подорож у часі"
Продовжуємо вівторок задачею з лайв-кодингу.
Умови: На сайті є банер "Знижка згорить завтра об 11:00". Як автоматизувати перевірку того, що банер дійсно зникає, коли настає час Х?
❌ Відповідь Джуна: "Я створю тестовий банер, який зникає через 5 секунд, поставлю sleep(5000) у тесті і перевірю." (Погано: хардкод логіки під тести).
✅ Відповідь Сеньйора:
Мокаєте час у тестах? 👇
🔥 — Постійно, це єдиний спосіб тестувати таймери!
👀 — Чесно? Навіть не знав(ла), що так можна.
Продовжуємо вівторок задачею з лайв-кодингу.
Умови: На сайті є банер "Знижка згорить завтра об 11:00". Як автоматизувати перевірку того, що банер дійсно зникає, коли настає час Х?
❌ Відповідь Джуна: "Я створю тестовий банер, який зникає через 5 секунд, поставлю sleep(5000) у тесті і перевірю." (Погано: хардкод логіки під тести).
✅ Відповідь Сеньйора:
Ми замокаємо системний годинник прямо в браузері!
Сучасні фреймворки мають вбудовану машину часу. Наприклад, у Playwright ми використовуємо await page.clock.setFixedTime(new Date('2026-12-31T11:01:00')).
Браузер думатиме, що дедлайн уже минув, Angular-компонент миттєво перерахує час, і банер зникне. Нуль очікувань, 100% надійність.
Мокаєте час у тестах? 👇
🔥 — Постійно, це єдиний спосіб тестувати таймери!
👀 — Чесно? Навіть не знав(ла), що так можна.
🕵️♂️ Інженерний детектив: "Невидима перешкода" (або Z-Index пастка)
Привіт, екіпаж! Середа — час розслідувань.
Симптоми: Користувачі скаржаться, що кнопка "Купити" не натискається. Ви запускаєте Cypress/Playwright — тест клікає кнопку і світиться зеленим. Ви відкриваєте руками — кнопка реально не реагує на мишку!
🔍 Що сталося:
✅ Вирок:
Привіт, екіпаж! Середа — час розслідувань.
Симптоми: Користувачі скаржаться, що кнопка "Купити" не натискається. Ви запускаєте Cypress/Playwright — тест клікає кнопку і світиться зеленим. Ви відкриваєте руками — кнопка реально не реагує на мишку!
🔍 Що сталося:
Playwright за замовчуванням має механізм "Actionability", але іноді він хибить. Фронтендер випадково розтягнув прозорий <div> (наприклад, невидимий тултип) поверх кнопки.
Візуально кнопка є. В DOM вона є. Але фізично курсор клікає по прозорому <div>, який лежить вище по z-index!
✅ Вирок:
У Playwright іноді використовують force: true для кліку. Це змушує фреймворк пробити DOM і клікнути елемент, ігноруючи перешкоди. Ніколи цього не робіть.
Якщо тест падає через "element intercepts pointer events" — це не нестабільний тест, це реальний баг UI, який блокує ваших живих користувачів.
📝 Ультимативна Шпаргалка: HTTP Status Codes (Версія для QA)
Ми всі знаємо 200 (OK) та 404 (Not Found). Але на співбесідах (і під час тестування NestJS бекендів) постійно плутають помилки клієнта. Зберігайте:
Перевіряйте не просто факт помилки, а її ТОЧНИЙ код у ваших API тестах!
Ми всі знаємо 200 (OK) та 404 (Not Found). Але на співбесідах (і під час тестування NestJS бекендів) постійно плутають помилки клієнта. Зберігайте:
401 Unauthorized: "Я не знаю, хто ти". (Ти забув передати токен авторизації, або він прострочений).
403 Forbidden: "Я знаю, хто ти, але тобі туди не можна". (Ти залогінений як звичайний юзер, а намагаєшся видалити проєкт як Адмін).
400 Bad Request: "Ти відправив технічну діч". (Синтаксична помилка, не валідний JSON, бракує обов'язкового поля).
422 Unprocessable Entity: "Формат правильний, але бізнес-логіка проти". (Ти відправив ідеальний JSON з полем email, але цей email вже зайнятий у PostgreSQL).
Перевіряйте не просто факт помилки, а її ТОЧНИЙ код у ваших API тестах!
🔥 Прожарка інструментів: Selenium WebDriver (Легенда, якій час на пенсію)
Привіт, екіпаж! Сьогодні смажимо прабатька всієї UI-автоматизації.
🟢 Як нам це продають: "Це стандарт! Він підтримує навіть Internet Explorer 8 і будь-які мови програмування!"
🥩 Прожарка:
Вердикт: Якщо ви стартуєте новий проєкт у 2026 році — Selenium є найгіршим вибором з точки зору швидкості розробки (DX). Залиште його для підтримки легасі.
Привіт, екіпаж! Сьогодні смажимо прабатька всієї UI-автоматизації.
🟢 Як нам це продають: "Це стандарт! Він підтримує навіть Internet Explorer 8 і будь-які мови програмування!"
🥩 Прожарка:
1️⃣ Пекло версій: Скільки життів згоріло через помилку SessionNotCreatedException: This version of ChromeDriver only supports Chrome version X. Ви оновили браузер — тести впали. Потрібно постійно менеджити драйвери (так, є WebDriverManager, але це ще один костиль).
2️⃣ Архітектура: Selenium працює через HTTP-запити до драйвера. Він фізично не може зазирнути всередину браузера, перехопити мережу чи працювати з LocalStorage так само елегантно і швидко, як Playwright чи Cypress (які спілкуються через CDP протокол).
Вердикт: Якщо ви стартуєте новий проєкт у 2026 році — Selenium є найгіршим вибором з точки зору швидкості розробки (DX). Залиште його для підтримки легасі.