ИИ заменит QA-инженера? Мы дали ему 2000 наших тестов, чтобы проверить. ai-ассистент.. ai-ассистент. allure.. ai-ассистент. allure. cucumber.. ai-ассистент. allure. cucumber. moon.. ai-ассистент. allure. cucumber. moon. playwright.. ai-ассистент. allure. cucumber. moon. playwright. rest assured.. ai-ассистент. allure. cucumber. moon. playwright. rest assured. selenide.. ai-ассистент. allure. cucumber. moon. playwright. rest assured. selenide. автоматизация тестирования.. ai-ассистент. allure. cucumber. moon. playwright. rest assured. selenide. автоматизация тестирования. автотесты.. ai-ассистент. allure. cucumber. moon. playwright. rest assured. selenide. автоматизация тестирования. автотесты. ИИ в тестировании.
ИИ заменит QA-инженера? Мы дали ему 2000 наших тестов, чтобы проверить - 1

Автотесты писали, чтобы экономить время. Но в какой-то момент поняли, что тратим на их обслуживание больше, чем экономим. 

Тест упал в CI — открываешь Allure, идёшь на стенд через VPN, авторизуешься, лезешь в DOM, ищешь, какой локатор отвалился. Полчаса-час на один тест. Прошёл релиз, и ещё пара дней уходит на то, чтобы починить локаторы после того, как фронтенд поменял вёрстку. А когда с утра видишь 200 красных тестов при нетронутом коде, начинается расследование. 

В этот момент хочется отдать всё это AI-ассистенту и заняться делом. Мы так и сделали, но панацеи не вышло. ИИ снимает часть рутины и экономит часы, но на сложных кейсах он ошибается, ходит по кругу и с уверенным видом выдаёт несуществующие локаторы за настоящие. 

Всем привет! На связи Егор Лаптев — QA Fullstack Java в SENSE на проекте крупного российского банка.

В статье рассказываю, где ассистент помогает, а где создаёт больше проблем. Показываю, как он устроен внутри и разбираю шесть рабочих сценариев с кодом. Отдельно выделил ограничения и стоимость нововведений. 

В примерах стек Selenide, Cucumber, REST Assured, Allure, Kafka и Moon в Kubernetes, но сами принципы переносятся на любую связку UI- и API-тестов.

С чем мы жили каждый день

В нашем проекте более 2000 автотестов: UI-сценарии на Selenide и Cucumber, API-тесты на REST Assured, интеграционные проверки с Kafka, инфраструктурный мониторинг. Всё это вшито в процессы команды: каждый прогон CI — это сотни сценариев, а каждый релиз — актуализация десятков тестов.

Боль 1: Упавший тест — это 30–60 минут рутины. Открыть Allure. Найти ошибку. Зайти на стенд через VPN. Авторизоваться. Найти страницу. Проверить DOM. Понять, что локатор сломался. Исправить. Запустить. Упало по-другому. Снова исправить. Снова запустить. Прошло. Следующий тест.

Боль 2: Релиз — это несколько дней актуализации. Фронтенд изменил data-test-id на 15 элементах. Нужно найти все 15, проверить DOM на стенде, обновить локаторы, запустить тесты. На каждый элемент — 10–15 минут. 15 элементов — 3–4 часа. А если ещё и вёрстка перестроилась — XPath тоже нужно переписывать.

Боль 3: Массовые падения — и непонятно почему. Утром пришёл — 200 тестов красных. Код не менялся. Что случилось? Moon лежит? DNS упал? Стенд перезапустили? Данные протухли? Нужно последовательно проверять каждую гипотезу — 20–30 минут на диагноз.

Боль 4: Написание нового теста — 2–3 часа на один сценарий. Прочитать тикет. Понять бизнес-логику. Написать feature-файл. Найти или создать Page Object. Написать шаги. Запустить. Упало — поправить локатор. Запустить. Упало — добавить wait. Запустить. Прошло.

Боль 5: Отладка iframe и перекрытых элементов — 1–2 часа на один кейс. Элемент «как бы есть» в DOM, но Selenide его не находит. Почему? iframe? Перекрытие? Не загрузился? Нет прав? Каждая гипотеза — 15–20 минут проверки на стенде.

ИИ берёт на себя рутину в этих пяти сценариях. Дальше разбираю подробнее каждый. 

