Skip to content

Что такое property-testing

Большинство PHP-тестов — пример-based: берём вход, проверяем выход.

php
public function reversingAOneTwoThree(): void
{
    Assert::same([3, 2, 1], array_reverse([1, 2, 3]));
}

Это доказывает, что функция работает правильно для [1, 2, 3]. Но ничего не говорит про [], [0], список из 10 000 элементов или список с -0.0. Каждый дополнительный кейс, который вы придумали — ещё один тест, который нужно написать, а те, что вы не придумали — как раз то место, где прячутся баги.

Property-based testing меняет направление. Вместо конкретного входа вы формулируете закон, который должен выполняться для любого входа из некоторого домена:

php
#[Property(runs: 200)]
public function reversingTwiceRestoresTheList(array $xs): void
{
    Assert::same(array_reverse(array_reverse($xs)), $xs);
}

Раннер генерирует 200 случайных массивов — пустые, огромные, с повторами, с отрицательными числами, с PHP_INT_MAX — и проверяет закон на каждом. Если один из них падает, раннер не просто сообщает про этот случайный вход: он shrink'ает его, ищет в дереве меньших кандидатов простейший вход, который всё ещё ломает закон, и сообщает уже его.

Property falsified after 246 successful run(s); seed=7382910
  Original: maxAttempts=17, baseSeconds=91, cap=847, attempts=23
  Shrunk:   maxAttempts=1, baseSeconds=848, cap=847, attempts=1 (12 shrink step(s), 41 trial(s))
  Changed:  maxAttempts=17 -> 1, baseSeconds=91 -> 848, attempts=23 -> 1

В этом вся идея: генерировать широко, потом минимизировать точно. Строка Changed: выше — и есть выигрыш: из четырёх сгенерированных аргументов на падение реально влияют только три, и вы знаете это без пошагового дебага.

Что добавляет этот пакет

  • Атрибут #[Property], который подключается прямо к обычному Testo тест-кейсу — без отдельного раннера, без нового тест-фреймворка.
  • Десятки встроенных генераторов (Gen::int(), Gen::string(), Gen::arrayOf(), …) плюс инструменты для своих — см. Генераторы.
  • Integrated shrinking: каждый генератор уже знает, как минимизировать собственные значения — см. Shrinking.
  • Путь дальше одиночных входов — к случайным последовательностям операций над stateful-системой — см. State machine тестирование.

Дальше

Быстрый старт устанавливает пакет и проводит через написание первого property. Концепции — короткий глоссарий терминов, используемых по всему сайту (property, arbitrary, shrinking, counterexample, seed), если хочется сначала прочитать определения.