PHPackages                             rasuvaeff/property-testing-phpunit - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Testing &amp; Quality](/categories/testing)
4. /
5. rasuvaeff/property-testing-phpunit

ActiveLibrary[Testing &amp; Quality](/categories/testing)

rasuvaeff/property-testing-phpunit
==================================

PHPUnit adapter for the property-testing engine: a fluent forAll()-&gt;check() trait over the framework-agnostic runner

v0.1.0(yesterday)010↑2600%BSD-3-ClausePHPPHP 8.3 - 8.5CI passing

Since Aug 9Pushed yesterdayCompare

[ Source](https://github.com/rasuvaeff/property-testing-phpunit)[ Packagist](https://packagist.org/packages/rasuvaeff/property-testing-phpunit)[ Docs](https://github.com/rasuvaeff/property-testing-phpunit)[ RSS](/packages/rasuvaeff-property-testing-phpunit/feed)WikiDiscussions master Synced today

READMEChangelog (1)Dependencies (10)Versions (2)Used By (0)

rasuvaeff/property-testing-phpunit
==================================

[](#rasuvaeffproperty-testing-phpunit)

[![Latest Stable Version](https://camo.githubusercontent.com/f737684b0487f9210ab996e4a57a75c63832527d24c88d4433306a191cd769b6/68747470733a2f2f706f7365722e707567782e6f72672f7261737576616566662f70726f70657274792d74657374696e672d706870756e69742f76)](https://packagist.org/packages/rasuvaeff/property-testing-phpunit)[![Total Downloads](https://camo.githubusercontent.com/205451c15d2f08f56c58bd49bf5385a9506dbefc2afdf0ecc52f1715a2c93068/68747470733a2f2f706f7365722e707567782e6f72672f7261737576616566662f70726f70657274792d74657374696e672d706870756e69742f646f776e6c6f616473)](https://packagist.org/packages/rasuvaeff/property-testing-phpunit)[![Build](https://github.com/rasuvaeff/property-testing-phpunit/actions/workflows/build.yml/badge.svg)](https://github.com/rasuvaeff/property-testing-phpunit/actions/workflows/build.yml)[![Static analysis](https://github.com/rasuvaeff/property-testing-phpunit/actions/workflows/static-analysis.yml/badge.svg)](https://github.com/rasuvaeff/property-testing-phpunit/actions/workflows/static-analysis.yml)[![Psalm level](https://camo.githubusercontent.com/68f7f31799f2b93c710b14ba3877072e7fe07ec9d7cee3fdf67e14beab3e1b6f/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f7073616c6d2d6c6576656c5f312d626c75652e737667)](https://github.com/rasuvaeff/property-testing-phpunit/actions/workflows/static-analysis.yml)[![PHP](https://camo.githubusercontent.com/e18b98379a2d86a7273d698a0ecc4ff9c148fc199cd19319992634f0d080e523/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f646570656e64656e63792d762f7261737576616566662f70726f70657274792d74657374696e672d706870756e69742f706870)](https://packagist.org/packages/rasuvaeff/property-testing-phpunit)[![License](https://camo.githubusercontent.com/6cb285b57819f8de0acfb34923298f4f569f962544e8fe35331da2d163f4e485/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f6c6963656e73652d4253442d2d332d2d436c617573652d626c75652e737667)](LICENSE.md)

[Русская версия](README.ru.md)

PHPUnit adapter for the [property-testing engine](https://github.com/rasuvaeff/property-testing-core): a `PropertyTesting` trait with a fluent `forAll()->check()` API over the framework-agnostic runner. Generate hundreds of random inputs per test, find the failing one, and shrink it to a minimal counterexample you can actually read — inside an ordinary PHPUnit `TestCase`.

> Using an AI coding assistant? [llms.txt](llms.txt) contains a compact API reference you can share with the model.

Part of the property-testing family
-----------------------------------

[](#part-of-the-property-testing-family)

PackageUse it when[`rasuvaeff/property-testing-core`](https://github.com/rasuvaeff/property-testing-core)You drive the engine yourself: a custom harness, CI guard, CLI checker, or another framework adapter[`rasuvaeff/property-testing-testo`](https://github.com/rasuvaeff/property-testing-testo)You test with [Testo](https://github.com/php-testo/testo) — drop-in replacement for the frozen `rasuvaeff/property-testing` with the same `#[Property]` attribute**`rasuvaeff/property-testing-phpunit`** (this package)You test with PHPUnit — the `PropertyTesting` trait with the fluent `forAll()->check()` APIRequirements
------------

[](#requirements)

- PHP 8.3+
- [`phpunit/phpunit`](https://packagist.org/packages/phpunit/phpunit) `^11.5 || ^12.0`
- [`rasuvaeff/property-testing-core`](https://packagist.org/packages/rasuvaeff/property-testing-core) `^0.1`

Installation
------------

[](#installation)

```
composer require --dev rasuvaeff/property-testing-phpunit
```

No configuration is needed: mix the trait into a `TestCase` and call `forAll()`from a test method.

Usage
-----

[](#usage)

Map each property-body parameter to a generator, configure the run with the fluent chain, and hand the property to `check()`. The engine generates random arguments, runs the closure the configured number of times, and on the first failure shrinks the counterexample to a minimal one:

```
use PHPUnit\Framework\TestCase;
use Rasuvaeff\PropertyTesting\Gen;
use Rasuvaeff\PropertyTesting\PhpUnit\PropertyTesting;

final class SortPropertyTest extends TestCase
{
    use PropertyTesting;

    public function testSortIsIdempotent(): void
    {
        $this->forAll(['values' => Gen::arrayOf(Gen::int())])
            ->runs(300)
            ->check(static function (array $values): void {
                sort($values);
                $once = $values;
                sort($values);

                self::assertSame($once, $values);
            });
    }
}
```

The closure's **parameter names select the generators**, exactly like a `#[Property]` method signature does under the Testo adapter. On failure the test fails with the engine's message:

```
Property falsified after 12 successful run(s); seed=7382910
  Original: values=[20, 82, 44, 43, 29, 47, 29, 0, … +4 more]
  Shrunk:   values=[0, 0, 0, 0, 0, 0] (7 shrink step(s), 29 trial(s))
  Changed:  values=[20, 82, 44, …] -> [0, 0, 0, 0, 0, 0]

```

Reproduce the exact run by pinning the reported seed: `->seed(7382910)`.

### The fluent chain

[](#the-fluent-chain)

`forAll()` returns a `PropertyCheck`; every setter returns it for chaining, and `check()` runs the property.

MethodMeaning`runs(int)`Successful checks to complete (default 100). Discarded runs do not count`seed(int)`Pins the random phase for reproduction. Also disables corpus replay — the pinned run wins`maxShrinks(int)`Cap on accepted shrink steps; `0` disables shrinking`maxDiscards(int)`Discard budget before the property fails with `GaveUpException`; default `runs * 10``timeoutMs(int)`Wall-clock deadline for a single run — exceeding it fails with `DeadlineExceededException``budgetMs(int)`Wall-clock budget for the whole random phase — running out fails with `TimeBudgetExceededException``examples(array)`Fixed positional argument tuples run **before** the random phase; a failing example short-circuits, unshrunk`listeners(...)``PropertyListener` observers of the engine's lifecycle events`output($stdout, $stderr)`Redirects the distribution report, discard warning and verbose trace (used by this package's own tests)### How results map onto PHPUnit

[](#how-results-map-onto-phpunit)

- A **pass** counts one assertion — the test is never marked risky.
- Every **failing outcome** (falsified, gave up, unmet coverage, deadline, budget, generation failure, failing example, replayed regression) surfaces as **one `AssertionFailedError`** whose message is the engine's own — seed, original and shrunk arguments, shrink statistics — and whose `previous` is the engine exception (`PropertyViolationException`, `GaveUpException`, `RegressionViolationException`, …).
- `Assume::that()` is a **discarded run inside the property**, retried by the engine — never a skipped PHPUnit test.

### Environment overrides

[](#environment-overrides)

Byte-for-byte parity with the Testo adapter — one contract across adapters:

VariableEffect`PROPERTY_RUNS`Positive integer that overrides every property's run count (dial runs up in CI)`PROPERTY_SEED`Integer seed for any property without an explicit `seed()` (replay a whole suite). An explicit `seed()` still wins`PROPERTY_VERBOSE`Any value except `''`/`'0'` logs every run's generated arguments and each accepted shrink step`PROPERTY_DB`Directory path enabling the regression corpus. Unset means off, nothing is writtenThe corpus format is exactly the one `rasuvaeff/property-testing` 2.8 wrote — a corpus recorded under Testo (or under 2.x) replays here and vice versa. On falsification the minimal input is recorded; the next run replays recorded failures **first** (unless `seed()` pins the property) and reports a still-red one as a `RegressionViolationException`; a green one is pruned.

### Distribution and discards

[](#distribution-and-discards)

`Classify::label()`/`when()`/`cover()` work inside the property body. When a classified property passes, the adapter prints the label distribution:

```
Property "testSortKeepsEveryElement" distribution: long 39% (77/200), short 61% (123/200)

```

A property that discards more than 90% of its attempts (via `Assume::that()`) gets a warning suggesting narrower generators.

### Why no `#[Property]` attribute?

[](#why-no-property-attribute)

PHPUnit's public extension/event API observes test execution but offers no stable contract for intercepting and re-invoking a test method many times — which is exactly what a property attribute must do. This adapter deliberately does not depend on PHPUnit internals; the fluent API needs only the documented surface. An attribute may appear later, only if it can be built on the documented extension API of the supported majors.

### Generators

[](#generators)

The full generator catalog (`Gen::int()` … `Gen::subset()`, `Gen::regex()`, `Gen::commands()`, `Gen::draw()`, `Shrinkable`, writing your own `ArbitraryInterface`, stateful/model-based testing) is the engine's API, documented in the [core README](https://github.com/rasuvaeff/property-testing-core#generators). Everything there is usable from a `check()` closure as-is.

Public API of this package
--------------------------

[](#public-api-of-this-package)

TypeRole`Rasuvaeff\PropertyTesting\PhpUnit\PropertyTesting`The trait a `TestCase` mixes in; `forAll()` is its single entry point`Rasuvaeff\PropertyTesting\PhpUnit\PropertyCheck`The fluent builder: resolves the chain and the environment into a core `PropertyDefinition`, runs the engine, maps the structured result onto PHPUnit`Rasuvaeff\PropertyTesting\PhpUnit\VerboseListener``PROPERTY_VERBOSE` output as an exception-hardened engine listener (internal)Security
--------

[](#security)

Generated values are pseudo-random (seeded MT19937), not cryptographic. Seeds are not secrets — they are printed in failure output by design. Treat `PROPERTY_DB` corpus files as test artifacts: they contain generated inputs verbatim, so do not point the variable at a directory that gets published.

Examples
--------

[](#examples)

See [examples/](examples/) — a complete property-based `TestCase`:

```
vendor/bin/phpunit examples/SortPropertyTest.php
```

Development
-----------

[](#development)

```
make install     # composer install (Docker)
make build       # validate + normalize + require-checker + cs + psalm + tests
make cs-fix      # apply code style
make mutation    # infection mutation testing
```

Tests run through PHPUnit (`composer test` is `phpunit`), not Testo.

License
-------

[](#license)

[BSD-3-Clause](LICENSE.md)

###  Health Score

40

—

FairBetter than 86% of packages

Maintenance100

Actively maintained with recent releases

Popularity7

Limited adoption so far

Community6

Small or concentrated contributor base

Maturity40

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Unknown

Total

1

Last Release

1d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/b0812d5572a7041dfe36e222d295b2e6dc55833a605350fcde58a51a5965ed30?d=identicon)[rasuvaeff](/maintainers/rasuvaeff)

---

Top Contributors

[![rasuvaeff](https://avatars.githubusercontent.com/u/1352718?v=4)](https://github.com/rasuvaeff "rasuvaeff (2 commits)")

---

Tags

phpphp8phpunitproperty-based-testingproperty-testingquickcheckshrinkingtestingtestingphpunitQuickCheckproperty-testingfuzzingshrinking

###  Code Quality

Static AnalysisPsalm, Rector

Code StylePHP CS Fixer

Type Coverage Yes

### Embed Badge

![Health badge](/badges/rasuvaeff-property-testing-phpunit/health.svg)

```
[![Health](https://phpackages.com/badges/rasuvaeff-property-testing-phpunit/health.svg)](https://phpackages.com/packages/rasuvaeff-property-testing-phpunit)
```

###  Alternatives

[brianium/paratest

Parallel testing for PHP

2.5k142.8M1.1k](/packages/brianium-paratest)[spatie/phpunit-snapshot-assertions

Snapshot testing with PHPUnit

69720.4M683](/packages/spatie-phpunit-snapshot-assertions)[allure-framework/allure-phpunit

Allure PHPUnit integration

6613.6M48](/packages/allure-framework-allure-phpunit)[facile-it/paraunit

paraunit

145905.2k19](/packages/facile-it-paraunit)[robiningelbrecht/phpunit-pretty-print

Prettify PHPUnit output

77598.4k18](/packages/robiningelbrecht-phpunit-pretty-print)[shopsys/http-smoke-testing

HTTP smoke test case for testing all configured routes in your Symfony project

67275.0k2](/packages/shopsys-http-smoke-testing)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