ИИ заменит QA-инженера? Мы дали ему 2000 наших тестов, чтобы проверить - 2

Архитектура: Supervisor + Workers + Skills

Ассистент работает по модели Supervisor-Worker с системой Skills:

  • Supervisor — оркестратор. Читает контекст, определяет тип задачи, делегирует работу конкретному воркеру

  • Workers — исполнители. Имеют доступ к файловой системе, терминалу, Allure-отчётам, Bitbucket, Confluence, Jira, TestOps

  • Skills — набор инструкций, которые определяют как делать задачи. Не общие знания, а конкретные протоколы под наш проект

Ключевое ограничение: Workers не видят историю чата. Supervisor обязан передавать весь контекст в каждом вызове. Пропущенная деталь — воркер работает без нужной информации.

Критическое правило инъекции: Supervisor обязан передавать содержимое релевантных Skills в каждом вызове воркеру. Не передал — воркер работает вслепую.

Практические сценарии

Процесс 1. Починка упавших UI-тестов

Задача: Тест упал с NoSuchElementException. Нужно понять причину и исправить.

Что делает ИИ:

  1. Определяет тип ошибки по стектрейсу

  2. Находит локатор в Page Object по аннотации @ElementName

  3. Проверяет реальный DOM через playwright-cli

  4. Сравнивает ожидаемый и реальный DOM, исправляет локатор

  5. Запускает тест и проверяет результат

Пример кода — Page Object с аннотациями:

@PageName("Карточка организации")
public class CompanyCardPage {
    @ElementName("Пользователи и роли")
    public SelenideElement usersAndRolesTile = $(byAttribute("data-test-id", "nibAdminViewButton"));

    @ElementName("Модальное окно Пользователи и роли")
    public SelenideElement usersAndRolesModal = $(byAttribute("data-test-id", "nibAdminModal"));
}

Пример кода — Step, ищущий элемент по имени:

@Когда("кликаем на блок-кнопку {string}")
public void кликаемНаБлокКнопку(String buttonName) {
    SelenideElement element = pageObjectFinder
        .getSelenideElementByName(buttonName, pageContext.getCurrentPage());
    element.shouldBe(Condition.visible).click();
}

Пример кода — проверка реального DOM через playwright-cli:

# Открываем браузер
playwright-cli open --headed about:blank

# Обход SSL + авторизация через Keycloak SSO
playwright-cli run-code --filename /tmp/pw_ssl_bypass.js

async () => {
  const browser = page.context().browser();
  const ctx = await browser.newContext({ignoreHTTPSErrors: true});
  const p = await ctx.newPage();
  await p.goto('https://app-int.moscow.internal.corp/...');
  await p.waitForSelector('#username', {timeout: 10000});
  await p.fill('#username', 'LOGIN');
  await p.fill('#password', 'PASSWORD');
  await p.click('#kc-login');
  await p.waitForLoadState('networkidle', {timeout: 15000});
  return p.url();
}

// Извлекаем все data-test-id из DOM
async () => {
  const result = await targetPage.evaluate(() => {
    const elements = [];
    document.querySelectorAll('[data-test-id]').forEach(el => {
      elements.push({
        tag: el.tagName,
        dataTestId: el.getAttribute('data-test-id'),
        text: el.textContent.substring(0, 200).trim()
      });
    });
    return JSON.stringify(elements, null, 2);
  });
  return result;
}

Приоритет локаторов при исправлении:

Приоритет

Тип локатора

Почему

1

@data-test-id

Стабильный, не зависит от вёрстки

2

XPath с contains()

По тексту или стабильному атрибуту

3

CSS-классы через contains()

CSS-модули генерируют хеш-суффиксы

Типичные проблемы и решения:

Проблема

Решение

/span не находит

Заменить на //span (не прямой потомок)

CSS-класс с хеш-суффиксом

contains(@class, ‘base-name’)

Элемент не кликается

click(ClickOptions.usingJavaScript())

Модалка: несколько совпадений

Привязать к @data-test-id конкретной модалки

Запуск и проверка:

./gradlew uiParallel -Dcucumber.filter.tags="@TICKET-3146 and not @Ignore"

Отладка сложных UI-флоу. Обычный сценарий: тест падает, инженер открывает стенд, видит, что элемент присутствует в DOM, но не кликается — и начинает разбираться. ИИ систематизирует этот процесс:

