diff --git a/CHANGELOG.md b/CHANGELOG.md index de01337..8f76f19 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,14 @@ # Changelog +## Unreleased + +- **Docs:** the READMEs and `llms.txt` now state what follows from driving the + engine through a single PHPUnit test method: `setUp()`/`tearDown()` and + `#[Before]`/`#[After]` run once per property rather than per run, test-double + expectations are verified after the last run (an unmet `once()` fails the + property as a whole and is never shrunk), and per-test coverage counts lines + reached only while shrinking. The Testo adapter does not share the first one. + ## 1.0.0 — 2026-09-20 The stability release, in step with `rasuvaeff/property-testing-core` 1.0.0. diff --git a/README.md b/README.md index 14a044e..f61578f 100644 --- a/README.md +++ b/README.md @@ -276,7 +276,22 @@ would otherwise falsify every run without a word of explanation. [`throws()`](#expected-exceptions-throws) instead, or assert on the exception inside the body. - `setUp()` runs once per test, not per generated input — a property is one - test method with one `check()`. + test method with one `check()`. So is `tearDown()`, and so are `#[Before]` + and `#[After]` hooks: state a run leaves behind is visible to the next run + and to every shrinking attempt. Build per-input fixtures inside the body. +- **Test double expectations are verified once per property.** PHPUnit + verifies the doubles it created when the test method ends, which is after + the last run. An unmet `once()` therefore fails the property as a whole: + it is not attributed to an input and it is not shrunk. A double that must + be checked per input has to be created and verified inside the body. +- **Code coverage counts shrinking attempts.** Every run — random phase, + corpus replay and each shrink trial — executes inside the one test method, + so per-test coverage includes lines reached only while shrinking. +- These three follow from driving the engine through a single test method, + which is what PHPUnit's extension points allow. The + [Testo adapter](https://github.com/rasuvaeff/property-testing-testo) does + not share the first one: there each run goes through the framework's + interceptor pipeline, so lifecycle hooks run per run. - With a **data provider**, the corpus id carries the data set name (`Class::method with data set "large"`), so the sets do not replay — and prune — each other's regressions. The unstable-id warning is printed only diff --git a/README.ru.md b/README.ru.md index ecf592e..8c0eccd 100644 --- a/README.ru.md +++ b/README.ru.md @@ -273,7 +273,24 @@ $this->forAll(['width' => Gen::intBetween(1, 5000), 'minWidth' => Gen::intBetwee прогона. Объявляйте ожидание через [`throws()`](#ожидаемые-исключения-throws) или проверяйте исключение внутри тела. - `setUp()` выполняется один раз на тест, а не на каждый вход — property это - один тестовый метод с одним `check()`. + один тестовый метод с одним `check()`. То же касается `tearDown()` и хуков + `#[Before]`/`#[After]`: состояние, оставленное прогоном, видно следующему + прогону и каждой попытке усадки. Фикстуры, нужные на каждый вход, стройте + внутри тела. +- **Ожидания тест-даблов проверяются один раз на свойство.** PHPUnit + верифицирует созданные им даблы в конце тестового метода — то есть после + последнего прогона. Невыполненный `once()` поэтому роняет свойство целиком: + он не привязывается к входу и не усаживается. Дабл, который должен + проверяться на каждый вход, нужно создавать и верифицировать внутри тела. +- **Покрытие учитывает попытки усадки.** Все прогоны — случайная фаза, + воспроизведение корпуса и каждая усадочная попытка — выполняются внутри + одного тестового метода, поэтому в покрытие теста попадают строки, + достигнутые только во время усадки. +- Эти три следствия вытекают из того, что движок работает внутри одного + тестового метода — так устроены точки расширения PHPUnit. + [Testo-адаптер](https://github.com/rasuvaeff/property-testing-testo) первого + из них лишён: там каждый прогон идёт через конвейер интерцепторов + фреймворка, поэтому хуки жизненного цикла выполняются на каждый прогон. - С **data provider** id корпуса несёт имя набора (`Class::method with data set "large"`), чтобы наборы не воспроизводили — и не удаляли — регрессии друг друга. Предупреждение о нестабильном id печатается только при diff --git a/llms.txt b/llms.txt index 8df8edd..a03d1c7 100644 --- a/llms.txt +++ b/llms.txt @@ -56,6 +56,14 @@ Requires PHP 8.3–8.5, `phpunit/phpunit` `^11.5 || ^12.0 || ^13.0` that returns normally or throws another class fails and shrinks. Skips and `Assume::that()` discards are unaffected. A non-Throwable class string is rejected with `InvalidArgumentException`. +- The engine drives ONE PHPUnit test method, so: `setUp()`/`tearDown()` and + `#[Before]`/`#[After]` run once per property, not per run (state leaks into + the next run and into every shrink attempt); test-double expectations are + verified after the last run, so an unmet `once()` fails the property as a + whole instead of being attributed to an input and shrunk; per-test coverage + includes lines reached only while shrinking. Per-input fixtures and doubles + belong inside the body. The Testo adapter runs each trial through the + framework's interceptor pipeline, so there lifecycle hooks DO run per run. - Environment contract and corpus format are identical to the Testo adapter and to frozen `rasuvaeff/property-testing` 2.8 — corpora interoperate. - Generators, `Shrinkable`, `Assume`, `Classify`, `Gen::draw()`, state-machine