Автогенерация unit-тестов в Claude Code

Claude Code умеет писать тесты сам, но результат зависит от модели и промпта. Как подключить его к zukko.pro и получить тесты, а не заглушки.

4 мин · · Обновлено · Claude Code · тестирование

Подключение

Claude Code — Anthropic-совместимый клиент, поэтому адрес шлюза указывается без /v1: он сам добавляет путь. Два способа задать переменные — через окружение или через settings.json в ~/.claude/.

Через окружение:

bash
export ANTHROPIC_BASE_URL=https://api.zukko.pro
export ANTHROPIC_AUTH_TOKEN=fetch_ваш_ключ

Через settings.json:

bash
{
  "env": {
    "ANTHROPIC_BASE_URL": "https://api.zukko.pro",
    "ANTHROPIC_AUTH_TOKEN": "fetch_ваш_ключ"
  }
}

Важно писать именно ANTHROPIC_AUTH_TOKEN, а не ANTHROPIC_API_KEY — со вторым Claude Code решает, что вы вошли через официальный аккаунт Anthropic, и просит логин вместо того, чтобы использовать ключ. Ключ создаётся в кабинете на странице https://zukko.pro/app/api-keys и показывается один раз, так что сразу сохраните его в менеджере паролей.

Какую модель ставить по умолчанию

Для генерации тестов имеет смысл разделить задачи по сложности. claude-sonnet-5 быстрый и достаточно точный для рутины: тесты на геттеры, валидацию входных данных, простые обработчики ошибок. claude-opus-5-5 имеет смысл пробовать для кода со сложной веткой логики — там, где нужно продумать граничные случаи, а не просто повторить структуру функции. У него можно включить extended thinking — проверьте на своих сложных модулях, становится ли меньше тестов, которые проходят, но ничего не проверяют. По цене в Zukko обе модели одинаковы ($0.30/$0.30 за 1M токенов входа/выхода).

Обе модели держат контекст в 1 000 000 токенов, так что можно скормить не только тестируемый файл, но и соседние модули, существующие тесты для стиля и конфиг тестового раннера — модель будет писать тесты, которые совпадают по конвенциям с остальным проектом, а не изобретать свой стиль моков.

Переключение модели в Claude Code — через /model claude-opus-5-5 в сессии или флаг при запуске.

Промпт, который не выдаёт бесполезные assert'ы

Модели, если не ограничить их явно, любят генерировать тесты вида expect(result).toBeDefined() — формально тест есть, по факту он ничего не ловит. Работающий промпт обычно включает три ограничения одновременно: явный запрет на тесты без проверки конкретных значений, требование покрыть хотя бы один граничный случай и требование смотреть на существующие тесты в репозитории как на образец.

Пример запроса в сессии Claude Code:

Напиши unit-тесты для src/payments/calculateFee.ts. Требования: - каждый тест проверяет конкретное числовое значение или конкретную ошибку, никаких toBeDefined/toBeTruthy без причины - отдельный тест на нулевую сумму и на отрицательную сумму (должна кидать ValidationError) - стиль и структура — как в src/payments/__tests__/calculateTax.test.ts - используй Vitest, как в остальном проекте

Такой промпт с контекстом соседнего тестового файла обычно даёт тесты, которые действительно падают, если сломать логику — можно проверить, испортив одну строку в функции и перезапустив тесты.

Что делать, если генерация обрывается

Если ответ обрывается на середине файла с тестами, это почти всегда не лимит токенов, а таймаут клиента или сети — стоит проверить max_tokens в конфиге и таймаут HTTP-клиента, если вы дергаете модель напрямую через SDK внутри своего скрипта, а не через встроенный агент Claude Code. Сам Claude Code таймауты настраивает разумно по умолчанию, обрывы там обычно случаются на очень длинных файлах — в таком случае имеет смысл просить тесты по одному модулю за раз, благо контекста в 1M токенов хватает, чтобы не терять картину проекта при таком дроблении.

Регрессионный тест по скриншоту бага

Раз модели Claude (как и GPT) принимают изображения, скриншот с багом можно приложить прямо к запросу вместо текстового описания. Модель видит, что именно сломано в интерфейсе, и генерирует тест, который эту ситуацию воспроизводит:

bash
import base64, requests

with open("bug-screenshot.png", "rb") as f:
    img = base64.b64encode(f.read()).decode()

resp = requests.post(
    "https://api.zukko.pro/v1/messages",
    headers={"Authorization": "Bearer fetch_ваш_ключ", "anthropic-version": "2023-06-01"},
    json={
        "model": "claude-opus-5-5",
        "max_tokens": 2048,
        "messages": [{
            "role": "user",
            "content": [
                {"type": "image", "source": {"type": "base64", "media_type": "image/png", "data": img}},
                {"type": "text", "text": "На скриншоте баг в форме оплаты. Напиши regression-тест на Playwright, который его ловит."},
            ],
        }],
    },
)

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

Если модель, на которую завязан CI, выключили

Каталог моделей время от времени обновляется, и модель, зашитая в конфиг CI, может оказаться выключенной — например, так уже произошло с claude-opus-4.6. Запрос к ней возвращает 503 с кодом model_disabled (не 404 и не model_retired):

bash
{"error": {"code": "model_disabled"}}

Замену смотрите в актуальном списке GET https://api.zukko.pro/v1/models — для Opus это claude-opus-5-5. Стоит обрабатывать этот код отдельно от прочих ошибок и логировать его — тогда обновление конфига CI занимает одну строчку, а не расследование, почему вдруг все прогоны генерации тестов начали падать.

Контроль расходов

Каждый обычный (не потоковый) ответ шлюза несёт заголовок X-Zukko-Cost-Micros — стоимость именно этого запроса в микродолларах (1 000 000 = $1) — и X-Fetch-Balance-Cents с остатком после списания (несмотря на название, тоже в микродолларах). У потоковых ответов — а сам Claude Code работает именно так — этих заголовков нет: расход сессий Claude Code смотрите в кабинете, через GET /v1/usage или GET /v1/balance. Если вы автоматизировали прогон генерации тестов по всем модулям проекта скриптом без стриминга, эти заголовки удобно логировать, чтобы понимать, на каких файлах модель "думает" дольше и дороже — обычно это сигнал, что там сложная логика и тесты стоит перепроверить руками.

Полную историю списаний (общую на весь аккаунт, все ключи вместе) можно посмотреть через GET /v1/usage с курсорной пагинацией, а текущий баланс — через GET /v1/balance. Оба требуют ключ:

bash
curl https://api.zukko.pro/v1/balance \
  -H "Authorization: Bearer fetch_ваш_ключ"

Если баланс закончится, шлюз вернёт 402 ещё до обращения к модели — запрос ничего не будет стоить, но и тестов вы не получите, так что для CI-пайплайна, который гоняет генерацию тестов на каждый PR, стоит настроить дневной лимит на ключе на странице ключа в кабинете, чтобы один сорвавшийся цикл не съел весь бюджет разом.

zukko

Платите в 25×
меньше

за Claude Code & ChatGPT

Один ключ fetch_* и base URL api.zukko.pro — в Cursor, Claude Code, Codex и любом SDK.