iframe (подсистемы внутри основного приложения). Фронтенд рендерит часть интерфейса в iframe — например, виджет из Подсистемы А внутри основного портала. Selenide по умолчанию ищет элементы в основном контексте, а элемент — в iframe. ИИI через playwright-cli переключает контекст, проверяет DOM внутри iframe и предлагает switchTo().frame() перед взаимодействием.

Перекрытые элементы. Элемент виден в DOM, но перекрыт другим — модалкой, тултипом, sticky-хедером. ИИ пробует разные стратегии клика:

  1. click() — стандартный клик → падает, ElementClickInterceptedException

  2. click(ClickOptions.usingJavaScript()) — JS-клик → может уйти не туда

  3. scrollIntoView(true).click() — скролл + клик → если элемент за viewport

  4. hover().click() — hover для убирания тултипа → иногда помогает

Если ни одна стратегия не работает — ИИ эскалирует на инженера. Проблема может быть не в локаторе, а в том, что модалка не закрылась из-за бага в приложении.

Race conditions. Тест падает через раз: иногда элемент успевает появиться, иногда нет. ИИ анализирует тайминги в Allure-отчёте — сколько шаг выполнялся, когда появился timeout — и предлагает wait-стратегии: shouldBe(visible) вместо shouldHave(text(…)), явные WaitFor с нужным условием, увеличение таймаута для конкретного шага.

Ограничение: ИИ не понимает ПОЧЕМУ элемент не появился. Он видит следствие — NoSuchElementException, ElementClickInterceptedException, timeout — но не причину. Причина может быть в бизнес-логике (роль не даёт доступ), в баге приложения (сервер вернул 500, данные не загрузились), в инфраструктуре (Moon-под завис, браузер не отвечает). AI видит только код и DOM, а не бизнес-контекст. Отладка — это партнёрство: ИИ работает с DOM, инженер принимает решение.

Процесс 2. Написание новых тестов

Задача: Написать новый автотест по описанию или тикету.

Что делает ИИ:

  1. Читает тикет из Jira / описание задачи

  2. Пишет feature-файл (Gherkin на русском)

  3. Создаёт или дополняет Page Object

  4. Пишет Step Definition

  5. Запускает тест и проверяет

Пример кода — Feature-файл:

@CompanyCardGeneralInfo
Функционал: Вкладка Общая информация карточки организации

  Предыстория: Пользователь входит на портал
    * переход на страницу "Страница входа" по ссылке "loginPage"
    * Пользователь с ролью "Role_DepartmentHead" авторизуется через логин
    * загрузка страницы "Поиск организаций"

  Сценарий: Отображение плиток при ролевой модели Подсистемы А
    * заполнено поле "Поиск компаний" со значением "тестовая компания"
    * нажата кнопка "Найти"
    * отображаются результаты поиска, содержащие компанию с названием "Тестовая организация 1"
    Тогда тег "Подсистема А" должен быть активен
    И отображается блок кнопка "Пользователи и роли"
    И элемент "Руководители" скрыт

Пример кода — Page Object:

@PageName("Карточка организации")
public class CompanyCardPage {
    @ElementName("ИНН")
    public SelenideElement inn = $(byXpath(
        "//div[contains(@class, 'main-info-header_row')]" +
        "//span[contains(text(), 'ИНН:')]/following-sibling::span"
    ));

    @ElementName("Пользователи и роли")
    public SelenideElement usersAndRolesTile = $(
        byAttribute("data-test-id", "nibAdminViewButton")
    );
}

Никаких @FindBy — только $, $x, $$, $$x. Ленивая инициализация Selenide.

Пример кода — Step Definition с PicoContainer DI:

public class CompanyCardStep {
    private final PageObjectFinder pageObjectFinder;
    private final PageContext pageContext;

    public CompanyCardStep(PageObjectFinder pageObjectFinder, PageContext pageContext) {
        this.pageObjectFinder = pageObjectFinder;
        this.pageContext = pageContext;
    }

    @Когда("в хедере карточки компании отображаются нередактируемые поля:")
    public void вХедереОтображаютсяПоля(DataTable dataTable) {
        List<Map<String, String>> data = dataTable.asMaps(String.class, String.class);
        for (Map<String, String> row : data) {
            SelenideElement element = pageObjectFinder
                .getSelenideElementByName(row.get("Поле"), pageContext.getCurrentPage());
            element.shouldBe(Condition.visible);
        }
    }
}

