Верифікація результатів AI
Зміст
У попередній главі Генерація тест-артефактів ми навчилися просити модель писати тест-кейси, автотести й дані. Ця глава — про другу, важливішу половину роботи: як не повірити вигляду. AI-вивід майже завжди читається впевнено, охайно відформатований і компілюється — і саме ця гладкість присипляє пильність. Тест зеленіє, код запускається, баг-репорт звучить професійно — а перевіряє тест порожнечу, код кличе неіснуючий метод, а в репорті — вигаданий заголовок. Верифікація AI-результатів — це навичка не довіряти виводу з першого погляду, і на співбесідах її питають прямо: «як ви перевіряєте те, що згенерував AI?».
Це глава з бейджем «поглиблення»: якщо ви на першому проході через основи, її можна пропустити й повернутися пізніше. Але щойно ви реально використовуєте AI для генерації тестів чи коду — вона перестає бути опційною, бо без неї ви комітите чужі помилки під своїм імʼям.
Чому «виглядає правильно» — це пастка
Мовна модель оптимізована передбачати правдоподібний наступний токен, а не казати правду. Механіку розібрано в главі Як працюють LLM; тут важливий наслідок: модель однаково впевнено видає і коректну відповідь, і галюцинацію (hallucination) — вигадку, подану як факт. Впевненість тону не корелює з правильністю. Немає внутрішнього індикатора «тут я не знаю»: page.waitForText() вона напише з тією ж інтонацією, що й реальний page.waitForSelector().
Проблема глибша за саму модель — вона у вашій голові. Чим полірованіший вивід, тим нижча пильність: спрацьовує упередження автоматизації (automation bias) — схильність довіряти пропозиції системи більше, ніж власній перевірці. Ви очікували робочий код, отримали щось схоже на робочий код — і мозок радо ставить галочку. Для QA це особливо небезпечно, бо весь фах збудований на протилежному рефлексі: не вірити, доки не перевірив.
Ключова теза глави: гарний вигляд, охайне форматування, успішна компіляція і навіть зелений прогін — це сигнали правдоподібності, а не правильності. Кожен із них можна отримати, нічого корисного не перевіривши.
Типові дефекти AI-згенерованого коду
Перш ніж говорити «як перевіряти», треба знати, що ламається найчастіше. AI-код має впізнаваний набір дефектів.
Зелені тести, що нічого не перевіряють. Найпідступніший клас. Тест проходить, метрика покриття росте — а перевірки або немає, або вона тавтологічна. Типове:
// Виглядає як тест логіну. Насправді не перевіряє нічого.
test('user can log in', async ({ page }) => {
await page.goto('/login');
await page.fill('#email', 'user@example.com');
await page.fill('#password', 'secret');
await page.click('button[type=submit]');
// ← тут мав бути expect. Його немає. Тест зелений завжди.
});
Той самий дефект у витонченішій формі — забутий await перед web-first асертом Playwright або перевірка, яка істинна за побудовою:
await expect(page.locator('.title')).toBeVisible(); // забули await → проміс не дочекано, помилка проковтнута
expect(true).toBeTruthy(); // тавтологія: не впаде ніколи
Вигадані API й селектори. Модель компонує імена методів зі шматків, які бачила, і легко вигадує неіснуючі: page.waitForText(), cy.getByRole() (у Cypress такого немає), опції конфіга, яких немає в документації. Так само з локаторами: getByRole('button', { name: 'Зберегти' }) виглядає бездоганно, але якщо в реальному DOM цієї ролі чи назви немає — селектор не знайде нічого. Модель не бачила вашої сторінки; вона вгадала розмітку.
Поверхневі перевірки. Покрито лише happy path. Перевіряється присутність елемента, а не його коректність: що кнопка є — але не що вона робить правильну дію; що прийшов 200 — але не що в тілі відповіді правильні дані.
Застарілі патерни. waitForTimeout(3000) замість web-first очікування, задеприкейчені виклики, стиль із туторіалів пʼятирічної давнини, які потрапили в тренувальні дані.
Три опори верифікації
Перевірка AI-виводу тримається на трьох діях: прочитати як рев'юер, виконати як реальність, звірити факти з першоджерелом. Жодна поодинці не достатня.
Рев'ю виводу як навичка
Читайте AI-вивід так, як ревʼюили б пул-реквест джуна — тільки скептичніше. У джуна є контекст задачі й совість сказати «я тут не впевнений»; у моделі немає ні того, ні того. Це та сама навичка, що й статичне рев'ю коду в класичному QA, лише обʼєкт — машинний вивід.
Головне питання до тесту: що станеться, якщо фіча зламається — цей тест почервоніє? Подумки «мутуйте» застосунок: приберіть кнопку, зверніть валідацію, поверніть неправильні дані — чи впіймає це тест? Якщо ні — тест декоративний. Друге питання: чого цей тест НЕ ловить? happy path без негативних перевірок, відсутні межові значення, перевірка присутності замість перевірки значення.
Для не-кодового виводу (тест-кейси з вимог, аналіз багів) рев'ю таке саме: чи всі кроки відтворювані, чи очікуваний результат — це справді інваріант вимоги, а не переказ дії, чи не проґавлені негативні сценарії.
Виконання як перевірка
Найдешевша перевірка реальністю — просто запустити. Компілюється? Селектор резолвиться в живому DOM? Тест доходить до кінця? Це відсіює вигадані API й селектори за секунди — те, чого рев'ю очима може не помітити.
Але виконання має межу, і це найважливіший пункт глави: зелений прогін не доводить, що тест щось перевіряє. Порожній тест із прикладу вище зелений завжди. Тому виконання доповнюють однією дією — зламати навмисне. Змініть очікуване значення на завідомо неправильне, закоментуйте фічу в застосунку, підмініть відповідь. Якщо тест лишається зеленим — він фальшивий. Справжній доказ якості тесту не в тому, що він проходить, а в тому, що він червоніє, коли має.
// Перевірка тесту: тимчасово ламаємо очікування
await expect(page.locator('.balance')).toHaveText('1000'); // було
await expect(page.locator('.balance')).toHaveText('НЕПРАВИЛЬНО'); // тест мусить впасти
Якщо після цієї підміни тест усе одно зелений — асерт не працює (забутий await, не той локатор, soft-режим без перевірки), і зеленому оригіналу гріш ціна.
Верифікація фактів
Для тверджень, а не коду, працює інше правило: звіряй із першоджерелом, а не з тією ж моделлю. Коли AI називає номер версії, назву HTTP-заголовка, сигнатуру методу чи статус-код — це рівно те, що модель галюцинує найохочіше. Перевіряйте проти канонічного джерела: офіційна документація інструмента, специфікація, реальний код застосунку.
Критична пастка: не перепитуйте ту саму модель «ти впевнений?». Вона не має нового джерела — тільки той самий розподіл імовірностей і той самий bias. Часто вона або впевнено підтвердить вигадку, або з тією ж упевненістю розвернеться на 180° без жодної нової інформації. І те, і те — шум. Реальна перевірка приходить лише ззовні: відкрити доку, запустити код, глянути в DevTools.
Приклад. Модель радить expect(page).toHaveText('Готово'). Виглядає розумно. Але за документацією Playwright toHaveText існує для локатора, не для page. Правильно — expect(page.locator('...')).toHaveText(...). Знайти це можна лише в доці, а не перепитуванням моделі.
Definition of Done для AI-виводу
Верифікація стає повторюваною, коли є чек-лист «готово». Для AI-згенерованого коду він не мʼякший за ваш звичайний DoD — він той самий:
- Компілюється і запускається без вигаданих API, опцій, імпортів.
- Селектори резолвляться в реальному DOM (див. DOM і селектори).
- Тест червоніє, коли фічу зламано (перевірено навмисною поломкою).
- Покрито не лише happy path: є негативні й межові перевірки з вимоги.
- Факти (версії, заголовки, коди, сигнатури) звірені з першоджерелом.
- Немає протікання секретів чи PII у код або в промпт.
- Відповідає конвенціям проєкту, а не абстрактному стилю з інтернету.
Якщо структурований вивід (JSON за схемою) генерується для інструментів — його валідують проти схеми програмно, не очима; про це докладніше в главі Tool use і function calling. А коли перевіряти доводиться масово й регулярно — це вже територія Evals та LLM-as-judge і Тестування AI-застосунків.
Відповідальність — твоя
Останнє й найважливіше. У коміті стоїть ваше імʼя, не імʼя моделі. «AI так написав» — не аргумент ні на код-рев'ю, ні на пост-мортемі після інциденту. Модель не несе відповідальності, не памʼятає контексту й не постане перед командою — усе це лягає на людину, яка натиснула «закомітити».
Найздоровіша ментальна модель: AI — це дуже швидкий джун, чию роботу ви апрувите своїм підписом. Швидкість він дає вам справжню; відповідальність за якість лишає вам повністю. Верифікація — це і є та точка, де ви цей підпис ставите свідомо.
Типові помилки
- Тест зелений — виглядає як робоче покриття, а насправді асерт відсутній або забутий
awaitпроковтнув помилку: тест не впаде ніколи. - Код компілюється — виглядає як коректний, а насправді кличе метод, що існує лише «у світі моделі» (
page.waitForText,cy.getByRole), і впаде на першому реальному виклику. - Модель упевнено назвала версію/заголовок/код — виглядає як факт, а насправді галюцинація, яку видно лише при звірці з документацією.
- «Перепитав модель — вона підтвердила» — виглядає як верифікація, а насправді те саме джерело з тим самим bias; нової інформації нуль.
- Селектор
getByRole('button', { name: '…' })— виглядає бездоганно, а насправді елемента з такою роллю чи назвою в DOM немає, бо модель не бачила сторінки. - AI покрив happy path — виглядає як готовий набір тестів, а насправді негативні й межові сценарії відсутні, і реальні баги пройдуть повз.
Підсумок
- Гладкість не дорівнює правильності: охайний, впевнений, скомпільований вивід нічого не гарантує.
- Зелений тест — не доказ; доказ — що тест червоніє, коли фіча ламається. Ламайте навмисне.
- Виконання ловить вигадані API й селектори, але не порожні перевірки — потрібні всі три опори: рев'ю, виконання, факти.
- Факти звіряй із першоджерелом, а не перепитуванням тієї ж моделі.
- DoD для AI-коду дорівнює твоєму звичайному DoD; відповідальність за вивід — на тобі, не на моделі.
Що питають на співбесіді
- «Як ви перевіряєте код, згенерований AI?» — інтервʼюер хоче почути, що зелений прогін вас не переконує: ви читаєте вивід критично, ламаєте тест навмисне, звіряєте API з документацією. Відповідь «запускаю, і якщо зелений — ок» — червоний прапорець.
- «AI написав тест, він проходить. Цього достатньо?» — пастка на розуміння різниці «проходить» vs «перевіряє». Сильна відповідь: ні, спершу переконаюся, що тест падає, коли фічу зламано.
- «Був випадок, коли AI вас підвів?» — поведінкове питання; готуйте конкретну історію: вигаданий метод, селектор під неіснуючий елемент, галюцинований заголовок — і як ви це впіймали.
- «Хто відповідає за баг у AI-згенерованому коді, що доїхав у прод?» — перевірка зрілості. Правильна рамка: той, хто закомітив; AI не суб'єкт відповідальності.
На що дивиться інтервʼюер: чи трактуєте ви AI-вивід як чернетку на рев'ю, а не як істину; чи усвідомлюєте упередження автоматизації; чи маєте конкретний, повторюваний спосіб перевірки, а не «на око».
Джерела
- ISTQB CTFL 4.0 Syllabus (PDF) — розділ 3 «Static Testing» (рев'ю як механізм верифікації; на нього спирається рев'ю AI-виводу).
- ISTQB Certified Tester AI Testing (CT-AI) — спеціалізована сертифікація з тестування систем із AI; дотична до систематичної перевірки AI-результатів.
- Playwright — Best Practices — офіційні рекомендації: чому web-first асерти й чому
waitForTimeout— антипатерн. - Playwright — Assertions — канонічний перелік реальних асертів; першоджерело для звірки «а такий метод узагалі існує?».
Чому «виглядає правильно» ще не означає «правильно», коли йдеться про AI-вивід?
Бо мовна модель оптимізована не під істину, а під правдоподібний наступний токен. Гладке форматування, впевнений тон і код, що компілюється, — це властивості форми відповіді, а не доказ її змісту. Проблема в тому, що всі ці зовнішні ознаки можна отримати, нічого корисного не перевіривши: тест зеленіє з порожнім тілом, код запускається з викликом неіснуючого методу, баг-репорт звучить професійно з вигаданим заголовком. Для QA це прямий конфлікт із фаховим рефлексом «не вірю, доки не перевірив»: тут перевіряти треба саме тому, що вивід переконливий. Практичний висновок — жоден із сигналів вигляду (охайність, компіляція, зелений прогін) не замінює власної верифікації.
Що таке галюцинація (hallucination) моделі й чому впевненість тону тут нічого не гарантує?
Галюцинація — це вигадка, подана з тією ж інтонацією, що й факт: модель називає неіснуючий метод, вигаданий заголовок чи хибний статус-код так само спокійно, як і правильні. Механізм у тому, що в моделі немає внутрішнього маркера «тут я не впевнена» — вона генерує і правду, і вигадку з одного розподілу ймовірностей, і рівень її «впевненості» жодним чином не корелює з коректністю. Тому спроба зчитати надійність відповіді з її тону — самообман. page.waitForText() вона напише рівно так само переконливо, як реальний page.waitForSelector(). Єдиний спосіб відділити факт від галюцинації — звірка із зовнішнім джерелом, а не оцінка того, наскільки впевнено це звучить.
Що таке упередження автоматизації (automation bias) і чому воно особливо небезпечне для тестувальника?
Це схильність людини довіряти підказці системи більше, ніж власній перевірці, — і що полірованіший вивід, то сильніше воно спрацьовує. Ти очікував робочий код, отримав щось дуже схоже на робочий код — і мозок радо ставить галочку, пропускаючи крок перевірки. Небезпека для QA у тому, що весь фах збудований на протилежному: сумніватися за замовчуванням. Automation bias тихо вимикає цей рефлекс саме там, де він найпотрібніший, — над згенерованим виводом, який виглядає готовим. Практичний наслідок: до AI-виводу треба свідомо застосовувати ту саму недовіру, що й до будь-якого непротестованого коду, а не знижувати планку через його охайність.
Які дефекти найчастіше зустрічаються в AI-згенерованому коді?
Їх чотири впізнаваних класи. Перший і найпідступніший — зелені тести, що нічого не перевіряють: асерт відсутній, забутий, або тавтологічний. Другий — вигадані API й селектори: модель компонує назви методів зі шматків, які бачила, і легко видає неіснуючі (page.waitForText, cy.getByRole), а локатори будує під розмітку, якої не бачила. Третій — поверхневі перевірки: покрито лише happy path, перевіряється присутність елемента замість коректності його поведінки. Четвертий — застарілі патерни: waitForTimeout замість web-first очікування, задеприкейчені виклики, стиль зі старих туторіалів, що осіли в тренувальних даних. Знати цей набір важливо, бо перевірка починається з розуміння, що саме тут зазвичай ламається.
Що таке «зелений тест, який нічого не перевіряє», і як він виникає?
Це тест, що стабільно проходить, піднімає метрику покриття, але не має реальної перевірки поведінки. Виникає він кількома шляхами: асерт просто забули дописати (тест доходить до кінця й завершується успіхом); асерт тавтологічний (expect(true).toBeTruthy() не впаде ніколи); або перед web-first асертом Playwright забули await, і проміс не дочекано — помилку проковтнуто, тест зелений. Підступність у тому, що зовні це виглядає як нормальне покриття, а насправді перевіряється порожнеча. Виявити такий тест виконанням не можна — він зелений завжди; ловиться він лише навмисною поломкою або уважним рев'ю.
Чому забутий await перед асертом Playwright робить перевірку фальшивою?
Web-first асерти Playwright (expect(locator).toBeVisible() тощо) асинхронні й повертають проміс. Якщо не поставити перед ними await, тест не дочекається результату перевірки: проміс лишається неопрацьованим, а можливе відхилення просто губиться, не валячи тест. Зовні асерт присутній, код читається як коректний — але фактично він не впливає на долю тесту. Наслідок: тест буде зеленим навіть тоді, коли реальна умова хибна. Саме тому забутий await — класичний спосіб отримати фальшиво-зелений тест, і саме тому перевірка через навмисну поломку його викриває: підмінене очікування все одно не завалить тест.
Чому виконання ловить вигадані API й селектори, а рев'ю очима — не завжди?
Бо вигаданий метод чи неіснуючий селектор проявляються в рантаймі миттєво: код або не компілюється, або падає на першому виклику, або локатор не резолвиться в живому DOM. Оком таку помилку легко проґавити — page.waitForText() виглядає абсолютно правдоподібно поруч зі справжніми методами, а getByRole('button', { name: 'Зберегти' }) синтаксично бездоганний, хоч елемента з такою роллю на сторінці може й не бути. Модель не бачила ні реальної бібліотеки в її поточній версії, ні твоєї сторінки — вона вгадала і те, і те. Тому найдешевша перевірка проти галюцинованих імен — просто запустити: секунда рантайму відсіює те, на що рев'ю могло б не спіткнутися.
На яких трьох опорах тримається верифікація AI-виводу?
На рев'ю, виконанні та звірці фактів — і жодна поодинці не достатня. Рев'ю — прочитати вивід критично, як пул-реквест, і спитати, що він НЕ ловить. Виконання — запустити й переконатися, що код живий, а тест реально червоніє, коли має. Звірка фактів — перевірити всі конкретні твердження (версії, заголовки, сигнатури) проти першоджерела. Кожна опора закриває сліпу зону іншої: рев'ю може не помітити вигаданого методу, який ловить виконання; виконання не викриває порожнього тесту, який видно навмисною поломкою; а факт про статус-код не перевірити ні тим, ні тим — лише документацією. Сильна відповідь на співбесіді якраз і показує, що кандидат застосовує всі три, а не покладається на одну.
Чим рев'ю AI-виводу відрізняється від рев'ю коду джуна?
За технікою — нічим, за налаштуванням скепсису — суттєво. Це та сама навичка статичного рев'ю: читаєш чужий код і питаєш, що з ним не так. Різниця в тому, що у джуна є контекст задачі й совість чесно сказати «тут я не впевнений», а в моделі немає ні того, ні іншого — вона однаково впевнено віддасть і правильне рішення, і повну вигадку. Тому AI-вивід треба ревʼювати ще прискіпливіше, ніж код живої людини. Практично це означає читати згенероване як чернетку на рев'ю, а не як готову відповідь, і не робити знижку на те, що воно охайно виглядає.
Яке головне питання треба поставити до згенерованого тесту під час рев'ю?
Головне — «а якщо фіча зламається, цей тест почервоніє?». Щоб відповісти, подумки «мутуй» застосунок: прибери кнопку, зверни валідацію, поверни неправильні дані — і перевір, чи впіймає це тест. Якщо ні — тест декоративний, він лише створює ілюзію покриття. Друге за важливістю питання — «чого цей тест НЕ ловить?»: чи є негативні сценарії, чи покрито межові значення, чи не підмінено перевірку значення перевіркою присутності. Ці два питання відділяють тест, що захищає поведінку, від тесту, що просто зеленіє.
Чому зелений прогін не доводить, що тест щось перевіряє?
Бо «проходить» і «перевіряє» — різні речі. Порожній тест без асерту, тест із забутим await чи з тавтологічним очікуванням зелений завжди — незалежно від того, працює фіча чи ні. Тобто зелений колір може означати як «поведінка коректна», так і «перевірки просто немає», і за самим фактом проходження ці два випадки не розрізнити. Ось чому виконання, хоч і ловить вигадані API, має принципову межу: воно не бачить порожнечі всередині тесту. Реальний доказ якості тесту — не те, що він проходить, а те, що він падає, коли має падати.
Що означає «зламати навмисне» і навіщо це роблять із AI-згенерованим тестом?
Це прийом перевірки самого тесту: тимчасово вносиш у систему завідомо неправильний стан і дивишся, чи тест на це відреагує. Практично — підміняєш очікуване значення на явно хибне (toHaveText('НЕПРАВИЛЬНО')), коментуєш фічу в застосунку або підмінюєш відповідь, і запускаєш. Якщо тест червоніє — асерт справді працює, тесту можна вірити. Якщо після поломки він усе одно зелений — перевірка мертва (забутий await, не той локатор, soft-режим без follow-up), і зеленому оригіналу гріш ціна. Це єдиний спосіб відрізнити тест, що захищає поведінку, від фальшиво-зеленого, бо звичайний прогін обидва показує однаково.
Модель назвала статус-код, версію бібліотеки й сигнатуру методу. Як це верифікувати?
Тільки звіркою із зовнішнім першоджерелом — офіційною документацією, специфікацією чи реальним кодом застосунку, — а не довірою до самої відповіді. Конкретні факти (номери версій, назви заголовків, статус-коди, сигнатури) — рівно та категорія, яку модель галюцинує найохочіше, бо тут немає простору для «приблизно правильно»: метод або існує, або ні. Механізм перевірки простий: відкрити доку інструмента, запустити код, глянути в DevTools — тобто дістати незалежне джерело істини. Важливо, що перевіряти треба навіть ті факти, які звучать абсолютно впевнено, — саме впевненість тут оманлива. Знайти помилку на кшталт «toHaveText існує для локатора, а не для page» можна лише в документації, не в самому діалозі з моделлю.
Чому не можна перевіряти факт, перепитавши ту саму модель «ти впевнений?»
Бо повторне питання не дає моделі нового джерела — лише той самий розподіл імовірностей і той самий bias. У відповідь вона з однаковою впевненістю або підтвердить вигадку, або розвернеться на 180° без жодної нової інформації, і обидва варіанти — шум, а не верифікація. Це створює ілюзію перевірки: діалог виглядає як діалог із експертом, який щойно передумав, хоча насправді нічого не звірялося. Реальна перевірка завжди приходить іззовні контексту моделі: документація, запуск коду, погляд у DevTools. Тому «перепитав — вона підтвердила» на співбесіді читається як нерозуміння того, звідки береться надійність відповіді.
Що входить у Definition of Done для AI-згенерованого коду?
Той самий DoD, що й для будь-якого свого коду, — його не пом'якшують через те, що написала машина. Ключові пункти: код компілюється й запускається без вигаданих API, опцій та імпортів; селектори резолвляться в реальному DOM; тест червоніє, коли фічу зламано (перевірено навмисною поломкою); покрито не лише happy path, а й негативні та межові сценарії з вимоги; усі факти звірені з першоджерелом; немає протікання секретів чи персональних даних у код або промпт; стиль відповідає конвенціям проєкту, а не абстрактному коду з інтернету. Сенс списку — зробити верифікацію повторюваною: замість «на око» є конкретний набір критеріїв, який проходить будь-який згенерований шматок перед комітом.
Як перевіряти структурований вивід моделі (наприклад, JSON за схемою)?
Програмно, валідацією проти схеми, а не очима. Коли модель генерує структуровані дані для інструмента (JSON під певний формат), візуальна перевірка ненадійна: пропущене поле чи неправильний тип легко проґавити в довгому об'єкті, а споживач цих даних впаде вже в проді. Тому такий вивід прогоняють через валідатор схеми, який детерміновано ловить невідповідності. Це частина ширшого принципу: усе, що можна перевірити автоматично й детерміновано, не варто перевіряти вручну. А коли перевіряти доводиться масово й регулярно — це вже задача для evals та підходу LLM-as-judge, а не для ручного рев'ю кожної відповіді.
Хто відповідає за баг в AI-згенерованому коді, що доїхав у прод?
Той, хто натиснув «закомітити», — людина, чиє ім'я стоїть у коміті, а не модель. «AI так написав» не працює як аргумент ні на код-рев'ю, ні на пост-мортемі: модель не суб'єкт відповідальності, вона не пам'ятає контексту й не постане перед командою. Уся відповідальність за якість лягає на того, хто вивід прийняв і підписав своїм комітом. Найздоровіша рамка тут — сприймати AI як дуже швидкого джуна: швидкість він дає справжню, але апрув його роботи — і наслідки цього апруву — повністю на тобі. Верифікація — це і є та точка, де ти цей підпис ставиш свідомо, а не автоматично.
Три кейси з життя QA, який щодня приймає AI-вивід: розбір фальшиво-зеленого тесту й перевірка його навмисною поломкою, полювання на вигаданий метод та селектор через виконання й документацію, і таблиця «який сигнал чого насправді вартий». Скрізь — що подивитися і чому.
Кейс 1. Фальшиво-зелений тест: прочитати, зламати, повірити
Ти попросив модель написати e2e-тест логіну. Вона віддала охайний код, він проходить у CI. Перш ніж мерджити — прочитай його як пул-реквест джуна, а не як готову відповідь.
import { test, expect } from '@playwright/test';
test('користувач логіниться', async ({ page }) => {
await page.goto('/login');
await page.fill('#email', 'user@example.com');
await page.fill('#password', 'secret');
await page.click('button[type=submit]');
expect(page.getByTestId('dashboard')).toBeVisible(); // ← пастка
});
Головне питання рев'ю: якщо логін зламається, цей тест почервоніє? Тут — ні. Перед expect(...).toBeVisible() забули await, тож web-first асерт повертає проміс, якого ніхто не дочекався: перевірка не встигає вплинути на долю тесту, і він зелений навіть тоді, коли дашборд не з'явився. Зовні асерт присутній, код читається коректно — але захисту немає.
Доказ здобувають не рев'ю, а навмисною поломкою. Тимчасово підмінюємо очікування на завідомо неправильне й запускаємо:
await expect(page.getByTestId('НЕ-ІСНУЄ')).toBeVisible(); // тест МУСИТЬ впасти
Що дивитися і чому:
- Тест лишився зеленим після підміни — асерт мертвий. Це остаточний доказ, що перевірка не працює: справжній асерт на неіснуючий локатор завалив би тест. Причина тут — забутий
await; той самий симптом дає soft-режим без follow-up або не той локатор. - Виправлення — повернути
awaitі перезапустити поломку. Правильний тест на підміні падає, на оригінальному значенні — проходить. Тільки після цих двох прогонів зеленому кольору можна вірити. - Зелений прогін сам собою нічого не довів. Порожній чи фальшивий тест зеленіє завжди; відрізнити його від справжнього можна лише поломкою, не спостереженням «ну він же проходить».
Кейс 2. Вигаданий метод і селектор: виконання плюс документація
Модель запропонувала «швидкий» варіант перевірки тексту й підказала локатор під кнопку. Виглядає бездоганно:
// порада моделі
await expect(page).toHaveText('Готово');
await page.getByRole('button', { name: 'Зберегти' }).click();
Обидва рядки — типова галюцинація в охайній обгортці, і ловляться вони різними опорами.
Перший рядок відсіює звірка з документацією. toHaveText існує для локатора, а не для об'єкта page — за офіційним переліком асертів Playwright такого поєднання немає. Перепитувати модель «ти впевнений?» тут марно: нового джерела вона не має й може з тією ж упевненістю підтвердити вигадку. Правильна форма береться з доки:
await expect(page.locator('.status')).toHaveText('Готово');
Другий рядок відсіює виконання. Синтаксично getByRole('button', { name: 'Зберегти' }) бездоганний, але модель не бачила твоєї сторінки — якщо в реальному DOM немає елемента з роллю button і такою назвою, локатор не знайде нічого й тест впаде на кліку. Секунда рантайму показує це швидше за будь-яке вчитування:
// перевірка гіпотези про селектор до того, як писати весь тест
const btn = page.getByRole('button', { name: 'Зберегти' });
console.log(await btn.count()); // 0 → ролі/назви в DOM немає, локатор вигаданий
Що дивитися і чому:
- Різні дефекти — різні опори. Неіснуючий метод ловить документація (рев'ю очима міг би проґавити), неіснуючий селектор — виконання в живому DOM. Одна опора не закриває обидва.
count()як дешевий зонд. Нуль елементів означає, що прив'язка вигадана; далі локатор звіряють із реальною розміткою, а не з тим, «як зазвичай називають кнопку».- Джерело істини — зовні моделі. І доку, і DOM дістаєш незалежно; повторний діалог із моделлю нового факту не приносить.
Кейс 3. Таблиця: який сигнал чого насправді вартий
Уся глава зводиться до одного розрізнення — правдоподібність проти правильності. Ось як типові «зелені» сигнали співвідносяться з тим, що вони реально доводять, і чим їх добивати.
| Сигнал від AI-виводу | Що здається | Що доведено насправді | Чим перевірити |
|---|---|---|---|
| Код компілюється | «Він коректний» | Лише що синтаксис валідний | Запустити; звірити API з докою |
| Тест зелений | «Фіча покрита» | Нічого — порожній тест теж зелений | Зламати навмисне: тест мусить почервоніти |
| Селектор виглядає правильно | «Знайде елемент» | Нічого — модель не бачила DOM | Виконати; count() у живій розмітці |
| Модель назвала версію/заголовок/код | «Це факт» | Нічого — улюблена зона галюцинацій | Першоджерело: доку, спека, DevTools |
| «Перепитав — підтвердила» | «Верифіковано» | Нічого — те саме джерело, той самий bias | Незалежне джерело поза моделлю |
| Покрито happy path | «Набір тестів готовий» | Лише позитивний сценарій | Дописати негативні й межові з вимоги |
Що дивитися і чому:
- Ліва колонка — це все ознаки форми. Кожен рядок «здається» — про вигляд, «насправді» — про зміст; збіг між ними не гарантований.
- Перевірка завжди в правій колонці. Вона або запускає код, або дістає зовнішнє джерело — тобто виходить за межі самого виводу; усе, що лишається всередині діалогу з моделлю, верифікацією не є.
- DoD для AI-коду = твій звичайний DoD. Компіляція без вигаданих API, резолвні селектори, червоніння на поломці, негативні й межові кейси, звірені факти, жодних секретів у коді чи промпті — і тільки тоді коміт із твоїм ім'ям.
Чому «виглядає правильно» — пастка
- Розумію, що модель оптимізована під правдоподібний токен, а не під істину, тож впевнений тон не корелює з коректністю.
- Можу пояснити, що охайність, компіляція і зелений прогін — сигнали правдоподібності, а не правильності, і кожен дається без реальної перевірки.
- Знаю, що таке галюцинація (hallucination): вигадка, подана з тією ж інтонацією, що й факт, без внутрішнього маркера «тут я не впевнена».
- Розумію упередження автоматизації (automation bias) і чому воно найнебезпечніше саме для QA — вимикає рефлекс «не вірю, доки не перевірив».
Типові дефекти AI-коду
- Знаю чотири класи дефектів: зелені тести без перевірки, вигадані API/селектори, поверхневі перевірки, застарілі патерни.
- Можу назвати три способи отримати фальшиво-зелений тест: відсутній асерт, тавтологія (
expect(true).toBeTruthy()), забутийawaitперед web-first асертом. - Можу пояснити, чому модель вигадує методи (
page.waitForText,cy.getByRole) і селектори під розмітку, якої не бачила. - Знаю різницю «перевірка присутності vs перевірка коректності»: що кнопка є — не те саме, що вона робить правильну дію.
Три опори верифікації
- Можу назвати три опори — рев'ю, виконання, звірка фактів — і пояснити, чому жодна поодинці не достатня.
- Розумію, що рев'ю AI-виводу — це статичне рев'ю з підвищеним скепсисом, бо в моделі немає ні контексту задачі, ні совісті сказати «не знаю».
- Знаю головне питання до тесту: «якщо фіча зламається, він почервоніє?» — і друге: «чого цей тест НЕ ловить?».
- Розумію межу виконання: воно ловить вигадані API й селектори, але зелений прогін не доводить, що тест щось перевіряє.
- Можу пояснити прийом «зламати навмисне»: підмінити очікування на хибне і впевнитися, що тест червоніє — доказ якості не в тому, що тест проходить, а в тому, що він падає, коли має.
Верифікація фактів
- Розумію, що конкретні факти (версії, заголовки, статус-коди, сигнатури) — те, що модель галюцинує найохочіше.
- Можу пояснити, чому факт звіряють із першоджерелом (доки, спека, код застосунку), а не з тією ж моделлю.
- Знаю, чому «перепитав модель — вона підтвердила» не є верифікацією: те саме джерело, той самий bias, нуль нової інформації.
DoD і відповідальність
- Можу перелічити DoD для AI-коду (той самий, що й для власного, не м'якший): компілюється без вигаданих API, селектори резолвляться, тест червоніє на поломці, покрито негативні й межові кейси, факти звірені, немає протікання секретів, стиль за конвенціями проєкту.
- Знаю, що структурований вивід (JSON за схемою) валідують програмно, а не очима.
- Можу чітко відповісти, хто відповідає за баг у AI-коді в проді: той, хто закомітив, а не модель.
- Тримаю в голові рамку «AI — швидкий джун, чию роботу апрувиш своїм підписом»: швидкість справжня, відповідальність за якість — на людині.
Що доводить той факт, що AI-згенерований код компілюється і зелено проходить у CI?
Питання
Правдоподібність проти правильності — у чому різниця для AI-виводу?