Skip to content

Рецепт: CI

Три небольшие настройки, важные, когда property-тесты гоняются в CI, а не только на машине разработчика: поднять число прогонов без правки каждого атрибута, не пускать корпус регрессий в систему контроля версий, и ограничить, сколько может выполняться патологический вход.

Поднять runs для CI без правки тестового кода

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

bash
docker run --rm -v "$PWD":/app -w /app -e PROPERTY_RUNS=2000 \
  composer:2 vendor/bin/testo

PROPERTY_RUNS безусловно переопределяет runs каждого property на этот запуск — включая property, зафиксировавшие своё значение в атрибуте. Никакой логики «не ниже» здесь нет: если property намеренно ставит низкий runs: из-за дорогого генератора, общий PROPERTY_RUNS=2000 в CI применится и к нему тоже. Держите действительно медленные property медленными как-то иначе (более узкий домен генератора, budgetMs ниже), а не полагайтесь на переменную окружения, что она их не тронет.

Игнорировать директорию корпуса регрессий в git

PROPERTY_DB=<dir> включает корпус регрессий: каждое фальсифицированное property сохраняет своё падение в маленький JSON-файл, реплеимый перед random-фазой на следующем прогоне. Это ровно то поведение, которое нужно внутри прогона CI или сессии локальной отладки, и ровно то поведение, которое не должно утекать в git — закоммиченная директория корпуса зафиксировала бы локальные падения каждого контрибьютора в общей истории и сделала бы прогоны с PROPERTY_DB невоспроизводимыми между машинами.

# .gitignore
/.property-db/

Указывайте PROPERTY_DB на тот же путь в CI:

bash
docker run --rm -v "$PWD":/app -w /app -e PROPERTY_DB=.property-db \
  composer:2 vendor/bin/testo

Если хотите, чтобы падения сохранялись между прогонами CI (поймать подозрительно нестабильное property, падающее раз в несколько тысяч прогонов, до того как оно полноценно регрессирует) — кэшируйте директорию между job'ами через кэш-action вашего CI-провайдера с устойчивым ключом, но не пускайте её в сам репозиторий.

Ограничить патологические входы через budgetMs

У PROPERTY_RUNS=2000 в CI есть неограниченный минус: генератор, изредка производящий катастрофически медленный вход (глубокая рекурсия, regex, против которого property матчится), может заставить всю random-фазу идти намного дольше любого бюджета CI-джобы — и ни одно assertion при этом не упадёт.

php
#[Property(runs: 2000, budgetMs: 30_000)]
public function parsesWithinBudget(string $input): void
{
    // ...
}

budgetMs ограничивает всю random-фазу целиком; превышение до набора runs успешных проверок бросает TimeBudgetExceededException со счётчиками выполненных/требуемых — понятное падение CI вместо джобы, уходящей в таймаут без диагностики. Для одного патологического входа, а не всей фазы, timeoutMs — более узкий инструмент; см. Дедлайны и временные бюджеты про разницу и почему прогон с таймаутом намеренно не шринкается.