Процесс 3. Анализ Allure-отчётов и умный реран

Задача: После прогона часть тестов упала по флаковым причинам. Нужно определить, какие стоит перезапустить, и сделать это автоматически.

Что делает ИИ:

  1. Парсит JSON-результаты Allure из build/allure-results/

  2. Разделяет упавшие тесты на параллельные (независимые) и процессные (с тегом @Sequential)

  3. Генерирует rerun-parallel.txt и rerun-sequential.txt

  4. Запускает только упавшие тесты через Gradle-таски

Пример кода — Gradle-таска рерана:

tasks.register('rerunParallel', Test) {
    filter { includeTestsMatching("ru.company.runner.UITestRunner") }
    ignoreFailures = true
    onlyIf {
        def rerunFile = file("${layout.buildDirectory.get()}/rerun-parallel.txt")
        rerunFile.exists() && !rerunFile.text.trim().isEmpty()
    }
    doFirst {
        def rerunPaths = file("${layout.buildDirectory.get()}/rerun-parallel.txt").text.trim()
        systemProperty "cucumber.features", rerunPaths.replaceAll('\n', ',')
    }
}

Ключевой нюанс: cucumber.features полностью переопределяет @SelectClasspathResource в Suite-раннере. Без onlyIf при пустом файле запустятся ВСЕ тесты.

Процесс 4. Диагностика Moon-кластера

Задача: UI-тесты не запускаются. Нужно понять, жив ли Moon-кластер (Aerokube) в Kubernetes.

Что делает ИИ:

  1. Проверяет доступность Moon через /wd/hub/status

  2. Пытается создать тестовую сессию Chrome

  3. Пробует навигацию на целевой URL

  4. Если навигация не работает — проверяет DNS внутри K8s

Пример кода — диагностика через curl:

# Проверка доступности
curl -sk http://moon.test-cluster.internal.corp/wd/hub/status

# Создание тестовой сессии Chrome
curl -sk -X POST http://moon.test-cluster.internal.corp/wd/hub/session 
  -H 'Content-Type: application/json' 
  -d '{"capabilities":{"alwaysMatch":{' +
       '"browserName":"chrome","browserVersion":"126.0.6478.126-6",' +
       '"acceptInsecureCerts":true}}}'

# Навигация на целевой URL
curl -sk --max-time 30 -X POST 
  "http://moon.test-cluster.internal.corp/wd/hub/session/{SESSION_ID}/url" 
  -d '{"url":"https://app-int.moscow.internal.corp/"}'

Процесс 5. Kafka в автотестах

Задача: Проверить, что сервис отправил сообщение в Kafka-топик после выполнения бизнес-операции.

Что делает ИИ:

  1. Подключается к Kafka-топику с SASL/SCRAM-аутентификацией

  2. Читает сообщения с нужным offset

  3. Проверяет содержимое сообщения

Пример кода — Cucumber-шаги для Kafka:

@Дано("подключение к Kafka топику {string}")
public void subscribeToTopic(String topic) {
    KafkaConsumerClient consumer = getConsumer();
    consumer.subscribe(topic);
}

@Когда("прочитаны сообщения из Kafka топика в течение {int} секунд")
public void pollMessages(int timeoutSeconds) {
    List<ConsumerRecord<String, String>> messages = consumer.pollMessages(timeoutSeconds);
    ScenarioContext.getInstance().setData("kafkaMessages", messages);
}

@Тогда("сообщение из Kafka топика содержит текст {string}")
public void verifyMessageContainsText(String expectedText) {
    List<ConsumerRecord<String, String>> messages =
        ScenarioContext.getInstance().getData("kafkaMessages");
    boolean found = messages.stream()
        .anyMatch(record -> record.value().contains(expectedText));
    assertTrue(found, "Сообщение с текстом '" + expectedText + "' не найдено");
}

Два режима: subscribe (consumer group) и assign (seekToBeginning, без влияния на offset).

Процесс 6. Выявление инфраструктурных проблем

Задача: Тесты падают массово, но код правильный. Нужно понять, что сломалось в инфраструктуре.

Что делает ИИI:

  1. Проверяет доступность Moon-кластера

  2. Проверяет создание сессии и навигацию

  3. Если навигация не работает — проверяет DNS

  4. Если DNS не резолвится — проверяет CoreDNS в K8s

  5. Формирует диагноз и передаёт инфраструктурной команде

