Tutorials

Продвинутые паттерны Puppeteer + CaptchaAI для собственных QA-сюит

Главная причина, по которой QA-сюита на Puppeteer с CAPTCHA-проверками начинает тормозить по мере роста числа сценариев, — не решение CAPTCHA само по себе, а то, как вокруг него организован браузер. Если каждый сценарий поднимает новый инстанс Chromium, а токен CaptchaAI не переиспользуется между попытками внутри одного прогона, CI растягивается на десятки минут вместо нескольких. Ниже — паттерны, которые снимают эту нагрузку: один браузер на множество контекстов, параллелизм с явным лимитом, ретраи с задержкой и кеш токена на уровне сценария.

Область применения: паттерны ниже предназначены для собственных или явно авторизованных QA-, staging- и production-сред. Здесь нет обхода CAPTCHA на чужих сайтах — только диагностика, тестирование и наблюдаемость собственной интеграции CaptchaAI.

Один браузер на все Puppeteer-контексты

Поднимать новый браузер под каждый сценарий — самый частый источник медленных прогонов. browser.createIncognitoBrowserContext() даёт изолированный контекст (свои куки, свой localStorage) внутри одного и того же процесса Chromium, поэтому запуск браузера происходит один раз за весь прогон:

import puppeteer from 'puppeteer';

const browser = await puppeteer.launch({ headless: 'new' });

async function withContext(fn) {
  const ctx = await browser.createIncognitoBrowserContext();
  const page = await ctx.newPage();
  try { return await fn(page); }
  finally { await ctx.close(); }
}

finally { await ctx.close(); } — не опция, а обязательное условие: без явного закрытия контексты копятся в памяти, и через 30–40 сценариев CI-раннер начинает падать по OOM.

Лимит параллелизма для CaptchaAI-запросов

Сценарии стоит параллелить с явным лимитом, а не «сколько потянет раннер»: неограниченный Promise.all по всем сценариям сразу упирается либо в память браузера, либо в очередь CaptchaAI.

import pLimit from 'p-limit';

const limit = pLimit(4);
await Promise.all(scenarios.map(s => limit(() => withContext(p => runScenario(p, s)))));

Если каждый параллельный контекст держит в моменте одну задачу CaptchaAI, для pLimit(4) из примера выше с запасом хватает плана STANDARD ($30/мес, 15 потоков). Для nightly-сюиты на 20+ параллельных контекстов логичнее сразу смотреть на ADVANCE ($90/мес, 50 потоков) — нужное число потоков считается напрямую от максимального числа одновременных сценариев.

Ретраи с экспоненциальной задержкой

При достаточном объёме прогонов сетевые сбои и временные ошибки CaptchaAI — норма, а не исключение. Один ретрай с фиксированной паузой почти всегда недостаточен под нагрузкой:

async function withRetry(fn, attempts = 3) {
  let last;
  for (let i = 0; i < attempts; i++) {
    try { return await fn(); }
    catch (err) { last = err; await new Promise(r => setTimeout(r, 1000 * (i + 1))); }
  }
  throw last;
}

Экспоненциальная задержка (1000 * (i + 1), то есть 1 с, 2 с, 3 с) не долбит API одинаковыми интервалами и даёт временному сбою шанс устраниться самому, прежде чем сценарий будет считаться упавшим.

Кеш CAPTCHA-токена на уровне сценария

Кешируйте токен внутри одного сценария, но не между разными сценариями или контекстами: токен привязан к конкретной странице и её sitekey, поэтому переиспользование между несвязанными прогонами приведёт не к экономии, а к ошибкам верификации на бэкенде.

Реально снижает нагрузку на CaptchaAI другое — внутренние ретраи одного и того же сценария. Если шаг формы падает по сети уже после того, как CAPTCHA решена, повторно дёргать CaptchaAI не нужно: токен ещё жив в рамках текущего прогона. Как только сценарий завершился — успешно или с ошибкой, — кеш сбрасывается вместе с контекстом.

