Зміст
- 1. Про заняття
- 2. Зелений тест — ще не перевірений тест · основи тестування
- Зелений тест — ще не перевірений тест
- Два зелені тести — одна сторінка
- Доплата 10 → 12 грн: A зелений, B червоний
- Тест, який жодного разу не падав, — нічого не довів
- «Пакунок»: 11 функцій — сценарії — джерело правди
- Тест-кейс і автотест — та сама анатомія
- Словник: вісім слів, якими говорить тест
- Піраміда тестування — внизу багато й швидко, вгорі мало й дорого
- Конус морозива: коли піраміду перевернули
- Де який тест — у проєкті QA1
- Рівень — де перевіряємо, тип — що, скринька — як проєктуємо
- Словник QA1 — усі терміни
- 3. Тест зі сценарію: агенти Playwright
- Сценарій словами → агент з живим браузером → тест
- Як агент отримує тест: три способи
- Одна команда — три агенти і MCP-сервер
- Planner → generator → healer — і ви між кроками
- Демо 1: план і тести S1–S4
- Що зробив generator — і що довелося виправити
- Trace: запис прогону крок за кроком
- Healer лагодить тест — а не продукт
- Тест упав: що змінилося?
- Демо 2: дві зміни — один healer
- Healer зупинився — а правило все одно запишіть
- 4. Чи ловить тест дефект · тест-дизайн
- Внести дефект навмисно — і подивитися, хто впаде
- Тест без діагностичної цінності: як він виглядає
- Як працює npm run defects:check
- Демо 3: межа 5 → 6 кг — хто спіймав?
- Добрий навмисний дефект — для L1
- Класи еквівалентності й межі — точки вибирають, а не вгадують
- Комбінації і стани — таблиця рішень і переходи станів
- Словник 2: дублери, flaky і хибні тривоги
- 5. Мутаційне тестування
- 6. Завдання QA1
- 7. Що далі
1. Про заняття
Цей розділ — про початок заняття QA1: що в ньому нового, що буде за дві години і що знадобиться, щоб повторити все самостійно. Розбір завдання С6 QA був лише на занятті — у слайдах і в конспекті його немає.
#Автотест з опису сценарію — і доказ, що він перевіряє
QA1 — перше заняття групи QA курсу «AI-агенти в роботі QA та BA». Воно відбулося в середу, 04.11.2026, і тривало дві години. Далі заняття груп ідуть раз на два тижні: QA2 — 18.11, QA3 — 02.12.
Агент пише автотест за хвилину. Отримати тест більше не проблема. Проблема в іншому: довести, що тест щось перевіряє. Зелений тест без суттєвих перевірок у звіті виглядає так само, як зелений тест, що ловить дефекти. Тому заняття складається з двох кроків:
- Отримати тест — агент з живим браузером пише тест Playwright за сценарієм.
- Перевірити тест — трьома способами змусити його впасти навмисно: внести дефект у код, дати healer змінену поведінку і запустити мутаційне тестування.
Строки й дати:
- Завдання QA1 — до вівторка, 17.11.2026, 23:59.
- Розбір QA1 — на початку QA2, у середу, 18.11.
Що нового в QA1:
- Новий проєкт — gitlab.com/koldovsky/ai-qa-ba-qa1: навчальний вебзастосунок «Пакунок» з тестами, сценаріями і командами для перевірки тестів. Форки С1–С6 не підходять, потрібен новий форк.
- Той самий стек, що в шаблоні фінального проєкту QA: TypeScript + Vite, Playwright 1.64, Vitest, Stryker 10. Що налаштуєте тут, знадобиться там.
- Потрібен агент, який запускає команди. Лише чату в браузері замало: тести треба запускати. Демо — Claude Code з моделлю Sonnet; підійде будь-який агент з терміналом — Codex, GitHub Copilot, Cursor, OpenCode.
- Варіант той самий, що в С1–С6, але тепер це функція в меню застосунку.
#План: отримати тест — і перевірити тест
Після розбору С6 — чотири тематичні блоки, у трьох із них живі демо, і два короткі теоретичні: основи тестування (термінологія, піраміда, де який тест) і тест-дизайн (класи, межі, таблиця рішень, стани). Усі демо — на одній демо-функції «00 · Вартість доставки» і йдуть ланцюжком: агенти Playwright пишуть тести за сценарієм; healer лагодить тести після зміни; навмисний дефект перевіряє, чи тести його ловлять; Stryker рахує, скільки дрібних дефектів ловлять юніт-тести.
| Час | Що відбувається | Розділ конспекту |
|---|---|---|
| 0:00–0:15 | Розбір С6 QA — зі звіту перевірки | 1 · слайд 278 |
| 0:15–0:23 | Зелений ≠ перевірений: два тести, одна зміна; застосунок і сценарії | 2 · слайди 279–283 |
| 0:23–0:34 | Основи тестування: тест-кейс → автотест, словник, піраміда, де який тест, рівні і типи | 2 · слайди 284–289 |
| 0:34–0:58 | Тест зі сценарію: MCP, planner → generator, healer; демо 1 і 2 | 3 · слайди 290–300 |
| 0:58–1:14 | Чи ловить тест дефект: порожні тести, defect seeding; демо 3 | 4 · слайди 301–305 |
| 1:14–1:20 | Тест-дизайн: класи еквівалентності й межі, таблиця рішень, стани; словник 2 | 4 · слайди 306–308 |
| 1:20–1:40 | Мутаційне тестування: юніт і компонент, Stryker; демо 4 | 5 · слайди 309–315 |
| 1:40–1:52 | Завдання QA1: варіант, L1–L3, здача | 6 · слайди 316–322 |
| 1:52–2:00 | Що далі, головне, ресурси, питання | 7 · слайди 323–326 |
Записані прогони всіх чотирьох демо — на сторінці «Демо»: ті самі промпти, справжні відповіді агента, цифри токенів і вартості.
#Розбір С6 QA — у звіті перевірки
Розбір завдання С6 QA тренер показував на занятті зі звіту перевірки в режимі показу: що вийшло добре, типові помилки змісту і здачі, найкращі роботи, бали за рівнями. На екрані був лише розбір — без імен, номерів MR і приватних нотаток. У слайдах і в конспекті розбору немає.
Відгук до вашої роботи — коментар у вашому MR С6. Не згодні з оцінкою чи коментарем — відповідайте там само.
Місток до QA1: у С6 ви перевіряли твердження агента про код — «файл і функція». Сьогодні агент пише тести, і перевірка та сама: тест каже «працює» — а що саме він перевірив? Реєстр міжсистемних сценаріїв і контракт стику з С6 знадобляться на QA2, де йдеться про API-тести.
2. Зелений тест — ще не перевірений тест · основи тестування
Цей розділ — про те, чому зелений тест ще нічого не доводить. Два тести на одну функцію, обидва зелені, і одна зміна в коді, яка показує різницю між ними. Наприкінці — застосунок «Пакунок» і сценарії, на яких побудовані демо і завдання. Друга половина розділу — основи тестування для тих, хто досі тестував вручну: як тест-кейс стає автотестом, слова, якими говорять про тести, піраміда тестування, де який тест у проєкті QA1 і чим рівень тестування відрізняється від типу. Наприкінці — словник усіх термінів заняття.
#Зелений тест — ще не перевірений тест
Агент пише тест, тест проходить. Але «проходить» може означати дві різні речі: продукт поводиться правильно — або тест не перевіряє нічого суттєвого. У звіті прогону ці два випадки виглядають однаково: зелена галочка.
#Два зелені тести — одна сторінка
Два тести на ту саму сторінку демо-функції «00 · Вартість доставки». Обидва написав агент, обидва зелені.
Тест A · «розрахунок працює». З тих, що агент пише, коли просиш «тест на розрахунок»: заповнити вагу, натиснути кнопку і побачити результат.
test('розрахунок працює', async ({ page }) => {
await page.getByLabel('Вага, кг').fill('7');
await page.getByRole('button', { name: 'Розрахувати' }).click();
await expect(page.getByTestId('result')).toBeVisible();
});
Він перевіряє, що блок результату є на сторінці. Але блок result є на сторінці завжди: у нього вставляється і картка з сумою, і текст помилки.
Тест B · сценарій S2 «межа 5 кг». Узятий зі сценаріїв функції: 5,2 кг — це вже понад 5 кг, отже 70 + 10 = 80 грн.
test('S2 · 5,2 кг по області — 80 грн', async ({ page }) => {
await page.getByLabel('Вага, кг').fill('5,2');
await page.getByLabel('Маршрут').selectOption('область');
await page.getByRole('button', { name: 'Розрахувати' }).click();
await expect(page.getByTestId('result-lines'))
.toContainText('До сплати: 80 грн');
});
Він перевіряє конкретне число зі сценарію — і саме над межею, де починається доплата.
#Доплата 10 → 12 грн: A зелений, B червоний
Ми це запустили: змінили доплату з 10 на 12 грн у функції 00 і прогнали тести. Звіт Playwright (npm run e2e:report) показав 1 failed · 2 passed.
- Упав лише тест зі сценарію. Playwright показує, що очікував і що отримав: «До сплати: 80 грн» проти 82 грн, з підсвіткою різниці.
- Тест A пройшов. Блок результату видно — отже, «працює». Зміну ціни він не помітив би ніколи. У звіті він зелений поруч із червоним, і без цього прогону ви б не відрізнили його від корисного.
- Що далі. 82 грн — дефект чи нова вимога? Може, ціну справді підняли. Вирішує не тест і не агент. Але зелений тест A не дав би навіть приводу спитати. До цього питання ми повернемося в розділі 3, коли дійдемо до healer.
У звіті є кнопка View Trace — запис прогону крок за кроком. Про неї — теж у розділі 3.
#Тест, який жодного разу не падав, — нічого не довів
Головна думка заняття. Тест — це твердження «якщо продукт зламається тут, я впаду». Поки ви не бачили, як він падає, ви не знаєте, чи це твердження правдиве. Сьогодні — три способи змусити тест упасти навмисно:
| Що перевіряємо | Як | Розділ |
|---|---|---|
| e2e-тест | Навмисний дефект (defect seeding): зсуваємо межу чи число в коді — тест має впасти. У фінальному проєкті — команда defects:check |
4 |
| юніт-тести | Мутаційне тестування: Stryker автоматично робить сотні маленьких змін і рахує, скільки спіймано. Mutation score — метрика якості тестів, а не кількості | 5 |
| healer | Зміна поведінки: агент, який лагодить тести, може «полагодити» тест під дефект. Перевіряємо, чи не послабив він перевірку | 3 |
#«Пакунок»: 11 функцій — сценарії — джерело правди
Застосунок для демо і завдання — навчальний «Пакунок» вигаданого сервісу доставки: стартовий трекер посилки і 11 невеликих функцій у меню. 00 — демо тренера, 01–10 — ваші варіанти. Кожна функція — форма з полів, кнопка і картка результату або текст помилки. Стек той самий, що в шаблоні фінального проєкту QA: TypeScript + Vite, Playwright 1.64, Vitest, Stryker 10.
У кожної функції — правила і сценарії мовою користувача у materials/features/NN-назва/scenarios.md. Це вхід для агента і джерело правди для очікуваних значень. Сценарії демо-функції 00:
| № | Дії | Очікувано |
|---|---|---|
| S1 | 2 кг · місто | «До сплати: 50 грн» |
| S2 | 5 кг · область, і 5,2 кг | 70 грн · 80 грн |
| S3 | 12 кг · Україна | 160 грн |
| S4 | 30,5 кг | помилка, картки немає |
| S5 | вага «два» | «Поле «Вага, кг» має бути числом.» |
Правила 00: місто — 50 грн, область — 70, Україна — 90, до 5 кг включно; далі +10 грн за кожен розпочатий кілограм; понад 30 кг — відмова.
Чому сценарії, а не сторінка. Агент, якого попросили «напиши тест на сторінку», подивиться, що сторінка показує, і запише це як очікуване. Якщо на сторінці дефект — тест його закріпить. Тому правило для агента: очікуване значення — зі сценарію; розходиться зі сторінкою — зупинись і скажи, це може бути дефект.
Код функцій не змінюємо: ми тестуємо код, а не підганяємо його під тести.
#Тест-кейс і автотест — та сама анатомія
Якщо ви писали тест-кейси вручну, головне в автотесті ви вже вмієте. Ручний тест-кейс має три частини: передумову, кроки й очікуваний результат. Автотест Playwright складається з тих самих трьох частин. Різниця одна: кроки виконує машина, і робить це однаково щоразу.
Тест-кейс TC-00-02 «S2 над межею 5 кг» і той самий тест у коді:
| Частина тест-кейсу | Що в ній | У коді Playwright |
|---|---|---|
| Передумова · Arrange | відкрито сторінку /#/00-delivery-cost, дата «сьогодні» фіксована |
test.beforeEach: page.clock.setFixedTime(…), page.goto(…) |
| Кроки · Act | «Вага, кг» = 5,2 · «Маршрут» = область · «Розрахувати» | fill, selectOption, click |
| Очікуваний результат · Assert | «До сплати: 80 грн» | expect(…).toContainText('До сплати: 80 грн') |
test.beforeEach(async ({ page }) => {
await page.clock.setFixedTime(new Date('2026-11-04T12:00:00'));
await page.goto('/#/00-delivery-cost');
});
test('S2 · 5,2 кг по області — 80 грн', async ({ page }) => {
await page.getByLabel('Вага, кг').fill('5,2');
await page.getByLabel('Маршрут').selectOption('область');
await page.getByRole('button', { name: 'Розрахувати' }).click();
await expect(page.getByTestId('result-lines'))
.toContainText('До сплати: 80 грн');
});
- Передумова — у
beforeEach: вона виконується перед кожним тестом файлу. Дату фіксують, щоб тест не залежав від дня, коли його запустили. - Кроки — дії користувача. Поле шукають за підписом (
getByLabel), кнопку — за роллю й назвою (getByRole): так, як їх бачить людина. - Очікуваний результат —
expect. Число 80 взято зі сценарію S2 (70 грн по області + 10 грн за розпочатий кілограм понад 5), а не з того, що показала сторінка.
Схему «підготувати — діяти — перевірити» називають Arrange — Act — Assert. Це те саме, що Given / When / Then з С2.
#Словник: вісім слів, якими говорить тест
Вісім термінів, які звучать на занятті весь час. Їх зручно запам'ятати трьома групами.
Що ми пишемо.
- Тестовий сценарій (test scenario) — що перевіряємо, мовою користувача, без кліків: «посилка трохи важча за 5 кг коштує на 10 грн більше». У QA1 це S1–S5 у
scenarios.md. - Тест-кейс (test case) — одна конкретна перевірка: передумова, кроки, очікуване. З одного сценарію виходить кілька тест-кейсів: S2 дає точки 4,9, 5 і 5,2 кг. В автотесті тест-кейс — один
test('…'). - Тестовий набір (test suite) — тести, які запускають разом: файл
*.spec.tsабо всі e2e черезnpm run e2e. Усередині файлу тести можна згрупувати черезtest.describe.
Як тест вирішує, зелений він чи червоний.
- Перевірка, твердження (assertion) — рядок, що валить тест, коли факт не збігається з очікуваним:
expect(…).toContainText('80 грн'). - Тестовий оракул (test oracle) — джерело правильної відповіді. У QA1 це
scenarios.md, а не те, що зараз показує сторінка. Якщо оракул — сторінка, тест закріплює дефект як правило; такі тести — у розділі 4.
На чому тест стоїть.
- Фікстура (fixture) — готове оточення, яке тест отримує до старту:
{ page }від Playwright (свої фікстури роблять черезtest.extend). Підготовку перед кожним тестом пишуть уbeforeEach— це hook. Фіксована датаpage.clock.setFixedTime— це вже тестовий дублер, stub (словник 2). Тестові дані (test data) — посилкиPARCELSдля трекера. - Локатор (locator) — спосіб знайти елемент.
getByRole,getByLabel— як бачить користувач;getByTestId— окрема міткаdata-testidдля тестів: стабільна, але користувач її не бачить. Усі три стійкіші за CSS-селектори. - Прогін (test run) — запуск набору плюс звіт:
npx playwright test(абоnpm run e2e), звіт —npm run e2e:report, trace.
Тест-кейс, чек-лист і тестові дані знайомі з С2. Дублери, flaky-тести і хибні тривоги — у словнику 2 (розділ 4). Усі терміни разом — у словнику в кінці цього розділу і в шпаргалці (розділ 12).
#Піраміда тестування — внизу багато й швидко, вгорі мало й дорого
Піраміду тестування описав Майк Кон у книзі «Succeeding with Agile» (2009). Її ідея: тести різних рівнів коштують по-різному, тому й кількість їх має бути різною. Знизу вгору:
| Поверх | Що це | У QA1 |
|---|---|---|
| юніт (модульні) | одна функція правил без сторінки: мілісекунди, тестів можуть бути сотні | Vitest: src/domain/*.test.ts; rules.test.ts для своєї функції пишете в L3 |
| компонентні (UI) | сторінка або UI-компонент у jsdom — без справжнього браузера: поля, картка, текст помилки | Vitest + jsdom: src/app.test.ts, src/ui/parcel-card.test.ts; page.test.ts — L3 |
| інтеграційні · контрактні | стик двох систем: чи однаково вони розуміють формат даних | немає; це QA2, вхід — контракт стику з С6 |
| API | сервіс без інтерфейсу: запит → відповідь | немає; це QA2 |
| e2e / UI | справжній браузер, шлях користувача від початку до кінця | Playwright: e2e/features/*.spec.ts |
| ручне · дослідницьке | людина: зручність, зрозумілість, «спірне» | «спірне» з плану planner, як 1e1 у полі ваги |
Чим вище поверх, тим тест повільніший, дорожчий і крихкіший: e2e падає від будь-чого на шляху, від мережі до підпису кнопки. І тим менш точно він показує, де дефект. Юніт-тест, що впав, вказує на функцію і рядок. Тому внизу тестів багато, а вгорі мало.
«Компонентні» тут — у сенсі фронтенду: тест сторінки чи UI-компонента без браузера. У ISTQB «компонентне тестування» — синонім модульного, тобто юніт; детальніше — нижче, у розділі про рівні.
Ручне тестування на вершині — не «гірше» за автоматизоване. Це те, що машина не оцінить: чи зрозуміла помилка, чи зручна форма, що робити з «1e1».
Орієнтир у чистому проєкті QA1, до ваших тестів: npm test (юніт + компонентні) — 38 тестів за секунди; npm run e2e — 16 тестів приблизно за хвилину разом зі збіркою. Це орієнтир, а не закон: важить не відсоток, а щоб кожна перевірка жила на своєму поверсі.
#Конус морозива: коли піраміду перевернули
Частий стан у командах, де автоматизація почалася з інтерфейсу: юніт-тестів майже немає, зате сотні e2e і тиждень ручної регресії. Фігура перевернута і схожа на ріжок морозива — звідси назва антипатерну (ice-cream cone; термін Alister Scott, 2012).
Симптоми:
- регресія — тиждень ручних прогонів перед кожним релізом;
- e2e червоніють від перейменованої кнопки — це хибні тривоги, і команда перестає їм вірити;
- коли e2e справді червоний, він не каже, де дефект: у правилі, у сторінці чи в даних.
З агентами ризик більший: e2e-тест тепер коштує хвилину, і спокуса писати лише їх зростає. Відповідь — не «менше e2e», а кожну перевірку робити на найнижчому рівні, де вона можлива. Правило ціни перевіряє юніт-тест, сторінку — компонентний, шлях користувача — кілька e2e. Тому в QA1 обидва поверхи: e2e — у L1–L2, юніт і компонентні — у L3.
#Де який тест — у проєкті QA1
Карта заняття: які тести є в проєкті, де вони лежать і чим перевірити самі тести. Рядки стоять як поверхи піраміди: ручне вгорі, юніт унизу.
| Рівень | Що перевіряє | Інструмент | Де в QA1 | Хто зазвичай пише | Як перевірити сам тест |
|---|---|---|---|---|---|
| ручне · дослідницьке | зручність, зрозумілість, «спірне» | людина | «спірне» з плану planner: 1e1, 0x10 |
QA | — |
| e2e / UI | шлях користувача в браузері | Playwright | e2e/features/NN-….spec.ts (L1–L2) · e2e/tracking.spec.ts |
QA-автоматизатор, агенти Playwright | npm run defects:check |
| API | сервіс без UI: запит → відповідь | — | немає · QA2 | QA-автоматизатор | QA2 |
| інтеграційний · контрактний | стик двох систем, формат даних | — | немає · QA2, контракт стику з С6 | обидві сторони стику + QA | QA2 |
| компонентний (UI) | сторінка в jsdom: поля, картка, текст помилки | Vitest + jsdom | page.test.ts (L3) · src/app.test.ts · src/ui/parcel-card.test.ts |
розробник, автоматизатор | Stryker |
| юніт (модульний) | правило: вага + маршрут → ціна | Vitest | src/features/NN-…/rules.test.ts (L3) · src/domain/*.test.ts |
розробник; на курсі — ви з агентом | Stryker, mutation score |
Остання колонка — головна: defects:check і Stryker — не рівні тестів, а перевірка самих тестів. Для e2e — навмисний дефект (розділ 4), для юніт- і компонентних тестів — мутанти (розділ 5).
Команди:
npm test— юніт і компонентні тести;npm run e2e— e2e;npm run defects:check— чи ловлять e2e навмисні дефекти;npm run mutation:feature -- NN— чи ловлять юніт і компонентні тести мутантів;npm run coverage— які рядки виконались під час юніт- і компонентних тестів. Це ще не означає, що їх перевірено.
CI у проєкті QA1 вимкнено: усе запускаєте руками.
#Рівень — де перевіряємо, тип — що, скринька — як проєктуємо
Плутанина номер один: «регресійне тестування — це рівень?». Ні. Тут три незалежні питання.
Рівень — масштаб: що саме ізолюємо. Юніт, або модульний (функція) → компонентний (UI-компонент або сторінка в jsdom) → інтеграційний (стик) → системний (уся система, e2e) → приймальний (UAT: приймання за критеріями з С2). Чим вище, тим більше системи в одному тесті.
Увага щодо назв. У ISTQB (CTFL) «компонентне тестування» — те саме, що модульне, тобто юніт, а наступний рівень — компонентне інтеграційне. На заняттях «компонентний» — у сенсі фронтенду: тест сторінки чи UI-компонента без браузера. За ISTQB такий тест ближчий до компонентного інтеграційного.
Тип — мета: що хочемо дізнатися.
- функціональне — чи виконується правило: 5,2 кг → 80 грн;
- нефункціональне — швидкодія, доступність, безпека, зручність;
- підтверджувальне (confirmation testing, re-test) — виправлений дефект справді виправлено;
- регресійне — зміна не зламала решту; разом з підтверджувальним ISTQB називає це тестуванням, пов'язаним зі змінами;
- smoke (димове) — швидко, широко й неглибоко: застосунок узагалі працює. У QA1 це 11 тестів «розрахунок працює» в
e2e/features— по одному на функцію. Тому тест A годиться як smoke, але не як перевірка правила; - sanity (санітарне) — вузько й глибше: конкретна зміна чи виправлення після збірки.
У глосарії ISTQB sanity test — синонім smoke test; у командах їх зазвичай розрізняють так, як вище.
Скринька — як спроєктовано тест, а не його рівень.
- чорна — за поведінкою і
scenarios.md: e2e, ручне тестування — і юніт-тест, написаний за сценарієм (як у L3). Точка 5,01 кг — теж чорна скринька: її дає правило «за кожен повний чи неповний кілограм понад 5 кг» у специфікації; - біла — тести, спроєктовані за кодом: гілки й умови
rules.ts, живі мутанти Stryker, що ведуть у код; - сіра — знаєте код, але тестуєте ззовні: форма читає поле через
Number(…)уsrc/features/form-page.ts, тож ви пробуєте1e1(= 10) і0x10(= 16) через сторінку.
#Словник QA1 — усі терміни
Терміни заняття в одному місці. Приклади — з функції 00 і трекера посилки.
| Термін | English | Що це |
|---|---|---|
| тест-кейс | test case | Одна перевірка: передумова, кроки, очікуваний результат. В автотесті — один test('…'), напр. «S2 · 5,2 кг по області — 80 грн». |
| тестовий сценарій | test scenario | Що перевіряємо — мовою користувача, без кліків. З одного сценарію — кілька тест-кейсів: S2 → 4,9 · 5 · 5,2 кг. У QA1 — S1–S5 у scenarios.md. |
| тестовий набір | test suite | Тести, які запускають разом: файл *.spec.ts, усі e2e через npm run e2e; група всередині файлу — test.describe. |
| перевірка, твердження | assertion (expect) | Рядок, що валить тест, коли факт не збігається з очікуваним: expect(…).toContainText('До сплати: 80 грн'). Тест без суттєвих перевірок не має діагностичної цінності. |
| тестовий оракул | test oracle | Джерело правильної відповіді. У QA1 — scenarios.md, а не те, що зараз показує сторінка: інакше тест закріплює дефект. |
| фікстура, тестові дані | fixture, test data | Фікстура — готове оточення до старту: { page } від Playwright (свої — через test.extend); підготовка — у beforeEach (hook); фіксована дата page.clock.setFixedTime — це stub. Тестові дані — посилки PARCELS для трекера. |
| локатор | locator | Спосіб знайти елемент. getByRole, getByLabel — як бачить користувач; getByTestId — окрема мітка data-testid для тестів (стабільна, але користувач її не бачить). Усі три стійкіші за CSS-селектори. |
| прогін | test run | Запуск набору і звіт: npx playwright test (або npm run e2e), звіт — npm run e2e:report, trace. |
| тестовий дублер: stub, fake, mock, spy | test double | Підміна справжньої залежності. Stub (заглушка) — заготовлена відповідь (now: () => NOW, brokenApi, slowApi у src/app.test.ts). Fake — спрощена робоча версія (fakeApi шукає посилку в PARCELS). Mock — дублер, у якого перевіряють взаємодію: expect(fn).toHaveBeenCalledWith(…). Spy (шпигун) — записує виклики: vi.spyOn(obj, 'm') обгортає справжню функцію, vi.fn() — порожня функція-записувач. Mock і spy у QA1 не використовуються. |
| нестабільний тест | flaky test | То зелений, то червоний без змін у коді: залежить від часу, порядку тестів, мережі, waitForTimeout. Лікують причину, а не повторами: у QA1 дата фіксована, а локально retries: 0. |
| хибна тривога · пропущений дефект | false positive · false negative | Хибнопозитивний результат — тест червоний, а дефекту немає (зламаний локатор, flaky). Хибнонегативний — тест зелений, а дефект є (тест A з toBeVisible()). Не плутати з позитивним тестом — тестом на валідних даних. |
| піраміда тестування | test pyramid | Внизу багато швидких юніт- (модульних) і компонентних (UI) тестів, вище — менше інтеграційних і API, вгорі — мало e2e і ручних. Кожну перевірку роблять на найнижчому рівні, де вона можлива. |
| конус морозива | ice-cream cone | Антипатерн — перевернута піраміда: багато ручних і e2e, мало юніт. Регресія повільна й дорога, багато хибних тривог, червоний тест не каже, де дефект. Термін — Alister Scott (2012). |
| рівень тестування | test level | Масштаб того, що тестуємо: юніт (модульне), компонентний (UI-компонент або сторінка; не плутати з ISTQB component testing = юніт), інтеграційний (контрактний), системний (e2e), приймальний (UAT). |
| тип тестування | test type | Мета перевірки: функціональне, нефункціональне (швидкодія, доступність, безпека, зручність), пов'язане зі змінами (підтверджувальне, регресійне). Будь-який тип можна робити на будь-якому рівні. |
| чорна, біла, сіра скринька | black, white, grey box | Як спроєктовано тест, а не його рівень. Чорна — за поведінкою і scenarios.md (e2e, ручне, юніт-тест за сценарієм). Біла — за кодом (гілки rules.ts, живі мутанти Stryker). Сіра — знаєте код, тестуєте ззовні: форма робить Number(…) у form-page.ts, тож пробуєте 1e1 і 0x10 через сторінку. |
| димове, санітарне тестування | smoke, sanity | Smoke — швидко, широко й неглибоко: застосунок узагалі працює (11 тестів «розрахунок працює» в e2e/features, по одному на функцію). Sanity — вузько й глибше: конкретна зміна чи виправлення після збірки. У глосарії ISTQB sanity — синонім smoke; у командах їх зазвичай розрізняють так. |
| підтверджувальне, регресійне тестування | confirmation testing (re-test), regression testing | Разом — тестування, пов'язане зі змінами. Підтверджувальне — виправлений дефект справді виправлено. Регресія — зміна не зламала решту: npm test і npm run e2e після кожної зміни. На QA2 це робить CI автоматично. |
| класи еквівалентності | equivalence partitioning | Значення, які програма обробляє однаково, утворюють один клас; на клас — один представник. Для 00: вага ≤ 0, (0; 5], (5; 30], > 30, не число; маршрути місто, область, Україна. «Не число» відсікає форма (form-page.ts), тож цей клас — для компонентного чи e2e-тесту. |
| аналіз граничних значень | boundary value analysis | Точки на межі й поруч з обох боків: 4,99 · 5 · 5,01; 30 · 30,01; 0 · 0,01. Тут живуть дефекти «на одиницю»: < замість <=, 5 замість 6. |
| таблиця рішень · переходи станів | decision table · state transition | Таблиця рішень — коли результат залежить від комбінації умов (маршрут × вага → ціна). Переходи станів — коли поведінка залежить від стану (Створена → … → Отримана); рівні покриття — кожен стан, кожен допустимий перехід, недопустимі переходи. |
| покриття коду і mutation score | code coverage, mutation score | Покриття (npm run coverage, лише тести Vitest) — які рядки виконались: юніт-тест deliveryCost(2, 'місто'), що перевіряє лише ok, виконує розрахунок і не помітить зміну ціни. Mutation score — частка виявлених мутантів: (вбиті + timeout) / (вбиті + timeout + живі + без покриття); compile error не рахується. |
| навмисний дефект | defect seeding | Вносимо відомий дефект у код (defects/*.json: знайти → замінити), проганяємо тести й повертаємо код. Тест, що лишився зеленим, цей дефект не бачить. У QA1 — npm run defects:check. |
3. Тест зі сценарію: агенти Playwright
Цей розділ — про те, як агент отримує тест зі сценарію: три способи, агенти Playwright (planner, generator, healer), як їх підключити і де між їхніми кроками ваша перевірка. Два демо: план і тести S1–S4, потім healer на двох змінах. Наприкінці — правило, яке не дає healer «полагодити» тест під дефект.
#Сценарій словами → агент з живим браузером → тест
Агент без браузера вгадує локатори з коду. Агент з браузером відкриває сторінку, бачить поля й кнопки і пише тест за кроками, які щойно виконав. Playwright 1.64 дає для цього готових агентів:
- planner — досліджує сторінку і пише план тестування;
- generator — виконує кроки плану в браузері і записує тест;
- healer — запускає тести, що впали, і лагодить їх.
Працюють вони через MCP-сервер playwright-test, вбудований у Playwright.
#Як агент отримує тест: три способи
| Агент без браузера | Playwright MCP | Playwright Test Agents | |
|---|---|---|---|
| Що бачить | код, сценарій, вивід тестів | живу сторінку: дерево доступності, знімки | живу сторінку і тест: запуск, пауза на помилці |
| Локатори | вгадує з коду | бере з того, що бачить | генерує з того, що бачить |
| Як підключити | нічого | @playwright/mcp — окремий сервер |
npx playwright init-agents |
| Коли | швидко, коли розмітка проста | дослідити сторінку, ручні перевірки | тести: план → тест → лагодження |
- Агент без браузера — найпростіше: «ось сценарій, напиши тест». На простій формі це часто спрацьовує, але агент не бачить, що насправді на екрані. Запускає тест — і виправляє, поки не пройде.
- Playwright MCP (
@playwright/mcp) — окремий MCP-сервер, через який агент керує браузером: відкриває сторінку, отримує дерево доступності — ролі, підписи, тексти, — натискає. Зручно досліджувати сторінку чи робити ручну перевірку руками агента. - Playwright Test Agents — з'явилися в Playwright 1.56, у проєкті QA1 — 1.64. Сервер
playwright-testуміє те саме, що й браузерний, плюс запускати тести, ставити тест на паузу на помилці й генерувати локатори. Приходить разом із Playwright проєкту — окремо нічого ставити не треба.
MCP — Model Context Protocol — стандартний спосіб дати агенту інструменти. Тут інструменти — «відкрий сторінку», «натисни», «запусти тест», «покажи знімок». Ми вже згадували MCP у С6, коли говорили про інтеграції.
#Одна команда — три агенти і MCP-сервер
У корені проєкту:
npx playwright init-agents --loop=claude
Для Claude Code команда створює:
.claude/agents/
├── playwright-test-planner.md
├── playwright-test-generator.md
└── playwright-test-healer.md # модель sonnet
.mcp.json # сервер playwright-test
specs/README.md # сюди planner пише плани
e2e/seed.spec.ts # якщо вже є — не перезаписує
--loop— для якого агента:claude,codex,copilot,opencode,vscode. Файли створюються у форматі того агента.- Описи агентів — звичайні markdown-файли з інструкціями, їх можна читати й змінювати. Модель у них — sonnet.
.mcp.json— підключення сервераplaywright-test. На Windows тамcmd /c npx playwright run-test-mcp-server— так і має бути.seed.spec.ts— тест, з якого агенти починають: відкриває застосунок у потрібному стані. У проєкті QA1 seed уже є, з фіксованою датою «сьогодні», і команда його не перезаписує. У новому проєкті команда створює заготовку з невикористаними змінними — у проєкті з суворою перевіркою типів збірка впаде, поки ви її не заповните.- Після команди перезапустіть агента в теці проєкту. Claude Code спитає, чи підключати сервер з
.mcp.json, — погодьтеся.
.claude/agents/ і .mcp.json — командні налаштування: їх комітять разом із тестами.
#Planner → generator → healer — і ви між кроками
- Planner відкриває сторінку через seed, досліджує її і пише план у
specs/— markdown зі сценаріями і кроками. Вхід — вашscenarios.mdіseed.spec.ts. Ви: план збігається зі сценаріями? Межі є? - Generator бере сценарії з плану чи з вашого промпту, виконує кроки в живому браузері і записує тест з того, що реально спрацювало, — у
e2e/features/NN-назва.spec.ts. Ви: що перевіряє коженexpect— і звідки очікуване значення? - Тест запускаєте ви або агент. Червоний — healer: запускає, ставить на паузу на помилці, дивиться сторінку, змінює тест і запускає знову. Ви: що він змінив і чому?
#Демо 1: план і тести S1–S4
Два промпти, один за одним, у Claude Code з моделлю Sonnet.
Промпт 1 — planner:
Використай агента playwright-test-planner: склади план тестування сторінки «00 · Вартість доставки» (адреса /#/00-delivery-cost) нашого застосунку. Правила функції — у materials/features/00-delivery-cost/scenarios.md. План збережи в specs/00-delivery-cost.md. Наприкінці коротко: скільки сценаріїв і які межі ти знайшов.
Промпт 2 — generator:
Прочитай materials/features/00-delivery-cost/scenarios.md. Використай агента playwright-test-generator: напиши e2e-тести для сценаріїв S1–S4 у файлі e2e/features/00-delivery-cost.spec.ts — додай нові тести, наявний тест у файлі не чіпай. Очікувані значення — зі сценаріїв. Потім запусти цей файл тестів і покажи результат.
На що дивитися:
- План: межі під, на і над 5 і 30 кг — чи всі є?
- Тест: локатори
getByLabel,getByRole,getByTestId. - Кожен
expect— число чи текст зі сценарію.
У записаному прогоні planner дослідив сторінку в браузері — прогнав 24 вхідні значення — і записав план на 23 сценарії в 6 групах: позитивні, межа 5 кг, межа 30 кг, межа 0 кг, валідація, початковий стан. Межі — точками під, на і над 0, 5, 6 і 30 кг. Окремо він позначив спірне: запис 1e1 і 0x10 застосунок приймає як число. Це питання до власника, а не до тесту.
Після тестів — запуск і звіт:
npx playwright test e2e/features/00-delivery-cost.spec.ts
npm run e2e:report
#Що зробив generator — і що довелося виправити
Фрагмент тесту із записаного прогону — назви тестів скорочено «…», S4 — у першій версії, до виправлення:
test('… S2 над межею 5 кг (5,2 кг, область) — 80 грн',
async ({ page }) => {
await page.getByLabel('Вага, кг').fill('5.2');
await page.getByLabel('Маршрут').selectOption('область');
await page.getByRole('button', { name: 'Розрахувати' }).click();
await expect(page.getByTestId('result-lines'))
.toContainText('До сплати: 80 грн');
});
test('… S4 понад ліміт 30 кг — помилка і без картки',
async ({ page }) => {
await page.getByLabel('Вага, кг').fill('30.5');
// …
await expect(page.getByTestId('result-error'))
.toHaveText('Посилки понад 30 кг відділення не приймає.');
await expect(page.getByTestId('result')).toHaveCount(0); // ✗
});
- 6 нових тестів для S1–S4: S1, три точки S2 — 4,9, 5 і 5,2 кг, — S3 і S4. Очікувані значення — зі сценаріїв. Наявний тест у файлі generator не чіпав, як просили.
- S4 падав. Тест перевіряв, що блоку
resultнемає. А цей блок є на сторінці завжди — у нього вставляється і помилка, і картка. Картку позначаєresult-card. Головний агент запустив тести, побачив падіння і виправив перевірку на «карткиresult-cardнемає». - Без
test.describe. Generator записав тести окремимиtest()поруч із наявним. Для L2 групуdescribeдодаєте ви чи агент на ваше прохання. - Вага з крапкою. Generator вводить
5.2, а не5,2, і пояснює це тим, що поле «має тип number». Насправді це текстове поле, яке приймає і кому, і крапку, — твердження агента про код теж перевіряйте. - Урок. Generator не запустив тести сам — запустив головний агент. Агент пише тест упевнено і помиляється в деталях, яких не видно без запуску. Тому порядок такий: запустити → прочитати кожен
expect→ лише тоді комітити.
#Trace: запис прогону крок за кроком
Trace Viewer показує кожен крок тесту, знімок сторінки до і після, локатор, очікуване й отримане значення, консоль і мережу. На слайді — справжній trace тесту S2 після зміни доплати на 12 грн: ліворуч кроки, по центру знімок сторінки, внизу — очікуване «До сплати: 80 грн».
- Trace записується для кожного тесту і зберігається для тих, що впали, — так налаштовано в проєкті (
retain-on-failure). - Як відкрити:
npm run e2e:report→ тест → View Trace. - UI-режим —
npm run e2e:ui: список тестів, запуск одного, кроки і знімки поруч. Зручно, коли пишете тест разом з агентом.
Агент теж може «подивитися» на сторінку — через MCP. Але trace — це ваш доказ, а не агента. Скріншот звіту чи trace показує, що тест падав і чому; кладіть його в evidence/ свого звіту.
#Healer лагодить тест — а не продукт
Healer — третій агент. Його інструкція — звичайний файл .claude/agents/playwright-test-healer.md: відкрийте і прочитайте.
- Що він робить: запускає тести, що впали → ставить на паузу на помилці → дивиться сторінку → змінює тест → запускає знову. Поки не пройде.
- Що в його інструкції: виправляти локатори, перевірки й очікувані значення. Не ставити питань користувачу — «зробити найрозумніше, щоб тест пройшов». Безнадійний тест — позначити
test.fixme(). - Ризик. Для зміни підпису кнопки це ідеально. Для зміни поведінки — небезпечно. Сторінка показує 82 грн замість 80, і для healer «найрозумніше» — записати 82. Тест знову зелений, але тепер він захищає дефект.
#Тест упав: що змінилося?
Перед тим як лагодити, класифікуйте причину: порівняйте сторінку зі scenarios.md.
| Що змінилося | Приклади | Що робити |
|---|---|---|
| Реалізація, поведінка та сама | підпис поля, текст кнопки, розмітка, порядок кроків; «Розрахувати» → «Порахувати вартість» | лагодити локатор чи крок; очікуване не чіпати |
| Очікувана поведінка | число, межа, текст правила, умова | стоп: «сценарій каже X, сторінка показує Y» → дефект чи нова вимога? |
Якщо змінилась поведінка:
- дефект — тест лишається червоним, пишемо баг-репорт;
- нова вимога — спершу оновлюємо
scenarios.md, потім тест.
Хто вирішує «дефект чи нова вимога» — не тест і не агент, а власник продукту чи аналітик. Це правило ви запишете в правила агента в L2. Ідея та сама, що в С5: правила агента — файл, який працює в кожній сесії.
#Демо 2: дві зміни — один healer
У кожній функції застосунку є дві готові зміни для L2. Їх вносить команда npm run change — код функцій руками не змінюємо.
Зміна інтерфейсу (ui-change.json): кнопку перейменовано «Розрахувати» → «Порахувати вартість». Поведінка та сама.
npm run change -- materials/features/00-delivery-cost/ui-change.json
npm run e2e # червоні
Зміна поведінки (behaviour-change.json): доплата за кілограм понад 5 кг — 12 грн замість 10. Чи це дефект — вирішує не тест і не агент.
git checkout -- src e2e
npm run change -- materials/features/00-delivery-cost/behaviour-change.json
Промпт — однаковий для обох змін:
Після останньої зміни коду впали тести e2e/features/00-delivery-cost.spec.ts. Використай агента playwright-test-healer, щоб їх полагодити. Наприкінці — що саме змінилося і що ти виправив.
Записані прогони:
- Зміна інтерфейсу. Healer замінив назву кнопки в усіх 7 тестах. Очікувані значення й перевірки — без змін. 7 з 7 зелені.
git diff e2eпоказує, що змінено лише назву кнопки. - Зміна поведінки. Упали 2 тести — S2 (80 → 82 грн) і S3 (160 → 174 грн). Healer тести не змінив: показав «сценарій каже 80, сторінка показує 82» і спитав, дефект це чи нова вимога.
Після демо код повертається: git checkout -- src e2e.
#Healer зупинився — а правило все одно запишіть
Запобіжник для healer — правило-класифікація з попереднього слайда, записане явно в правилах агента:
## Перед тим як лагодити тест
Тест упав — спершу класифікуй причину і напиши її користувачу:
- **Змінилася реалізація** (підпис, текст кнопки, розмітка), а поведінка за `scenarios.md` та сама — лагодь локатор чи крок.
- **Змінилася очікувана поведінка** (число, межа, текст правила) — **не змінюй очікуване значення в тесті**. Зупинись, покажи розбіжність «сценарій каже X, сторінка показує Y» і спитай: це дефект чи нова вимога? Тест змінюй лише після відповіді й оновленого `scenarios.md`.
Чесно про записані прогони. Ми запускали healer на зміні поведінки тричі (модель Sonnet): з правилами проєкту QA1, де AGENTS.md називає scenarios.md джерелом правди; із запобіжником; і з прибраними правилами. Усі три рази healer не змінив очікуване значення: прочитав сценарій, показав 80 проти 82 і спитав.
Чому запобіжник однаково потрібен. Інструкція healer лишається такою: «виправ очікувані значення, питань не став». Інша модель, інший агент, довга сесія з десятком тестів, що впали, — і вона може переважити. Правило записують, щоб результат не залежав від везіння.
Де записати:
AGENTS.mdчиCLAUDE.md— діє в кожній сесії;- опис агента healer у
.claude/agents/— лише для нього, і його перезапише наступнийinit-agents.
4. Чи ловить тест дефект · тест-дизайн
Цей розділ — про перевірку e2e-тестів навмисним дефектом (defect seeding). Чотири форми тестів без діагностичної цінності, як працює команда npm run defects:check, демо 3 з дефектом межі 5 кг і правила доброго навмисного дефекту для L1. Потім — техніки тест-дизайну, які допомагають вибирати точки систематично (класи еквівалентності, граничні значення, таблиця рішень, переходи станів), і словник 2: дублери, flaky-тести, хибні тривоги, покриття.
#Внести дефект навмисно — і подивитися, хто впаде
Як дізнатися, чи тест щось ловить? Зламати продукт навмисно і подивитися. Це defect seeding — посів дефектів: зсуваємо межу чи число в коді, проганяємо тести, повертаємо код. Тест, що лишився зеленим, цей дефект не бачить.
Ідея давня: якщо ви навмисно внесли десять дефектів, а тести спіймали сім, — ви знаєте ціну своїм тестам. У шаблоні фінального проєкту QA це вже автоматизовано: дефекти лежать у defects/ як JSON «знайти → замінити», а команда npm run defects:check вносить їх по одному, проганяє тести і повертає код. У проєкті QA1 команда та сама.
#Тест без діагностичної цінності: як він виглядає
Чотири типові форми. Усі зелені, усі написані агентами в реальних прогонах — або дуже схожі.
1 · Перевіряє те, що є завжди. Найчастіший тест «розрахунок працює»:
await expect(page.getByTestId('result')).toBeVisible();
Блок результату є і з карткою, і з помилкою.
2 · Перевіряє заголовок, а не число.
await expect(page.getByTestId('result-title'))
.toHaveText('Вартість доставки');
Картка з'явилася, але з якою сумою — не перевірено. Заголовок той самий за будь-якої суми.
3 · Перевіряє власний ввід.
await page.getByLabel('Вага, кг').fill('7');
await expect(page.getByLabel('Вага, кг')).toHaveValue('7');
Доводить, що поле приймає текст. Про розрахунок — нічого.
4 · Очікуване — «що показала сторінка». Найпідступніший:
// агент подивився на сторінку: 82 грн
await expect(lines).toContainText('До сплати: 82 грн');
Такий тест падає, коли дефект виправляють, і зелений, поки дефект живе. Він закріплює дефект як правило.
Як їх знайти: прочитати кожен expect і спитати «що має зламатися в продукті, щоб це впало?». Якщо відповідь «нічого» або «сторінка не відкриється» — тест без діагностичної цінності. А довести це — навмисним дефектом.
#Як працює npm run defects:check
Дефект — JSON-файл: ідентифікатор, опис і список змін «у файлі знайти рядок → замінити». Наприклад, дефект демо 3:
{
"id": "D-04",
"title": "Доплата починається понад 6 кг замість понад 5 кг",
"changes": [{
"file": "src/features/00-delivery-cost/rules.ts",
"find": "export const INCLUDED_KG = 5;",
"replace": "export const INCLUDED_KG = 6;"
}]
}
Скрипт бере дефекти по одному:
- вносить зміну в
src/; - збирає застосунок і проганяє e2e-тести;
- упав хоч один тест — «спіймано ✓», жоден — «пропущено ✗»;
- повертає код і дописує результат у
reports/defects/summary.md— таблицю з назвами тестів, що впали. Її ви вставляєте у звіт L1.
- Рядок
find— точно один раз у файлі. Інакше скрипт зупиняється. - Код після перевірки повертається сам.
- Лише ваш дефект:
npm run defects:check -- --only D-04.
У чистому проєкті QA1 defects:check показує «спіймано 3 з 3» — це три дефекти шаблону в трекері посилки. Той самий формат — у шаблоні фінального проєкту QA.
#Демо 3: межа 5 → 6 кг — хто спіймав?
Промпт:
Перевір, чи мають наші тести для функції 00 діагностичну цінність. Додай навмисний дефект defects/D-04-extra-kg-boundary.json (формат — defects/README.md): у src/features/00-delivery-cost/rules.ts доплата починається не понад 5 кг, а понад 6 кг (заміни рядок `export const INCLUDED_KG = 5;`). Запусти npm run defects:check і поясни: які тести спіймали дефект, а який тест функції 00 пройшов з дефектом і чому.
Результат записаного прогону — «спіймано 1 з 1»:
| Тест | Вага | Результат з дефектом | Чому |
|---|---|---|---|
| S1 | 2 кг | зелений | нижче обох меж — доплати немає ні з 5, ні з 6 |
| S2 під межею | 4,9 кг | зелений | те саме |
| S2 на межі | 5 кг | зелений | те саме: межа зсунулась угору, а не вниз |
| S2 над межею | 5,2 кг | впав ✓ | з дефектом доплати ще немає: 70 грн замість 80 |
| S3 | 12 кг | впав ✓ | доплата за 6 кг замість 7: 150 грн замість 160 |
| S4 | 30,5 кг | зелений | помилка ліміту спрацьовує до розрахунку доплати |
| «розрахунок працює» | 7 кг | зелений | сума змінилася (80 замість 90), але тест перевіряє лише toBeVisible() |
Агент сам запропонував посилити тести точками 5 → 5,01 → 6 → 6,01 кг.
#Добрий навмисний дефект — для L1
Так:
- Правило вашого варіанту зі
scenarios.md: межа, число, умова. Найкорисніші — на межі: «не пізніше ніж» стало «раніше ніж», 7 днів стало 8. - Одна зміна — як помилка, яку міг би зробити розробник:
<=→<, 5 → 6, пропущена перевірка. - Видно на сторінці: інше число, немає помилки, зайва помилка. E2E-тест бачить лише сторінку.
- Ваш тест його ловить — і ви знаєте, який саме.
Ні:
- Ламає збірку чи всю сторінку — впаде будь-який тест, це нічого не доводить.
- Зміна, якої не видно: перейменована змінна, коментар.
- Дефект під тест, який уже є. Порядок — спершу дефект з правила, потім перевірка, чи ловить.
- Зміна чужої функції чи
form-page.ts.
Файл — defects/NN-коротка-назва.json, формат як у D-04. Запуск — npm run defects:check, у звіт — таблиця з reports/defects/summary.md.
#Класи еквівалентності й межі — точки вибирають, а не вгадують
У демо 3 одна точка на межі зловила зсув межі лише в один бік. Як вибирати точки систематично, а не «на око»? Для цього є дві класичні техніки тест-дизайну.
Класи еквівалентності (equivalence partitioning). Значення, з якими програма поводиться однаково, утворюють один клас. Для програми 2 і 3 кг — те саме, тож на клас досить одного представника. Для ваги у функції 00 класів п'ять:
| Клас ваги | Що робить програма | Представник |
|---|---|---|
| ≤ 0 | помилка | −1 кг |
| (0; 5] | базова ціна маршруту | 2 кг |
| (5; 30] | база + 10 грн за кожен розпочатий кілограм понад 5 | 12 кг |
| > 30 | відмова | 31 кг |
| не число, порожнє поле | помилка (перевіряє форма — form-page.ts; юніт-тест rules.ts сюди не дістане) |
«два» |
Плюс маршрут — ще три класи: місто, область, Україна. Невалідні класи (≤ 0, > 30, не число) — теж класи, і тест на кожен потрібен.
Клас «не число» — підказка, де живе тест. Форма читає поле в src/features/form-page.ts і відсікає порожнє поле й «два» ще до функції правил: deliveryCost отримує вже число. Тож цей клас перевіряють компонентним або e2e-тестом, а не юніт-тестом rules.ts.
Граничні значення (boundary value analysis). Дефекти живуть на межах класів, бо розробник найчастіше помиляється «на одиницю»: < замість <=, 5 замість 6. Тому точки ставлять на межі і поруч з обох боків. Для маршруту «область»:
| Межа | Точки | Очікувано |
|---|---|---|
| 0 кг | 0 · 0,01 | помилка · 70 грн |
| 5 кг | 4,99 · 5 · 5,01 | 70 · 70 · 80 грн |
| 30 кг | 30 · 30,01 | 320 грн · відмова |
Двоточковий варіант — точка на межі й найближча за нею (5 і 5,01). Триточковий — ще й точка перед межею (4,99, 5 і 5,01).
У сценарії S2 точки інші — 4,9 · 5 · 5,2 кг. Вони теж під і над межею; 4,99 і 5,01 — найближчі при кроці 0,01 кг. Крок — домовленість: поле приймає будь-який дріб (і 5,001 коштує 80 грн), тож його уточнюють у власника. А правило «за кожен розпочатий кілограм» робить межею кожен цілий кілограм: 6 кг → 80 грн, 6,01 кг → 90 грн. Межу 6 кг planner теж запропонував у демо 1. Зсув «на одиницю» — саме те, що вносив дефект у демо 3 (5 → 6 кг) і що робить мутант Stryker > → >= (розділ 5).
#Комбінації і стани — таблиця рішень і переходи станів
Ще дві техніки — для випадків, коли результат залежить не від одного поля.
Таблиця рішень (decision table) — коли результат залежить від комбінації умов. Для функції 00 це маршрут і вага. Колонки — класи ваги, 5,2 кг — представник класу (5; 30]:
| Маршрут \ вага | (0; 5] кг | 5,2 кг | > 30 кг |
|---|---|---|---|
| місто | 50 грн | 60 грн | відмова |
| область | 70 грн | 80 грн | відмова |
| Україна | 90 грн | 100 грн | відмова |
Три маршрути на три класи ваги — дев'ять комбінацій. Але правило «понад 30 кг — відмова незалежно від маршруту» зводить останню колонку до однієї перевірки: виходить 7 тестів. Класи ≤ 0 і «не число» теж не залежать від маршруту — ще 2 тести, усього 9. Клітинка, якої немає в тестах, — сценарій, якого ніхто не перевірив.
Переходи станів (state transition) — коли поведінка залежить від стану. Приклад — трекер посилки зі стартового застосунку (src/domain/status.ts): статуси «Створена» → «Прийнята у відділенні» → «В дорозі» → «Прибула у відділення» → «Отримана».
У техніки є рівні покриття: кожен стан → кожен допустимий перехід → недопустимі переходи. У QA1 статус приходить готовим із даних (PARCELS), а код лише показує його: назву, крок, шкалу. Тож тут тестують кожен стан.
Що тестуємо:
- кожен стан: назву і шкалу прогресу («Створена» — 0%, «Отримана» — 100%);
- очікувану дату доставки — вона є лише в станах «Прийнята у відділенні» і «В дорозі»;
- стан без шкали: «Повертається відправнику» — картка без прогресу (
progressPercentповертаєnull).
#Словник 2: дублери, flaky і хибні тривоги
Слова, які знадобляться в розділі 5 і в розмовах з розробниками.
Тестові дублери (test doubles) — підміни справжньої залежності. У компонентних тестах сторінку відрізають від сервера і годинника, щоб тест був швидким і передбачуваним.
| Дублер | Що робить | У QA1 |
|---|---|---|
| stub (заглушка) | повертає заготовлену відповідь | now: () => NOW, brokenApi (сервер падає), slowApi (відповідає повільно) у src/app.test.ts; page.clock.setFixedTime(…) у e2e |
| fake (фейк) | спрощена робоча версія | fakeApi у src/app.test.ts шукає посилку в масиві PARCELS замість сервера |
| mock | дублер, у якого перевіряють взаємодію: expect(fn).toHaveBeenCalledWith(…) |
— (у проєкті не використовуються; приклад з Vitest) |
| spy (шпигун) | записує виклики: vi.spyOn(obj, 'm') обгортає справжню функцію і за замовчуванням викликає її; vi.fn() — порожня функція-записувач |
— (у проєкті не використовуються; приклад з Vitest) |
У розмові всі вони — «моки». Уточнюйте, що саме мається на увазі.
Чотири результати тесту. Що каже тест — і чи є дефект насправді:
| Дефект є | Дефекту немає | |
|---|---|---|
| Тест червоний | справжня тривога ✓ | хибна тривога (false positive): зламаний локатор після зміни інтерфейсу, flaky-тест |
| Тест зелений | пропущений дефект (false negative): тест A з toBeVisible() |
усе добре ✓ |
Найнебезпечніший кут — пропущений дефект: його не видно, поки не внесеш дефект навмисно. Саме про це розділ 4. Не плутайте: позитивний тест — тест на валідних даних, а хибнопозитивний — результат тесту.
Flaky — нестабільний тест: то зелений, то червоний без змін у коді. Причини — час, порядок тестів, мережа, waitForTimeout. Лікують причину, а не повторами: у проєкті QA1 дата у тестах фіксована, а локально retries: 0, щоб повтор не ховав нестабільність.
Покриття ≠ перевіреність. npm run coverage (лише тести Vitest) показує, які рядки й гілки виконались. Юніт-тест deliveryCost(2, 'місто'), що перевіряє лише ok, виконує весь розрахунок — і не помітить зміну ціни. Mutation score показує інше: чи помітили б тести зміну рядка. Про це — розділ 5.
Усі терміни заняття — у словнику в розділі 2.
5. Мутаційне тестування
Цей розділ — про перевірку юніт- і компонентних тестів мутаційним тестуванням. Два нижні поверхи піраміди, як Stryker робить мутантів і рахує mutation score, реальний прогін з двома слабкими тестами, живі мутанти як пропущені сценарії, демо 4 і чесний бал.
#Сотні маленьких дефектів — автоматично
Defect seeding — ви вносите дефекти руками, по одному. Мутаційне тестування робить те саме автоматично. Stryker змінює код по одній дрібниці — > на >=, + на -, текст на порожній — і щоразу проганяє юніт-тести. Кожна така зміна — мутант. Упав хоч один тест — мутанта вбито. Жоден — мутант вижив, і це місце, де ваші тести сліпі.
Mutation score — частка вбитих мутантів. Це метрика якості тестів, а не їхньої кількості. У фінальному проєкті QA поріг — 60%.
#Два нижні поверхи — Stryker рахує лише їх
Це два нижні поверхи піраміди тестування (розділ 2, «Піраміда тестування» і «Де який тест»). Тут — лише те, що важливо для Stryker.
| Рівень | Що робить | Інструмент | Як перевіряємо тести |
|---|---|---|---|
юніт · rules.test.ts |
викликає функцію правил напряму: deliveryCost(5.2, 'область') → 80 грн. Мілісекунди; межі — десятками |
Vitest | mutation score |
компонентний · page.test.ts |
монтує сторінку функції в jsdom через mountFeature: поля, кнопка, картка, текст помилки. Без браузера |
Vitest | mutation score |
e2e · *.spec.ts |
справжній браузер, зібраний застосунок, користувацький сценарій. Секунди на тест | Playwright | defects:check, не Stryker |
Юніт-тест не потребує сторінки: вага і маршрут на вході, результат на виході. Тому кожну межу можна перевірити трьома точками:
it('межа 5 кг: 5 — 70 грн, 5,01 — 80 грн', () => {
expect(deliveryCost(5, 'область')).toEqual(ok('Вартість доставки', ['Маршрут: область', 'До сплати: 70 грн']));
expect(deliveryCost(5.01, 'область')).toEqual(ok('Вартість доставки', ['Маршрут: область', 'До сплати: 80 грн']));
});
jsdom — це DOM без браузера, у Node. Компонентний тест перевіряє, що форма читає поля, показує картку і текст помилки. Як монтувати сторінку — приклад у src/app.test.ts.
Stryker проганяє лише Vitest — юніт і компонентні тести. E2E на mutation score не впливають: Stryker їх не запускає, бо один прогін e2e займає хвилину, а мутантів — десятки.
#Мутант: вбитий, живий або без покриття
З одного рядка Stryker робить кілька мутантів: змінює оператор порівняння, замінює умову на true чи false, текст — на порожній рядок, арифметику — на протилежну. Наприклад, з if (w > 30) у rules.ts:
- мутант 1 —
if (w >= 30); - мутант 2 —
if (false); - мутант 3 —
fail(''), порожній текст помилки.
Для кожного мутанта — прогін npm test:
| Результат | Що сталося | Що означає |
|---|---|---|
| killed ✓ | тест упав | тести помітили б таку помилку |
| survived ✗ | усі тести зелені | тести не помітили б таку помилку |
| no coverage ✗ | жоден тест не виконує цей рядок | тестів тут просто немає |
Mutation score = (вбиті + timeout) / (вбиті + timeout + живі + без покриття). Timeout — мутант, через який тести зависли; Stryker теж рахує його виявленим. Поріг у проєкті — 60%: нижче — команда завершується помилкою.
- Compile error — мутант, якого не пропускає TypeScript. У бал не входить.
- Еквівалентний мутант — зміна, яку не видно ззовні. Чесно вбити його неможливо: у звіті пишете, чому він еквівалентний, а не підганяєте тест.
#Два слабкі юніт-тести — 37,93%
Реальний прогін з двома навмисно слабкими юніт-тестами для функції 00: легка посилка по місту і «важка посилка — помилка» лише з перевіркою ok: false.
npm run mutation:feature -- 00
Команда запускає Stryker лише на теці функції 00 — приблизно 20 секунд. Повний npm run mutation по всьому src/ у завданні не потрібен.
Звіт — reports/mutation/index.html:
- усі файли — 37,93%, нижче порогу 60: команда завершується з помилкою;
page.ts— 0%: компонентних тестів немає, 11 мутантів без покриття;rules.ts— 61,11%: тести є, 11 мутантів вбито, але 6 вижили. Два тести — і вже «понад поріг» наrules.ts. Але подивимося, що саме вижило.
#Живий мутант — це сценарій, якого немає в тестах
Кожен живий мутант читається як питання «а що, якби розробник помилився тут?». У звіті Stryker живі мутанти — червоні позначки в рядках коду.
| Рядок | Що змінив Stryker | Яке правило показує | Який тест додати |
|---|---|---|---|
| 16 | > → >= |
межа 30 кг: рівно 30 — ще приймаємо | 30 кг — картка; 30,01 — помилка |
| 18 | + → -, * → / |
доплата понад 5 кг | 5,2 кг — 80 грн, а не лише «ok» |
| 15–16 | текст помилки → "" |
текст відмови для користувача | перевірити точний текст помилки |
- Рядок 16. Якщо рівно 30 кг почнуть відхиляти, тести не помітять: межа 30 кг перевірена лише значенням 30,5. Потрібні точки на і над межею.
- Рядок 18. Доплату почали б віднімати — тест «важка посилка» перевіряє лише, що результат не помилка, а не суму.
- Текст помилки. Тести перевіряють, що помилка є, але не яка. Користувач побачив би порожнє повідомлення.
#Демо 4: агент пише тести — Stryker перевіряє
Промпт:
Напиши юніт-тести для src/features/00-delivery-cost/rules.ts і компонентні тести для сторінки (page.ts через mountFeature у jsdom) за сценаріями з materials/features/00-delivery-cost/scenarios.md. Запусти npm test, потім npm run mutation:feature -- 00. Покажи мутаційний бал, розбери мутантів, що вижили: який сценарій чи межу вони показують. Підсиль тести і запусти мутаційне тестування ще раз. Наприкінці — бал до і після.
Записаний прогін, Sonnet, близько 2,5 хвилини:
| Перша версія тестів | Після підсилення | |
|---|---|---|
| Mutation score | 89,66% (26 вбито, 3 вижили) | 96,55% (28 вбито, 1 вижив) |
rules.ts |
94,44% | 100% |
page.ts |
81,82% | 90,91% |
Живі мутанти, які агент розібрав:
>=на межі 30 кг — додав тести 30 кг (приймається) і 30,01 кг (відмова);- порожній вступний текст сторінки — додав перевірку точного тексту;
- порожнє значення пункту «місто» у списку маршрутів — агент пояснив, що браузер і так вибирає перший пункт, тобто мутант еквівалентний, і не став перевіряти розмітку.
Остання поведінка правильна: тест під реалізацію гірший за чесно пояснений живий мутант.
#Чесний mutation score — і що його ламає
Так бал піднімають нечесно:
- знизити поріг у
stryker.config.mjs; - поставити
// Stryker disableнад «незручним» рядком; - написати тест, що повторює реалізацію: те саме
70 + ceil(w − 5) × 10в тесті, що й у коді. Такий тест вбиває мутантів, але разом із кодом повторить і помилку в правилі; - взяти очікуване з виводу функції, а не зі сценарію.
Агент, якому сказали «досягни 60%», може зробити будь-що з цього. Тому в AGENTS.md проєкту QA1 це заборонено. У вашому робочому проєкті допишіть таке саме.
Так бал стає правдою:
- очікувані значення — зі
scenarios.md; - кожна межа — під, на і над;
- текст помилки — точно, а не «є помилка»;
- живий мутант — або новий тест, або пояснення, чому еквівалентний.
E2E-тести на mutation score не впливають: Stryker проганяє лише Vitest. Для e2e — defects:check. Разом це дві відповіді на одне питання: «чи ловлять мої тести дефекти?»
6. Завдання QA1
Цей розділ — про завдання QA1: проєкт і швидкий старт, ваш варіант, рівні L1–L3 і як здати. Усе, що ми зробили в чотирьох демо, тільки на функції вашого варіанту: демо 1 і 3 — це L1, демо 2 і порожні тести — L2, демо 4 — L3. Повний текст завдання — у README проєкту.
#Завдання QA1: тест і доказ, що він ловить
Той самий застосунок «Пакунок», функція вашого варіанту.
- L1 — тест зі сценарію і навмисний дефект.
- L2 — набір тестів, healer і тест без діагностичної цінності.
- L3 — юніт- і компонентні тести до mutation score 60%.
| Строк | до вівторка, 17.11.2026, 23:59 — два тижні |
| Розбір | на QA2, у середу, 18.11 |
| Проєкт | новий — koldovsky/ai-qa-ba-qa1 |
| Варіант | той самий, що в С1–С6 |
| Інструмент | агент, що запускає команди в терміналі |
#Проєкт koldovsky/ai-qa-ba-qa1 — старт за 15–30 хвилин
Кроки — у README проєкту, розділ «Швидкий старт». Після Fork на gitlab.com, у cmd або Git Bash:
git clone https://gitlab.com/ваш-username/ai-qa-ba-qa1.git
cd ai-qa-ba-qa1
git config user.email …@users.noreply.gitlab.com
git switch -c qa1
npm ci
npx playwright install chromium
npm test # 38 passed
npm run e2e # 16 passed
npm run defects:check # спіймано 3 з 3
npx playwright init-agents --loop=claude # або codex, copilot…
- Що потрібно: Node.js 24 LTS (або 22.22.2+), Git, близько 1 ГБ на диску і агент, що запускає команди.
- Форк, клон свого форку, приватна пошта для комітів — як у С1–С6, без
--global. npm ciі браузер Chromium — один раз. Якщо 38 юніт-тестів і 16 e2e пройшли, аdefects:checkпоказав 3 з 3, — середовище готове.init-agents— для вашого агента. Без агентів Playwright теж можна: попросіть свого агента написати і запустити тест.
Якщо щось не стає — питання в чат групи цього тижня, а не 17-го ввечері.
#Ваш варіант — функція в меню застосунку
Варіант — за першою літерою прізвища, той самий номер, що в С1–С6. Теми — ті самі функції «Пакунка», що в С1–С5, але правила нові, прості й однозначні: тут важливе тестування, а не аналіз вимог.
| Прізвище | № | Функція |
|---|---|---|
| А Б | 01 | Слот кур'єра |
| В Г Ґ | 02 | Відмова від посилки |
| Д Е Є Ж З | 03 | Довірена особа |
| И І Ї Й К | 04 | Кур'єр за посилкою |
| Л М | 05 | Претензія про пошкодження |
| Н О П | 06 | Оплата бонусами |
| Р | 07 | Часткова оплата |
| С | 08 | Повідомлення |
| Т У Ф Х | 09 | Комірка поштомату |
| Ц Ч Ш Щ Ю Я | 10 | Промокод |
Код функції — src/features/NN-…. У кожній функції є все для завдання: scenarios.md з правилами і сценаріями, один «порожній» тест, а також ui-change.json і behaviour-change.json для L2.
Відкрийте застосунок — npm run dev — і свою функцію в меню. Прочитайте scenarios.md своєї функції до того, як просити агента. Функція 00 — демо, не варіант.
#L1: тест зі сценарію і доказ дефектом
Файл тестів — e2e/features/NN-назва.spec.ts.
- Щонайменше 2 сценарії зі
scenarios.mdвашого варіанту, один — з межею. Для межі — точки під, на і над, як у демо 3. - Агент пише тести; ви запускаєте:
npx playwright test e2e/features/NN-назва.spec.ts— зелені. Можна і агентом, але вивід запуску у звіті обов'язковий. - Читаєте самі: що перевіряє кожен
expectі звідки очікуване значення — сценарій, а не сторінка. Що виправили після агента — у звіт; це плюс, а не мінус. - Навмисний дефект
defects/NN-коротка-назва.jsonна правило варіанту →npm run defects:check→ ваш тест спіймав. Лише свій дефект —--onlyз його id. - Токен-звіт — як у С5–С6: цифри агента або оцінка, і одне речення, як зменшити.
У звіті: промпт дослівно, вивід запуску, таблиця «тест → що перевіряє → звідки очікуване», таблиця з reports/defects/summary.md.
- Наявний тест у файлі в L1 не чіпайте — він знадобиться в L2.
- Будь-який агент — Test Agents, Playwright MCP чи агент без браузера — зараховується однаково.
#L2: набір тестів, healer, порожній тест
- Набір із щонайменше 3 тестів за конвенціями Playwright:
test.describe,beforeEach, локаториgetByRole/getByLabel/getByTestId. БезwaitForTimeoutі CSS-селекторів. Серед тестів — помилка і межа. - Healer після зміни інтерфейсу. Спершу закомітьте тести — щоб потім побачити diff healer і повернути все однією командою. Далі
npm run change -- …/ui-change.json→ червоні → healer → зелені. У звіт: що змінив healer і чи не послабив перевірки. - Запобіжник.
behaviour-change.json→ що зробив healer? Потім правило-класифікація в правилах агента → ще раз → різниця. Пишіть чесно, що сталося: якщо агент зупинився і без правила — напишіть, що саме його зупинило. - Тест без діагностичної цінності. У вашому файлі
e2e/features/він уже є, зелений. Знайти, довести дефектом, що дефект проходить крізь нього, і виправити, щоб тест перевіряв результат.
Після кожної вправи — git checkout -- src e2e. Код функцій не змінюємо — лише npm run change.
#L3: юніт- і компонентні тести до 60%
Файли — src/features/NN-назва/*.test.ts.
- Юніт-тести
rules.tsі компонентні тести сторінки —mountFeatureу jsdom; приклад —src/app.test.ts. npm run mutation:feature -- NN→ бал до — з першої спроби.- Щонайменше 3 живі мутанти з розбором, як у таблиці на слайді 313: що змінив Stryker → яке правило чи межу показує → який тест додали → вбито.
- Бал після ≥ 60%. Без змін
stryker.config.mjs, без// Stryker disable, без.skip. Будь-яке послаблення не зараховується. - Одне речення: чому e2e не впливають на mutation score — своїми словами.
- Звіт Stryker —
reports/mutation/index.html; скріншот файлу з мутантами можна покласти вevidence/. - Повний
npm run mutationне потрібен: функції інших варіантів без тестів, тож загальний бал буде нижчим — це очікувано. - Еквівалентний мутант не вбивається — поясніть у звіті, чому. Не підганяйте тест під розмітку.
#Як здати — один MR
Тести і дефект лишаються там, де їх бачать Playwright і Stryker. Звіт — за шаблоном templates/qa1-report.md.
ai-qa-ba-qa1/ # ваш форк · гілка qa1
├── e2e/features/NN-назва.spec.ts # L1–L2
├── defects/NN-коротка-назва.json # L1
├── src/features/NN-назва/*.test.ts # L3
└── submissions/ваш-username/
├── qa1.md # звіт за шаблоном
└── evidence/ # за бажанням
- Merge request: source — ваш форк, гілка
qa1→ targetkoldovsky/ai-qa-ba-qa1,main. Шаблон опису —QA1. Назва:QA1 · username · варіант NN. Виправлення після рев'ю — у той самий MR. - Бали: до 10 за кожен рівень за частинами, рівні окремо, як у С3–С6. Формальності — до −2 за роботу. Відгук — коментар у MR.
- Строк: вівторок, 17.11, 23:59. Розбір — на QA2, у середу, 18.11.
7. Що далі
Цей розділ — про те, що буде після QA1: заняття QA2 і QA3, зв'язок із фінальним проєктом, п'ять головних думок заняття, ресурси і відповіді на типові питання.
#Далі — QA2 і фінальний проєкт
| Коли | Що | Деталі |
|---|---|---|
| вт 17.11, 23:59 | строк завдання QA1 | один MR у проєкт QA1 |
| ср 18.11 | QA2 · API-тести, регресія і автоматичний запуск | агент будує API-тести з опису сервісу: позитивні, негативні, межові. Регресійний набір запускається при кожній зміні — GitLab CI. На початку — розбір QA1; вхід — контракт стику і реєстр міжсистемних сценаріїв з С6 |
| ср 02.12 | QA3 · Діагностика дефектів. Підсумок | від збою до першопричини: чому впав тест у CI, відтворення, локалізація, чернетка виправлення з регресійним тестом. Без завдання; розбір типових зауважень перед поданням фінального проєкту |
| пн 14.12, 23:59 | подання фінального проєкту | — |
Фінальний проєкт QA: усе сьогоднішнє — його частина. У шаблоні ті самі команди: e2e на кожен потік, defects:check, mutation score ≥ 60%. Агенти Playwright підключаються так само — init-agents. Не відкладайте до грудня.
#П'ять речей, які забираєте
- Тест, що не падав, — не доказ. Зелений тест без перевірок виглядає так само, як корисний. Різницю показує лише навмисний дефект. (Розділи 2 і 4.)
- Очікуване — зі сценарію. Не «що показала сторінка». Інакше тест закріплює дефект як правило. (Розділи 2 і 3.)
- Межа — три точки. Під, на і над. Одна точка ловить зсув лише в один бік: тест «на межі» 5 кг не спіймав зсув угору. (Демо 3.)
- Healer лагодить тест, а не продукт. Змінилась реалізація — лагодь. Змінилась поведінка — стоп і питання: дефект чи нова вимога? Правило — у правилах агента. (Демо 2, L2.)
- Mutation score — якість, а не кількість. Живий мутант — сценарій, якого немає в тестах. Бал піднімають тестами, а не порогом. (Розділ 5, демо 4.)
#Ресурси
- Практика QA1 — gitlab.com/koldovsky/ai-qa-ba-qa1: завдання, варіанти, застосунок і сценарії, шаблон звіту.
- Playwright Test Agents — playwright.dev/docs/test-agents: planner, generator, healer,
init-agents. - Playwright: локатори і trace — locators, trace viewer, best practices:
getByRole,getByLabel,getByTestIdі що вважати хорошим тестом. - Stryker — stryker-mutator.io/docs: мутатори, еквівалентні мутанти, звіт.
- Сайт курсу — koldovsky.github.io/2026-ai-qa-ba: слайди, конспект, шпаргалка і записані прогони демо QA1.
- Тренер — Вʼячеслав Колдовський, Ph.D., dev-ai.dev.
Питання між заняттями — у Telegram-чат когорти, гілка QA.
#Питання
Агент напише тест за хвилину. Довести, що тест щось перевіряє, — ваша робота: навмисний дефект, мутант, питання «дефект чи нова вимога?».
Типові питання і короткі відповіді:
- «Агента Playwright немає для мого інструмента?»
init-agentsпідтримує claude, codex, copilot, opencode, vscode. Інакше — ваш агент пише і запускає тест сам; зараховується однаково. - «Лише чат у браузері?» Для QA1 замало: тести треба запускати. Агент з терміналом — обов'язково.
- «Chromium заблоковано?»
PW_CHANNEL=msedge, як у README. - «Healer зупинився і без правила — L2.3 не вийде?» Вийде: напишіть, що його зупинило, і додайте правило однаково.
- «Mutation score нижче 60% на всьому проєкті?» Рахуємо лише вашу функцію:
mutation:feature -- NN. - «Можна змінити код функції, щоб тест пройшов?» Ні. Розбіжність зі сценарієм — знахідка для звіту.
- «Робочий проєкт?» Лише за політикою компанії; у публічних MR — ніколи.