Типичные инфраструктурные проблемы:

  • Moon недоступен — все тесты падают с Connection Refused

  • DNS не резолвится — браузер открывается, но навигация зависает, ERR_NAME_NOT_RESOLVED

  • Egress заблокирован — сетевые политики K8s блокируют исходящий трафик, страница без стилей

  • Сессии зависают — поды не освобождаются, новые тесты встают в очередь и падают по таймауту

  • БД недоступна — API-тесты падают с 500, в логах ConnectionPoolTimeoutException

Пример кода — цепочка диагностики:

# Шаг 1: Moon жив?
curl -sk http://moon.test-cluster.internal.corp/wd/hub/status
# → 200 OK

# Шаг 2: Можем создать сессию?
curl -sk -X POST http://moon.test-cluster.internal.corp/wd/hub/session ...
# → Сессия создана

# Шаг 3: Навигация работает?
curl -sk --max-time 30 -X POST ".../url" -d '{"url":"https://app-int.moscow.internal.corp/"}'
# → Timeout!

# Шаг 4: DNS резолвится?
nslookup app-int.moscow.internal.corp
# → NXDOMAIN

# Шаг 5: CoreDNS
kubectl get pods -n kube-system -l k8s-app=kube-dns
# → CoreDNS pod CrashLoopBackOff

Весь диагноз за 3 минуты вместо 20–30 минут ручного разбора. ИИ не может починить CoreDNS — только поставить диагноз.

Система Skills: как мы управляем знаниями

Skills — это это конкретные протоколы под конкретные задачи:

Skill

Когда применять

quickfix

Упал UI-тест — починить

new-test

Написать новый тест

dom-tree-analysis

Исследовать DOM через playwright-cli

cucumber-rerun

Реализовать умный реран

moon-cluster-analysis

Диагностика Moon

kafka-consumer

Работа с Kafka в тестах

infra-diagnostics

Диагностика инфраструктурных проблем

no-analysis-scripts

НЕ создавать мусорные файлы в проекте

Правило маршрутизации: читать минимум Skills, достаточный для задачи. Не читать всё подряд.

Ограничения и проблемы

Галлюцинации

ИИ заменит QA-инженера? Мы дали ему 2000 наших тестов, чтобы проверить - 3

Проблема: ИИ генерирует несуществующие Cucumber-шаги и data-test-id.

Пример 1 — несуществующие шаги. ИИ «придумывает» шаг, которого нет в коде. Feature-файл выглядит логично, синтаксис Gherkin правильный — но при запуске UndefinedStepException. Какой именно шаг не существует — выясняется только после компиляции.

Пример 2 — придуманные data-test-id. ИИ анализирует паттерны именования в проекте и генерирует правдоподобный идентификатор. Выглядит как настоящий, но при запуске — NoSuchElementException.

Пример 3 — NBSP в тексте. ИИ видит в DOM <span>Руководители</span> и предлагает:

$(byXpath("//span[text()='Руководители']"))

Но реальный текст содержит NBSP: “Руководителиxa0”. В 9 из 10 случаев работает, на конкретном стенде — нет.

Пример 4 — CSS-хеш-суффиксы. ИИ видит <div class=”modal_header__a3b2c1″> и предлагает $(byClassName(“modal_header__a3b2c1”)). Завтра фронтенд пересоберётся, хеш изменится — тест падает. Правильный вариант:

$(byXpath("//div[contains(@class, 'modal_header')]"))

Последствие: UndefinedStepException или NoSuchElementException при запуске. Инженер тратит время на поиск несуществующего шага в кодовой базе.

Решение:

  • Перед добавлением нового Cucumber-шага ИИ обязан найти все существующие шаги через поиск по репозиторию. Если подходящий шаг существует — использовать его, а не создавать новый.

  • Перед предложением локатора ИИ обязан проверить реальный DOM через playwright-cli. Придуманные data-test-id запрещены.

Зацикливание

Проблема: ИИ зацикливается на исправлении, меняя локатор туда-сюда без результата.

Пример — перекрытый элемент:

Тест падает (NoSuchElementException)
  → AI меняет локатор: //span[text()='X']
  → Тест падает (элемент перекрыт)
  → AI меняет локатор: //span[contains(text(),'X')]
  → Тест падает (та же причина)
  → AI возвращает оригинальный локатор
  → Цикл повторяется

