QA Co-pilot
94 subscribers
298 photos
1 video
47 links
QA Co-pilot 🚀

Ваш другий пілот у світі тестування.

👨‍💻 Для кого: Для тестувальників-практиків, які хочуть рости.
🎯 Про що: Делегуємо рутину нейромережам, прискорюємо роботу та звільняємо час на головне.
Чого тут немає: Нудної теорії та води.
Download Telegram
🧨 Руйнівники IT-міфів: "Справжній E2E-тест має клікати по реальному Google Auth чи Stripe"

Привіт, екіпаж! Сьогодні розбираємо міф, через який автотести обростають "милицями", падають на 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)

Привіт, екіпаж! Середа — час нашого розслідування. Сьогодні шукаємо архітектурний баг, який змушує сервер сипати помилками, а клієнтів — писати гнівні листи в саппорт. ☕️

Сцена злочину (Симптоми):
Ви тестуєте звичайний 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)
Приклади: 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. ☕️

🟢 Як нам це продають:
"Абсолютний індустріальний стандарт навантажувального тестування! Безкоштовний, перевірений часом, підтримує всі протоколи світу. Відкрив інтерфейс, наклікав запити — і поклав сервер!"

🥩 Прожарка (Сувора реальність):
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."
Алгоритм:

1️⃣ Фронтенд пише тест, який генерує "Контракт" (JSON-файл). У ньому чітко написано: "Я відправляю запит X і очікую отримати поле userId у форматі числа".

2️⃣ Цей контракт автоматично завантажується в Pact Broker (спільний репозиторій).

3️⃣ Коли бекендер пише свій код, його CI/CD пайплайн автоматично стягує цей контракт і перевіряє свій API проти нього.

4️⃣ Якщо бекендер перейменував поле або змінив тип даних — його пайплайн падає ще до деплою.


Золоте правило: E2E-тести занадто повільні для перевірки інтеграції двох команд. Контрактні тести — це автоматизований "договір" між клієнтом і сервером, який не дозволяє їм зламати один одного.

А як ви тестуєте розірвані команди? 👇
🔥 — Pact — наш бро! Ловимо невідповідності ще в пайплайнах.
👀 — Пишемо E2E з моками і молимось, щоб бекенд нічого не змінив.
🤯 — Стоп, тобто фронтенд може диктувати бекенду, які дані повертати?!
💩 Код з душком: Жорсткі паузи (або Злочин під назвою sleep())

Привіт, екіпаж! Вивітрюємо антипатерни. Сьогодні говоримо про найпопулярніший (і найгірший) спосіб "полагодити" нестабільний (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)
Ви пишете тести людською мовою через ключові слова 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 цього тижня

Привіт, екіпаж! Понеділок — час оновлювати тулбокс. Два інструменти, які зараз активно обговорюють у ком'юніті:

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 (Скріншоти)
Ви робите скріншот сторінки і порівнюєте його з еталоном.

Мінус: Тести стають надзвичайно крихкими (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% покриття коду тестами. Ми можемо спокійно релізити, багів немає!"

💥 Реальність:
Code Coverage показує лише те, які рядки коду виконувалися під час тестів. Але він НЕ гарантує, що ви перевірили результат!

Ви можете написати тест, який викликає функцію calculateSalary(), і не написати жодного expect(). Покриття буде 100%, а функція може повертати null.


Як має бути:
Справжній показник надійності — це Mutation Testing (наприклад, Stryker). Цей інструмент штучно вносить баги у ваш код (міняє + на -) і перевіряє, чи впадуть ваші тести. Якщо не впали — ваші тести сліпі, незалежно від відсотків покриття!


Ганяєтесь за 100% покриттям? 👇
🔥 — Ні, фокусуємось на критичних бізнес-сценаріях!
👀 — У нас KPI на 80% покриття, пишемо тести заради цифри...
🧠 Задача з Senior співбесіди: "Подорож у часі"

Продовжуємо вівторок задачею з лайв-кодингу.

Умови: На сайті є банер "Знижка згорить завтра об 11:00". Як автоматизувати перевірку того, що банер дійсно зникає, коли настає час Х?

Відповідь Джуна: "Я створю тестовий банер, який зникає через 5 секунд, поставлю sleep(5000) у тесті і перевірю." (Погано: хардкод логіки під тести).

Відповідь Сеньйора:
Ми замокаємо системний годинник прямо в браузері!

Сучасні фреймворки мають вбудовану машину часу. Наприклад, у Playwright ми використовуємо await page.clock.setFixedTime(new Date('2026-12-31T11:01:00')).

Браузер думатиме, що дедлайн уже минув, Angular-компонент миттєво перерахує час, і банер зникне. Нуль очікувань, 100% надійність.


Мокаєте час у тестах? 👇
🔥 — Постійно, це єдиний спосіб тестувати таймери!
👀 — Чесно? Навіть не знав(ла), що так можна.
🕵️‍♂️ Інженерний детектив: "Невидима перешкода" (або Z-Index пастка)

Привіт, екіпаж! Середа — час розслідувань.

Симптоми: Користувачі скаржаться, що кнопка "Купити" не натискається. Ви запускаєте 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 бекендів) постійно плутають помилки клієнта. Зберігайте:

