Integrated shrinking
generate() returns value + lazy shrink tree together — transformed generators (map, flatMap) shrink correctly for free.
One framework-agnostic engine, two framework adapters — Testo and PHPUnit.
property-testing-core — the framework-agnostic engine: generators, shrinking, the regression corpus, events. Depends on nothing but PHP.property-testing-testo — the #[Property] attribute, self-registering with Testo's interceptor discovery.property-testing-phpunit — a fluent forAll()->check() trait for PHPUnit test cases.Install core plus exactly the adapter your test suite already uses; composer why testo/testo stays empty if you never asked for it.
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 -> 1Four generated arguments went in; the Changed: line tells you only three of them actually drive the failure — the shrinker found that by searching, you didn't have to step through a debugger to see it.
See Cookbook for real monorepo incidents property tests would have caught before they shipped.