AI пробует стратегии клика:

  1. click() → падает, элемент перекрыт

  2. click(ClickOptions.usingJavaScript()) → падает, клик уходит не туда

  3. scrollIntoView(true).click() → падает, элемент за пределами viewport

  4. hover().click() → падает, hover не помогает

  5. Меняет локатор на другой → падает, потому что проблема не в локаторе

5 итераций без результата.

Другой сценарий: тест падает из-за таймаута Moon. ИИ думает, что проблема в локаторе, и начинает его менять. //span[text()=’X’] → //span[contains(text(),’X’)] → //div//span[text()=’X’] → добавляет shouldBe(visible) вместо shouldHave(text(…)) — не помогает. Проблема в том, что Moon-под завис и браузер не отвечает. ИИ видит только код, а не метрики кластера.

Последствие: 3–5 итераций без результата, потерянное время на запуск тестов.

Решение: лимит 3 итерации, после — эскалация на инженера с описанием всех попыток.

Невозможность определить причину

ИИ заменит QA-инженера? Мы дали ему 2000 наших тестов, чтобы проверить - 4

Проблема: NoSuchElementException имеет 5+ разных причин, ИИ не может отличить их без анализа DOM.

Пример: тест падает на шаге «отображается блок кнопка Пользователи и роли». ИИ видит NoSuchElementException и начинает менять локатор. Но проблема в том, что пользователь авторизовался под ролью, у которой нет доступа к Подсистеме А — и плитка просто не рендерится. ИИ не знает бизнес-логику, он видит только «элемент не найден».

Возможные причины NoSuchElementException:

  • Элемент убрали из интерфейса → переписать тест

  • Элемент переехал в другой контейнер → обновить локатор

  • Элемент не загрузился из-за таймаута → добавить wait

  • Элемент скрыт из-за прав пользователя → проверить роль

  • Элемент рендерится в iframe → переключить контекст

Последствие: ИИ тратит 3–5 минут на анализ DOM, инженер с опытом видит паттерн за 30 секунд.

Решение: если после анализа DOM причина неясна — ИИ честно пишет «не могу определить причину, требуется инженер» вместо угадывания.

Частичные решения

Проблема: ИИ может починить 8 из 10 упавших тестов, а 2 останутся — и для них нужен инженер. Эти 2 — обычно самые сложные: race condition, неочевидная зависимость между шагами, баг в самом приложении.

ИИ может написать 90% нового теста, но последние 10% — только человек. Сложная бизнес-логика: почему при роли Role1 видна плитка «Пользователи и роли», а при роли Role2 — нет. ИИ видит DOM, но не знает почему.

Хитрые wait-стратегии — тоже проблема. Нужно дождаться не просто появления элемента, а завершения асинхронной операции. ИИ ставит shouldBe(visible) — элемент появился, но данные ещё пустые. Тест проходит иногда, иногда нет. Флак.

Потеря контекста

Проблема: Workers не видят историю чата. Если Supervisor не передал критическую деталь — воркер работает без контекста.

Пример: инженер сказал «не меняй локатор для модалки, он правильный, проблема в таймауте». Supervisor не передал это воркеру — воркер поменял локатор. По протоколу: элемент не найден → проверить DOM → обновить локатор. Воркер действовал по протоколу, но без контекста — сломал то, что не нужно было трогать.

Чем длиннее сессия — тем больше контекста теряется. Supervisor пытается сжимать историю, но иногда выкидывает критические детали.

Последствие: воркер выполняет правильный протокол, но без нужных инструкций — ломает то, что не нужно было трогать.

Решение: протокол инъекции — Supervisor обязан передавать содержимое релевантных Skills в каждом вызове воркеру. Пропущенный пункт — воркер работает без нужных инструкций.

Сколько времени экономит ИИ-ассистент

ИИ заменит QA-инженера? Мы дали ему 2000 наших тестов, чтобы проверить - 5

Ассистент не заменяет инженера, но оптимизирует рутину: диагностика, локаторы, реран, инфраструктурные проверки. Ниже сводная таблица, которая поможет наглядно оценить экономию времени и понять ограничения:

Процесс / Задача

Вручную (Было)

С ИИ-ассистентом (Стало)

Ограничения / Нужен человек

Диагностика UI-теста

30–60 минут

5–10 минут

В 20% случаев (сложная бизнес-логика)