401 Unauthorized: "Я не знаю, хто ти". (Ти забув передати токен авторизації, або він прострочений).

403 Forbidden: "Я знаю, хто ти, але тобі туди не можна". (Ти залогінений як звичайний юзер, а намагаєшся видалити проєкт як Адмін).

400 Bad Request: "Ти відправив технічну діч". (Синтаксична помилка, не валідний JSON, бракує обов'язкового поля).

422 Unprocessable Entity: "Формат правильний, але бізнес-логіка проти". (Ти відправив ідеальний JSON з полем email, але цей email вже зайнятий у PostgreSQL).


Перевіряйте не просто факт помилки, а її ТОЧНИЙ код у ваших API тестах!
🔥 Прожарка інструментів: Selenium WebDriver (Легенда, якій час на пенсію)

Привіт, екіпаж! Сьогодні смажимо прабатька всієї 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). Залиште його для підтримки легасі.
🩻 Рентген співбесіди: "Тестування Webhooks"

Продовжуємо четвер питанням для мідлів+.

Питання: "Ваш додаток інтегровано з платіжною системою. Коли оплата проходить, стороння система відправляє Webhook (асинхронний POST запит) на ваш бекенд. Як це протестувати?"

Відповідь Джуна: "Я оплачу на UI і буду робити sleep(10000) або постійно оновлювати сторінку, поки баланс не зміниться."

Відповідь Сеньйора:
Я взагалі не буду чіпати сторонню систему. Я використаю API-клієнт в автотестах і сам відправлю POST-запит на наш ендпоінт вебхука (наприклад, /api/webhooks/stripe), імітуючи payload від платіжки з правильними signature-заголовками.

Або, якщо треба протестувати реальний флоу, підніму локальний тунель через Ngrok у CI/CD, щоб стороння система могла "достукатися" до мого тестового оточення.
💩 Код з душком: Магічні числа (або "Чому саме 5?")

Привіт, екіпаж! П'ятниця — вивітрюємо антипатерни.

Знайдіть проблему:
test('Відображення списку статей', async ({ page }) => {
await expect(page.locator('.article-card')).toHaveCount(5);
});

Чому це тхне:
Що таке 5? Це хардкод. Якщо хтось додасть нову статтю в базу даних — тест впаде, хоча функціонал працює. Вам доведеться йти в код і міняти 5 на 6. Це перетворює підтримку тестів на рутину.

Як має бути:
Завжди спирайтеся на динамічні дані. Якщо ви мокаєте мережу — беріть довжину з моку. Якщо берете з бази — робіть запит:
// 🚀 Ідеально
const articlesMock = [{id: 1}, {id: 2}];
await page.route('**/api/articles', route => route.fulfill({ json: articlesMock }));
await expect(page.locator('.article-card')).toHaveCount(articlesMock.length);

Додали елемент у масив? Тест адаптується автоматично.
⚔️ Битва підходів: Page Object Model (POM) проти Custom Fixtures

Завершуємо тиждень класичною битвою архітектур у Playwright.

🥊 Підхід 1: Page Object Model (Класика)
Ви створюєте клас LoginPage з методами fillEmail() та clickSubmit().

Проблема: З часом класи стають гігантськими ("Божественний об'єкт"), вимагають постійного new LoginPage(page) і засмічують пам'ять.


🥊 Підхід 2: Playwright Fixtures (Сучасність)
Ви інкапсулюєте кроки прямо в інфраструктуру тесту. Тест просто просить те, що йому треба у параметрах:
test('buy item', async ({ loggedInUser, cartInfo }) => { ... })

Перевага: loggedInUser сам під капотом відкриє сторінку, залогіниться, перевірить сесію і віддасть готовий стан. Жодних new, максимальна ізоляція та перевикористання.


Вердикт: POM все ще крутий для опису селекторів (аби не дублювати locator), але для складних флоу переходьте на кастомні Fixtures — це робить ваші тести чистими як сльоза.