vyvchy
    Теми розділу

    14 · AI для QA

    Верифікація результатів 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 згенерував

    Рев'ю: прочитати критично

    Виконання: запустити й зламати навмисне

    Факти: звірити з першоджерелом

    Проходить DoD?

    Твій коміт, твоя відповідальність

    Ні

    Так

    AI згенерував

    Рев'ю: прочитати критично

    Виконання: запустити й зламати навмисне

    Факти: звірити з першоджерелом

    Проходить DoD?

    Твій коміт, твоя відповідальність

    Рев'ю виводу як навичка

    Читайте 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 — канонічний перелік реальних асертів; першоджерело для звірки «а такий метод узагалі існує?».