💩 Код з душком: "Магічні числа" в API-тестах (або Чому ваші пайплайни падають по понеділках)
Привіт, екіпаж! П'ятниця — традиційний час вивітрювати "смердючий" код з репозиторіїв. Ми вже розбиралися з крихкими UI-локаторами та залежними тестами. Сьогодні ліземо під капот і препаруємо найпопулярніший гріх при тестуванні бекенду. Говоримо про хардкод і статичні дані. ☕️
Знайдіть проблему в цьому тесті:
Чому цей код тхне:
✅ Як це виглядає після код-рев'ю Senior-інженера:
Тест ніколи не повинен розраховувати на те, що в базі "вже щось є". Він має сам забезпечити себе даними, отримати динамічний ідентифікатор (UUID або автоінкремент) і працювати виключно з ним.
Чому це шедевр:
Цей тест абсолютно автономний і куленепробивний. Ви можете запускати його в 100 потоків паралельно, на порожній чи заповненій базі — він ніколи не перетнеться з іншими тестами, бо щоразу генерує унікальні сутності і посилається тільки на них.
Золоте правило: Якщо у ваших API-тестах є захардкоджені ID (наприклад 100500, user-123), рано чи пізно цей тест зрадить вас у пайплайні. Генеруйте дані "на льоту".
А як у вас справи з тестовими даними? 👇
🔥 — Тільки динамічна генерація, мій фреймворк сам створює все необхідне!
👀 — Грішу хардкодом... У нас є спеціальний тестовий юзер, якого ми всі боїмося видалити.
🤯 — А що робити, якщо бекенд не має ендпоінту для створення юзера?!
Привіт, екіпаж! П'ятниця — традиційний час вивітрювати "смердючий" код з репозиторіїв. Ми вже розбиралися з крихкими UI-локаторами та залежними тестами. Сьогодні ліземо під капот і препаруємо найпопулярніший гріх при тестуванні бекенду. Говоримо про хардкод і статичні дані. ☕️
Знайдіть проблему в цьому тесті:
// ❌ Як пишуть "на швидку руку" (Антипатерн: Прив'язка до статичної БД)
test('Повинен успішно отримати профіль користувача', async ({ request }) => {
// Звертаємось до конкретного ID, який хтось колись створив руками
const response = await request.get('/api/users/42');
const user = await response.json();
expect(response.status()).toBe(200);
expect(user.email).toBe('test-qa@example.com');
});
Чому цей код тхне:
Це класична бомба уповільненої дії. На вашій локальній машині цей тест буде зеленим. Але коли ви тестуєте сучасний бекенд (наприклад, написаний на NestJS у зв'язці з PostgreSQL), сутності створюються динамічно.
Щойно цей тест потрапить у CI/CD, де база даних піднімається з нуля в Docker-контейнері, або якщо інший розробник випадково видалить юзера з ID 42 — тест впаде з помилкою 404 Not Found. Ви будете витрачати години на дебаг "зламаного" бекенду, хоча насправді бекенд працює ідеально, просто тест зав'язаний на ілюзорні дані.
✅ Як це виглядає після код-рев'ю Senior-інженера:
Тест ніколи не повинен розраховувати на те, що в базі "вже щось є". Він має сам забезпечити себе даними, отримати динамічний ідентифікатор (UUID або автоінкремент) і працювати виключно з ним.
// 🚀 Ідеально чистий код (Динамічна генерація стану)
test('Повинен успішно отримати профіль користувача', async ({ request }) => {
// 1. Arrange: Динамічно створюємо юзера з унікальним email
const uniqueEmail = `qa-test-${Date.now()}@example.com`;
const createRes = await request.post('/api/users', {
data: { email: uniqueEmail, role: 'candidate' }
});
const newUser = await createRes.json();
// 2. Act: Використовуємо динамічний ID, який щойно згенерувала БД
const response = await request.get(`/api/users/${newUser.id}`);
const fetchedUser = await response.json();
// 3. Assert: Перевіряємо результат
expect(response.status()).toBe(200);
expect(fetchedUser.email).toBe(uniqueEmail);
});
Чому це шедевр:
Цей тест абсолютно автономний і куленепробивний. Ви можете запускати його в 100 потоків паралельно, на порожній чи заповненій базі — він ніколи не перетнеться з іншими тестами, бо щоразу генерує унікальні сутності і посилається тільки на них.
Золоте правило: Якщо у ваших API-тестах є захардкоджені ID (наприклад 100500, user-123), рано чи пізно цей тест зрадить вас у пайплайні. Генеруйте дані "на льоту".
А як у вас справи з тестовими даними? 👇
🔥 — Тільки динамічна генерація, мій фреймворк сам створює все необхідне!
👀 — Грішу хардкодом... У нас є спеціальний тестовий юзер, якого ми всі боїмося видалити.
🤯 — А що робити, якщо бекенд не має ендпоінту для створення юзера?!
❤1👍1
QA Co-pilot
💩 Код з душком: "Магічні числа" в API-тестах (або Чому ваші пайплайни падають по понеділках) Привіт, екіпаж! П'ятниця — традиційний час вивітрювати "смердючий" код з репозиторіїв. Ми вже розбиралися з крихкими UI-локаторами та залежними тестами. Сьогодні…
📡 Tech Radar: Головне за тиждень (01.06 – 07.06.2026) від QA Co-pilot
Привіт, екіпаж! Перший тиждень літа позаду, і він приніс кілька потужних релізів. Цього разу фокусуємось на інструментах, які знімають з QA найбільший головний біль: генерацію тестових даних, адаптивну верстку та доступність. Зібрали для вас найважливіше без води. ☕️
📱 BrowserStack: "Layout-Intelligence" для мобільного та кросбраузерного тестування
🗄 Testcontainers Cloud "Time Machine": Миттєві клони баз даних для CI/CD
♿️ Axe-core 5.0: Динамічне тестування доступності (A11y)
Який апдейт найбільше потрібен вашому проєкту прямо зараз? 👇
🔥 — Testcontainers! Мої паралельні тести постійно ламають базу одне одному.
👀 — BrowserStack Layout: нарешті не треба шукати зламану верстку на старих айфонах.
🤯 — Axe-core: у нас якраз замовники з Європи почали вимагати доступність!
Привіт, екіпаж! Перший тиждень літа позаду, і він приніс кілька потужних релізів. Цього разу фокусуємось на інструментах, які знімають з QA найбільший головний біль: генерацію тестових даних, адаптивну верстку та доступність. Зібрали для вас найважливіше без води. ☕️
📱 BrowserStack: "Layout-Intelligence" для мобільного та кросбраузерного тестування
Скільки разів ви ловили баг, коли німецький чи український текст вилазив за межі кнопки на маленькому екрані iPhone SE? Раніше для цього треба було писати візуальні тести на 10 різних розмірів екранів.
BrowserStack викотив круту фічу на базі машинного зору: тепер їхня хмара автоматично сканує ваш додаток на 50 різних пристроях у фоні і сама шукає перекриття елементів (overlaps), обрізаний текст чи зламані флексбокси.
Як це вплине на нас: Ви більше не пишете тести на адаптивність. Платформа сама "підсвітить" пристрої, де UI розсипався, навіть якщо логіка кнопки працює ідеально.
🗄 Testcontainers Cloud "Time Machine": Миттєві клони баз даних для CI/CD
Усі знають цей біль: ви запускаєте 20 API-тестів паралельно, вони починають одночасно створювати/видаляти користувачів в одній базі даних, конфліктують і падають.
Testcontainers випустили фічу "State Snapshots". Ви піднімаєте базу (наприклад, PostgreSQL), заливаєте туди базові дані і робите "зліпок". Тепер кожен з ваших 20 паралельних тестів (воркерів) за 1 мілісекунду отримує свою власну, ідеально ізольовану копію бази даних.
Як це вплине на нас: Прощавайте, "моргаючі" API-тести через конфлікт даних. Ідеальна ізоляція (F.I.R.S.T. принцип) тепер доступна "з коробки" без хитрих скриптів очищення.
♿️ Axe-core 5.0: Динамічне тестування доступності (A11y)
Після того, як Європейський акт про доступність (EAA) набув повної сили, тестування Accessibility стало обов'язковим для всіх проєктів, орієнтованих на ЄС. Axe-core (найпопулярніший рушій перевірки a11y) отримав мажорне оновлення.
Раніше він міг перевірити лише статичний HTML (контрастність, наявність alt-тексту). Версія 5.0 вміє динамічно перехоплювати фокус клавіатури (Tab навігацію) прямо під час прогону ваших Playwright-тестів і автоматично ловити "focus traps" (коли юзер застряг у модалці).
Як це вплине на нас: Замість мануального "проклікування" форм табом, ви просто додаєте один плагін у конфіг, і ваші E2E-тести автоматично валять білд, якщо модалку не можна закрити кнопкою Esc або клавіатурою.
Який апдейт найбільше потрібен вашому проєкту прямо зараз? 👇
🔥 — Testcontainers! Мої паралельні тести постійно ламають базу одне одному.
👀 — BrowserStack Layout: нарешті не треба шукати зламану верстку на старих айфонах.
🤯 — Axe-core: у нас якраз замовники з Європи почали вимагати доступність!
🧨 Руйнівники IT-міфів: "За якість продукту відповідає QA-відділ"
Привіт, екіпаж! Сьогодні дістаємо з шафи найболючіший міф, через який тестувальники вигорають, а розробники розслабляються. Цю фразу хоча б раз у житті чув кожен з нас. ☕️
❌ Міф: "Якщо на продакшені знайшли критичний баг — винен виключно QA-інженер. Як ви могли це пропустити?! Ви ж відповідаєте за якість!"
💥 Реальність (Бум!):
Чому цей міф шкідливий:
✅ Як має бути (Здоровий підхід):
А як у вашій компанії ставляться до пропущених багів на проді? 👇
🔥 — Шукаємо причину в процесах, робимо блеймлес-постмортем.
👀 — Прилітає по шапці виключно тестувальнику...
🤯 — А що, код мають перевіряти самі розробники перед відправкою в QA?!
Привіт, екіпаж! Сьогодні дістаємо з шафи найболючіший міф, через який тестувальники вигорають, а розробники розслабляються. Цю фразу хоча б раз у житті чув кожен з нас. ☕️
❌ Міф: "Якщо на продакшені знайшли критичний баг — винен виключно QA-інженер. Як ви могли це пропустити?! Ви ж відповідаєте за якість!"
💥 Реальність (Бум!):
QA-відділ взагалі не здатний "зробити" продукт якісним. Це фізично неможливо.
Найкраща аналогія: QA — це медичний термометр. Термометр може дуже точно показати, що у пацієнта (продукту) температура 39.5°C. Але термометр не лікує хворобу. Лікує імунітет (архітектура) та лікарі (розробники). Якщо розробник написав кривий код без архітектури і покриття Unit-тестами, жоден у світі мануальник чи автоматизатор не зробить цей продукт стабільним.
Чому цей міф шкідливий:
1️⃣ Синдром "Перекидання через паркан": Розробник пише код абияк, не перевіряє навіть Happy Path і кидає тікет у статус "Ready for QA", думаючи: "Тестувальник знайде всі баги, це його робота". У підсумку QA витрачає години на дебаг елементарних помилок NullPointerException.
2️⃣ Ілюзія "Вортаря": Бізнес вважає, що тестування — це остання фаза перед релізом. Але якщо баг закладено на етапі бізнес-вимог (наприклад, аналітик забув про податки у формулі оплати), фіксити його після розробки буде в 10 разів дорожче і довше.
✅ Як має бути (Здоровий підхід):
1️⃣ Якість — це відповідальність усієї команди. Розробники відповідають за Unit-тести та код-рев'ю. Продакти — за логічні вимоги без "дірок".
2️⃣ Shift-Left Testing (Зсув вліво): Справжня робота QA починається не тоді, коли код написаний, а на етапі обговорення ідеї. Ми тестуємо вимоги, тестуємо дизайн у Figma (а що буде, якщо юзер з довгим ім'ям зайде без інтернету?), щоб запобігти появі коду з багами.
3️⃣ Головна задача QA: Не "знайти всі баги", а надати бізнесу об'єктивну інформацію про поточний стан продукту та ризики для прийняття рішення про реліз.
А як у вашій компанії ставляться до пропущених багів на проді? 👇
🔥 — Шукаємо причину в процесах, робимо блеймлес-постмортем.
👀 — Прилітає по шапці виключно тестувальнику...
🤯 — А що, код мають перевіряти самі розробники перед відправкою в QA?!
🕵️♂️ Інженерний детектив: Справа про "Подвійне списання" (або Магія Ідемпотентності)
Привіт, екіпаж! Середа — час нашої рубрики. Як ми й домовлялися: ніяких таймерів, дат чи локаторів. Сьогодні ми розслідуємо суто архітектурний злочин на рівні платіжних систем, який може коштувати вашій компанії мільйони і купу судових позовів. ☕️
Сцена злочину (Симптоми):
❌ Хибний слід:
🔍 Що сталося насправді (Відсутність Ідемпотентності):
✅ Вирок та Рішення (Підхід Архітектора):
Блокувати кнопку на UI — це правило хорошого тону для UX, але не захист бізнес-логіки.
QA повинен вимагати від бекенд-розробників реалізації Idempotency Key (Ключа ідемпотентності).
Як це працює:
Золоте правило: Ніколи не тестуйте фінансові або критичні операції (POST, PUT) без перевірки на дублювання запитів. Завжди перехоплюйте запит у DevTools (або Postman) і відправляйте його двічі-тричі. Якщо створилося кілька сутностей — це критичний баг бекенду.
Привіт, екіпаж! Середа — час нашої рубрики. Як ми й домовлялися: ніяких таймерів, дат чи локаторів. Сьогодні ми розслідуємо суто архітектурний злочин на рівні платіжних систем, який може коштувати вашій компанії мільйони і купу судових позовів. ☕️
Сцена злочину (Симптоми):
Ви тестуєте оформлення замовлення. Інтернет трохи "лагає" (наприклад, ви увімкнули Throttling "Slow 3G" у DevTools). Ви натискаєте кнопку "Оплатити". З'являється лоадер, але він крутиться нескінченно довго.
Ви, як типовий роздратований користувач, починаєте маніакально клікати на цю кнопку ще 3 рази підряд.
Врешті-решт вас перекидає на сторінку "Успіх". Ви заходите у свій банківський додаток і бачите, що гроші за одне замовлення списалися 4 рази!
❌ Хибний слід:
Джун-тестувальник біжить до фронтендера. Вони вирішують проблему "на поверхні": фронтендер додає атрибут disabled на кнопку "Оплатити" одразу після першого кліку.
Тепер кнопку фізично неможливо натиснути двічі. Тест проходить, всі плескають у долоні.
Але через тиждень з продакшену знову прилітає скарга на подвійне списання! Чому? Бо юзер натиснув кнопку один раз, у нього блимнув Wi-Fi, він злякався, натиснув F5 (оновив сторінку) і знову відправив форму. Блокування кнопки на UI його не зупинило.
🔍 Що сталося насправді (Відсутність Ідемпотентності):
Справжній детектив знає, що UI — це лише ілюзія безпеки. Серверна частина була написана так, що вона сліпо виконує кожен POST /api/pay запит, який до неї долітає.
У розподілених системах (мікросервісах) мережа ніколи не буває надійною. Запити можуть дублюватися через ретраї на рівні балансувальників (Nginx) або через дії самого юзера. Бекенд не мав жодного механізму, щоб зрозуміти: "Гей, я вже обробляв цю оплату 2 секунди тому!".
✅ Вирок та Рішення (Підхід Архітектора):
Блокувати кнопку на UI — це правило хорошого тону для UX, але не захист бізнес-логіки.
QA повинен вимагати від бекенд-розробників реалізації Idempotency Key (Ключа ідемпотентності).
Як це працює:
1️⃣ Коли користувач тільки заходить на сторінку чекауту, фронтенд або бекенд генерує унікальний UUID для цієї конкретної спроби оплати.
2️⃣ При натисканні "Оплатити" фронтенд передає цей UUID у заголовку (наприклад, Idempotency-Key: 123e4567...).
3️⃣ Якщо юзер клікне 4 рази або оновить сторінку, всі ці запити полетять з однаковим ключем.
4️⃣ База даних бачить цей ключ, успішно списує гроші за ПЕРШИМ запитом. Наступні три запити з цим же ключем вона не виконує (гроші не списує), а просто повертає закешовану відповідь 200 OK.
Золоте правило: Ніколи не тестуйте фінансові або критичні операції (POST, PUT) без перевірки на дублювання запитів. Завжди перехоплюйте запит у DevTools (або Postman) і відправляйте його двічі-тричі. Якщо створилося кілька сутностей — це критичний баг бекенду.
📝 Ультимативна Шпаргалка: HTTP-заголовки, які врятують ваш дебаг
Коли джуніори починають тестувати API (у Postman, Swagger або DevTools), вони зазвичай дивляться тільки на Body (тіло відповіді) та Status Code. Але справжні архітектурні баги ховаються у метаданих — HTTP-заголовках (Headers).
Ось ультимативний набір заголовків, які має розуміти кожен QA. ☕️
1️⃣ Пастка форматів: Content-Type vs Accept
2️⃣ Детектив мікросервісів: X-Request-ID (або X-Correlation-ID)
3️⃣ Ілюзія старих даних: Cache-Control та ETag
4️⃣ Біль крос-доменних запитів: Access-Control-Allow-Origin (CORS)
5️⃣ Управління сесіями: Authorization vs Cookie
Зберігайте цю шпаргалку. Наступного разу замість абстрактного "У мене бекенд повертає помилку", ви зможете чітко написати: "Баг: Мікросервіс платежів губить X-Correlation-ID під час редиректу". Це рівень справжнього Архітектора.
Коли джуніори починають тестувати API (у Postman, Swagger або DevTools), вони зазвичай дивляться тільки на Body (тіло відповіді) та Status Code. Але справжні архітектурні баги ховаються у метаданих — HTTP-заголовках (Headers).
Ось ультимативний набір заголовків, які має розуміти кожен QA. ☕️
1️⃣ Пастка форматів: Content-Type vs Accept
Дуже часто QA ловить 415 Unsupported Media Type або 406 Not Acceptable і не розуміє, чому валідний JSON не проходить.
🔹Content-Type: Каже серверу, у якому форматі ви ВІДПРАВЛЯЄТЕ дані (наприклад, application/json або multipart/form-data для файлів).
🔹Accept: Каже серверу, у якому форматі ви хочете ОТРИМАТИ відповідь. Якщо ви просите application/xml, а сервер вміє віддавати тільки JSON — буде помилка.
2️⃣ Детектив мікросервісів: X-Request-ID (або X-Correlation-ID)
Що це: Унікальний паспорт вашого запиту.
Як тестувати: Уявіть, що ви натиснули кнопку "Купити". Цей запит проходить через 5 різних мікросервісів (Авторизація, Склад, Платежі, Нотифікації). Десь на третьому кроці стається помилка 500. Як бекендеру знайти саме ВАШ запит у гігабайтах логів?
Правильно спроєктований фронтенд або API-шлюз генерує X-Request-ID при першому кліці і прокидає його через усі сервіси. Якщо ви бачите цей заголовок у відповіді — прикріпіть його до баг-репорту. Розробник скаже вам величезне "Дякую".
3️⃣ Ілюзія старих даних: Cache-Control та ETag
Що це: Відповідають на запитання "Чому я видалив товар, а він досі на вітрині?!"
Як тестувати: Якщо бекенд віддає заголовок Cache-Control: max-age=3600, браузер запам'ятає цю відповідь на годину і взагалі не буде робити нові запити на сервер, хоч би скільки ви тиснули F5. Якщо ви перевіряєте динамічні дані, шукайте Cache-Control: no-cache.
А ETag — це хеш-сума (версія) ресурсу. Якщо вона не змінилася, сервер поверне статус 304 Not Modified і заощадить трафік.
4️⃣ Біль крос-доменних запитів: Access-Control-Allow-Origin (CORS)
Що це: Головний біль усіх фронтендерів.
Як тестувати: Ви тестуєте API через Postman — усе працює ідеально. Фронтендер підключає це API до UI — отримує в консолі червону помилку "CORS policy blocked".
Чому? Тому що браузери забороняють сайту frontend.com робити запити на api.backend.com з міркувань безпеки, ПОКИ бекенд явно не віддасть заголовок Access-Control-Allow-Origin: https://frontend.com. Якщо API публічне, там має бути *.
5️⃣ Управління сесіями: Authorization vs Cookie
Що це: Два шляхи передачі вашого "ключа від дверей".
🔹Authorization: Bearer <token>: Сучасний стандарт (JWT-токени). Фронтенд сам бере токен і чіпляє його до кожного запиту. Ідеально для мікросервісів.
🔹Cookie: Класичний підхід. Браузер сам автоматично чіпляє їх до запитів. Але тут завжди перевіряйте наявність прапорців HttpOnly (щоб хакери не вкрали куку через XSS) та Secure (передача тільки по HTTPS).
Зберігайте цю шпаргалку. Наступного разу замість абстрактного "У мене бекенд повертає помилку", ви зможете чітко написати: "Баг: Мікросервіс платежів губить X-Correlation-ID під час редиректу". Це рівень справжнього Архітектора.
👍1🔥1
🔥 Прожарка інструментів: Postman (або Еволюція в монстра)
Привіт, екіпаж! Четвер — розпалюємо гриль. Сьогодні на решітці інструмент, який встановлено на комп'ютері кожного QA, але який останнім часом викликає більше роздратування, ніж користі. Говоримо про Postman. ☕️
🟢 Як нам це продавали колись:
"Ідеально легкий клієнт для тестування API. Вставив URL, додав JSON, натиснув Send — миттєво отримав результат. Працює швидко і не ставить зайвих питань."
🥩 Прожарка (Сувора реальність 2026 року):
⚖️ Вердикт QA Co-pilot:
На чому зараз "сидить" ваша команда? 👇
🔥 — Давно переїхав на Bruno / Insomnia, літає як ракета!
👀 — Терплю Postman, бо ліньки переносити тисячі старих запитів...
🤯 — Стоп, я можу тестувати API прямо у своєму редакторі коду?!
Привіт, екіпаж! Четвер — розпалюємо гриль. Сьогодні на решітці інструмент, який встановлено на комп'ютері кожного QA, але який останнім часом викликає більше роздратування, ніж користі. Говоримо про Postman. ☕️
🟢 Як нам це продавали колись:
"Ідеально легкий клієнт для тестування API. Вставив URL, додав JSON, натиснув Send — миттєво отримав результат. Працює швидко і не ставить зайвих питань."
🥩 Прожарка (Сувора реальність 2026 року):
1️⃣ Хмарне рабство: Вони фактично вбили офлайн-режим (Scratch Pad). Тепер, щоб просто відправити GET-запит на свій локалхост, ви маєте залогінитись, відправити свої колекції на їхні сервери і постійно відбиватися від банерів "Купіть Enterprise".
2️⃣ Апетит до оперативки: Простий HTTP-клієнт тепер "жере" стільки ж пам'яті, скільки IDE. Він перетворився на важкий комбайн із вбудованими ШІ-агентами, моніторингом, дашбордами і ще десятком фіч, які для щоденного дебагу взагалі не потрібні.
3️⃣ Версіонування: Спроба безкоштовно шерити колекції в команді через їхній Workspace — це біль. Замість зручного зберігання тестів у вашому Git-репозиторії разом із кодом (як це роблять нормальні фреймворки), ви замкнені в їхній екосистемі.
⚖️ Вердикт QA Co-pilot:
Postman став неповоротким корпоративним гігантом, який забув про своє коріння. Для швидкого мануального тестування API та написання легких скриптів інженери зараз масово переходять на опенсорсні Bruno, Hoppscotch або ж звичайні плагіни типу REST Client прямо у VS Code. Вони літають, нічого не просять і зберігають колекції у звичайних текстових файлах.
На чому зараз "сидить" ваша команда? 👇
🔥 — Давно переїхав на Bruno / Insomnia, літає як ракета!
👀 — Терплю Postman, бо ліньки переносити тисячі старих запитів...
🤯 — Стоп, я можу тестувати API прямо у своєму редакторі коду?!
💩 Код з душком: Assertions всередині Page Object (або Чому ваші хелпери не гнучкі)
Привіт, екіпаж! П'ятниця — час вивітрювати антипатерни. Сьогодні розбираємо найчастішу архітектурну помилку при побудові Page Object Model (POM), яка змушує вас дублювати код. ☕️
Знайдіть проблему в цьому класі:
Чому цей код тхне:
✅ Як це виглядає після код-рев'ю Senior-інженера:
Page Object має бути "тупим". Він відповідає ТІЛЬКИ за навігацію та дії. Усі перевірки (expect) мають жити виключно у самому тесті.
Золоте правило: Дії (click, fill) — живуть у класах Page Object. Перевірки (expect) — живуть у тестах. Ніколи не змішуйте їх.
Де у вас лежать перевірки? 👇
🔥 — Усі expect тільки в тестах, мої класи тупі і гнучкі!
👀 — Грішу... У мене половина ассертів захована десь у хелперах.
🤯 — Чекайте, а як тоді перевірити, що кнопка натиснулась?!
Привіт, екіпаж! П'ятниця — час вивітрювати антипатерни. Сьогодні розбираємо найчастішу архітектурну помилку при побудові Page Object Model (POM), яка змушує вас дублювати код. ☕️
Знайдіть проблему в цьому класі:
// ❌ Як пишуть на початку (Антипатерн "Розумна сторінка")
class LoginPage {
async login(email: string, pass: string) {
await this.page.fill('#email', email);
await this.page.fill('#pass', pass);
await this.page.click('#submit');
// ❌ Assert зашитий прямо в метод сторінки!
await expect(this.page.locator('.dashboard')).toBeVisible();
}
}
Чому цей код тхне:
Ви щойно вбили можливість перевикористати цей метод. Що, якщо завтра вам треба буде написати тест на невалідний логін?
Ви викличете метод login('bad@email', '123'), і він впаде, бо почне шукати .dashboard, якого там бути не повинно. Вам доведеться створювати метод-клон loginForNegativeTest(). Це шлях до хаосу.
✅ Як це виглядає після код-рев'ю Senior-інженера:
Page Object має бути "тупим". Він відповідає ТІЛЬКИ за навігацію та дії. Усі перевірки (expect) мають жити виключно у самому тесті.
// 🚀 Ідеально чистий код (Розділення відповідальності)
class LoginPage {
async login(email: string, pass: string) {
await this.page.fill('#email', email);
await this.page.fill('#pass', pass);
await this.page.click('#submit');
}
}
// У файлі тесту:
test('Успішний логін', async ({ page }) => {
await loginPage.login('good@email.com', 'pass');
await expect(page.locator('.dashboard')).toBeVisible(); // Assert тут!
});
test('Помилка логіну', async ({ page }) => {
await loginPage.login('bad@email.com', '123');
await expect(page.locator('.error-msg')).toHaveText('Невірний пароль'); // Assert тут!
});
Золоте правило: Дії (click, fill) — живуть у класах Page Object. Перевірки (expect) — живуть у тестах. Ніколи не змішуйте їх.
Де у вас лежать перевірки? 👇
🔥 — Усі expect тільки в тестах, мої класи тупі і гнучкі!
👀 — Грішу... У мене половина ассертів захована десь у хелперах.
🤯 — Чекайте, а як тоді перевірити, що кнопка натиснулась?!
❤1
📡 Tech Radar: Головне за тиждень (08.06 – 14.06.2026) від QA Co-pilot
Привіт, екіпаж! Червень у розпалі. Зібрали для вас свіжу порцію інструментів, які роблять життя QA простішим, а пайплайни — надійнішими. Без води, лише база за 30 секунд. ☕️
🧠 Ollama для QA: Безпечна локальна генерація даних
🕵️ Proxyman 5.0: Автоматичний детектор "Schema Drift"
⚡️ Lighthouse 14: Жорсткий ліміт на INP (Interaction to Next Paint)
Яку фічу забираєте у свій арсенал? 👇
🔥 — Локальний ШІ через Ollama, безпека понад усе!
👀 — Proxyman: пішов ловити бекендерів на гарячому.
🤯 — Lighthouse: у нас весь фронт лагає при кліках, доведеться фіксити...
Привіт, екіпаж! Червень у розпалі. Зібрали для вас свіжу порцію інструментів, які роблять життя QA простішим, а пайплайни — надійнішими. Без води, лише база за 30 секунд. ☕️
🧠 Ollama для QA: Безпечна локальна генерація даних
Використовувати ChatGPT для генерації JSON-мок чи тестових сценаріїв зручно, але служба безпеки б'є по руках за злив комерційної таємниці?
Спільнота масово переходить на Ollama — інструмент для локального запуску легких ШІ-моделей (типу Llama 3).
Як це впливає на нас: Ви піднімаєте ШІ прямо на своєму робочому ноуті або в закритому контурі CI/CD. Модель генерує ідеальні фейкові дані та парсить складні логи без жодного звернення до інтернету. Ваші NDA в безпеці.
🕵️ Proxyman 5.0: Автоматичний детектор "Schema Drift"
Тестуєте мобілки чи фронтенд? Proxyman (головний конкурент Charles) викотив кілер-фічу. Тепер він вміє автоматично порівнювати реальні API-запити з вашою OpenAPI (Swagger) документацією "на льоту".
Як це впливає на нас: Якщо бекендер мовчки змінив тип поля id з number на string, Proxyman миттєво підсвітить цей запит червоним у вашому лозі, ще до того, як додаток впаде з незрозумілою помилкою.
⚡️ Lighthouse 14: Жорсткий ліміт на INP (Interaction to Next Paint)
Google офіційно зробив INP головною метрикою швидкодії замість старого FID. Якщо раніше ми тестували тільки "швидкість завантаження", то тепер фокус на "швидкості реакції".
Як це впливає на нас: Якщо користувач натиснув на чекбокс або відкрив дропдаун, і UI "задумався" більше ніж на 200 мілісекунд (навіть якщо сторінка вже давно завантажена) — Lighthouse завалить ваш Performance-тест. Час вимірювати мікро-лаги!
Яку фічу забираєте у свій арсенал? 👇
🔥 — Локальний ШІ через Ollama, безпека понад усе!
👀 — Proxyman: пішов ловити бекендерів на гарячому.
🤯 — Lighthouse: у нас весь фронт лагає при кліках, доведеться фіксити...
🧨 Руйнівники 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...
🤯 — А що, можна ставити запити "на паузу" під час тесту?!
❤1
🕵️♂️ Інженерний детектив: Справа про "Раптовий логаут" (або Гонка 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🤝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👍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 рядків... Допоможіть!
❤1
🔥 Прожарка інструментів: 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