Написание нового теста

2–3 часа

30–40 минут

ИИ пишет ~90% кода, финал за человеком

Актуализация при релизе

Несколько дней

Максимум 1 день

Требуется контроль контекста

Инфраструктурный анализ

20–30 минут

3–5 минут

ИИ только диагностирует, но не чинит K8s

Реран упавших тестов

Ручной разбор Allure

Автоматический анализ + перезапуск

Ложные срабатывания ~5%

Отладка сложных флоу (iframe, перекрытия, race conditions)

1–2 часа

15–30 минут

ИИ не понимает бизнес-контекст, только DOM

Ускорение производительности инженера

30–40% времени на рутину

Рутина автоматизирована, фокус на архитектуре

ИИ не принимает архитектурные решения

Актуализация DOM-карты при изменении фронтенда

Полный аудит вручную 4–8 часов

Автоматический обход через playwright-cli за 30–60 минут

Требуется верификация человеком

Поиск корневой причины массовых падений

1–3 часа

5–15 минут

ИИ ставит диагноз, но не чинит инфраструктуру

Сколько это стоит: считаем ресурсы

ИИ заменит QA-инженера? Мы дали ему 2000 наших тестов, чтобы проверить - 6

Возьмём команду из 8 человек. Средний запрос к ассистенту около 2 000 токенов (вход плюс выход). За рабочий день набегает примерно 200 запросов, это около 400 000 токенов. За месяц (22 рабочих дня) — порядка 8,8 млн токенов.

При цене ~$5 за миллион токенов это выходит около $44 в месяц. Немного. Но это уже оптимизированный режим — экономию потребления мы выстраивали семь месяцев. Без оптимизации каждый запрос использует полный контекст и требует около 15 000 токенов. Тот же месяц превращается в ~66 млн токенов, а счёт — примерно в $330 в месяц. Разница почти в 7 раз.

Когда ассистентом пользуется весь отдел тестирования, цифры растут:

Параметр

1 команда

10 команд

50 команд

Токенов/мес

~8,8 млн

~88 млн

~440 млн

Стоимость/мес

~$44

~$440

~$2 200

Стоимость/год

~$528

~$5 280

~$26 400

Без оптимизации/год

~$3 960

~$39 600

~$198 000

И это ещё оптимистичная картина. В неё не заложены пиковые нагрузки при релизах (потребление ×3–5 к базовому), стоимость GPU-инфраструктуры под LLM, затраты на адаптацию под каждый проект и лицензии на IDE-плагины.

Почему одних денег может не хватить

ИИ заменит QA-инженера? Мы дали ему 2000 наших тестов, чтобы проверить - 7

Даже с бюджетом упереться можно в физику инфраструктуры:

— GPU в дефиците. LLM требуют A100/H100, очередь на такие карты — месяцы. Закупишь под пик — переплата, под среднее — не хватит в часы релизов.

— Пропускная способность ограничена. При 200+ одновременных сессиях сервис начинает троттлить: время ответа растёт с 5 секунд до 30+, и автотесты сыплются по таймаутам.

— Контекстное окно — бутылочное горлышко. У каждого проекта свой контекст, а 2 000+ тестов — это огромная кодовая база. Втиснуть её в окно целиком нельзя, резать — терять качество, а больше контекста означает дороже запрос.

— Оптимизация не бесплатна. Те самые семь месяцев и 80% потребления придётся повторять для каждого нового проекта. Без этого — потребление и стоимость в 5 раз выше.

— Скрытые расходы. Дообучение, промпт-инженерия, поддержка Skills, мониторинг качества ответов — это не разовые затраты, а постоянный FTE.

Вывод

ИИ-ассистент в тестировании — это инфраструктурный проект со своей стоимостью владения. Для одной команды это $528–3 960 в год, в зависимости от того, оптимизировали вы потребление или нет, — терпимо в обоих случаях. Для 50 команд вилка уже $26K–198K, и это предмет для разговора. 

Экономия времени реальна. Но вопрос в том, кто за неё платит и сколько. На масштабе банка ИИ в тестировании — это больше про перераспределение затрат: вместо часов инженеров — часы GPU-кластеров.

P.S. А вы считали полную стоимость владения ИИ на своём проекте — не только токены, но и GPU-инфраструктуру, адаптацию и поддержку? Или пока платите только за токены?

Автор: Egor1301

Источник