Логи и наблюдаемость QA-сюиты

Структурированные логи помогают сравнивать поведение CAPTCHA между релизами и быстро находить регрессии в собственных формах:

import json, time, logging

log = logging.getLogger('captcha-qa')

def record(event: str, **fields) -> None:
    payload = {'ts': time.time(), 'event': event, **fields}
    log.info(json.dumps(payload, ensure_ascii=False))

Минимальный набор полей для каждой попытки: slug, captcha_type, task_id, wait_seconds, verify_status, env. Этого достаточно, чтобы построить дашборд медианы, P90 и P99 по типу CAPTCHA и по среде — и быстро увидеть, какой релиз замедлил решение Cloudflare Turnstile, а какой — reCAPTCHA v2.

Если сценарии логируют тестовые email или платёжные данные, помечайте их как фиктивные заранее — это дешевле, чем разбирать постфактум, соответствуют ли логи staging-среды требованиям 152-ФЗ «О персональных данных» или GDPR.

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

Симптом Что сделать
Утечка памяти Закрывайте контексты в finally
Параллельные тесты влияют друг на друга Используйте incognito
Ретраи бесконечны Ограничьте attempts
Токены тратятся слишком быстро Кешируйте токен внутри сценария
CI-джоба падает по общему таймауту Считайте таймаут на весь прогон, а не на один сценарий, и закладывайте время под ретраи
Сложно сравнить P90 между релизами Фиксируйте один и тот же набор сценариев для каждого сравнения

QA-чек-лист перед мержем

  • Запрос отправляется только на собственные или авторизованные endpoints.
  • Тестовые учётные записи, события и платежи помечены как фиктивные.
  • CAPTCHA-токен проверяется на собственном backend, а не доверяется клиенту.
  • Логи содержат task_id, тип CAPTCHA, время ожидания и pass/fail.
  • Лимит параллелизма (pLimit) и общий timeout джобы заданы явно, а не оставлены на усмотрение CI-раннера.
  • Скрипт возвращает корректный exit code, чтобы CI мог принять решение.

Частые вопросы

Можно ли использовать эти паттерны на сторонних сайтах?

Нет. Все сценарии в этой статье рассчитаны только на собственные или явно авторизованные среды. Для чужих ресурсов сначала нужно письменное разрешение владельца.

Что делать, если CaptchaAI вернул ошибку?

Залогируйте task_id, тип CAPTCHA и текст ошибки, повторите запрос с экспоненциальной задержкой и фиксируйте долю ошибок в дашборде. Если доля ошибок растёт от прогона к прогону, сначала проверьте sitekey и саму страницу, а не код ретраев.

Сколько потоков CaptchaAI нужно под параллельный QA-прогон?

Отталкивайтесь от значения pLimit: одновременных задач CaptchaAI в моменте примерно столько же, сколько параллельных контекстов Puppeteer. Для 4–5 контекстов достаточно STANDARD ($30/мес, 15 потоков); для крупных nightly-сюит — ADVANCE ($90/мес, 50 потоков).

Как безопасно кешировать токен, не ловя ложные срабатывания?

Держите кеш в рамках одного сценария и одного контекста и сбрасывайте его вместе с контекстом в finally. Токен, переиспользованный в другом сценарии или на другой странице, не пройдёт проверку на бэкенде — это не оптимизация, а источник ложных failed-тестов.

Как сравнивать результаты между релизами?

Сохраняйте логи в одном формате и стройте отчёт по медиане, P90 и P99 на одинаковом наборе сценариев. Сравнивайте только сопоставимые выборки в собственной среде — иначе разница будет объясняться разным набором сценариев, а не самим релизом.

Безопасные связанные руководства

Хотите ускорить собственную QA-сюиту на Puppeteer? Подключите CaptchaAI и настройте пул потоков под параллельные прогоны.

Комментарии для этой статьи отключены.