Skip to content

Property ​

Rasuvaeff\PropertyTesting\Property

Class — Package: property-testing-testo — Source — Version: v1.0.0

Implements: Testo\Pipeline\Attribute\Interceptable

Marks a test method as a property: the PropertyInterceptor takes over, generating random arguments from a generators method until the property has completed $runs successful checks or exhausted its discard budget.

Attribute arguments in PHP must be constant expressions. A provider can be a method name, any callable accepted by an attribute expression, or an invokable provider object; callable providers return array<string, ArbitraryInterface>, keyed by parameter name. When $generators is null the runner falls back to a method named <testMethod>Generators.

With $auto the provider becomes optional: parameters it does not cover are derived from the property's own signature through Gen::forParameters() — the @param psalm type when there is one, the native type otherwise.

Constructor ​

php
__construct(
    int $runs = 100,
    ?int $seed = NULL,
    callable|array<array-key,mixed>|string|null $generators = NULL,
    ?int $maxShrinks = NULL,
    callable|array<array-key,mixed>|string|null $examples = NULL,
    ?int $maxDiscards = NULL,
    ?int $timeoutMs = NULL,
    ?int $budgetMs = NULL,
    ?\Runner\ShrinkMode $shrink = NULL,
    ?int $shrinkBudgetMs = NULL,
    ?list<\Runner\Phase> $phases = NULL,
    bool $derandomize = false,
    ?string $path = NULL,
    \Runner\EdgeCases $edgeCases = Rasuvaeff\PropertyTesting\Runner\EdgeCases::Mixin,
    bool $auto = false,
    ?class-string<\Throwable> $throws = NULL,
    bool $exhaustive = false,
    int $exhaustiveBudget = 10000,
    int $flakyReplays = 2,
    int $searchRuns = 0,
)
ParameterTypeDefaultDescription
$runsint100Number of successful random inputs to check. Discarded inputs do not count.
$seed?intNULLFixed seed for reproducibility. Omit to let the runner pick a random one (the failing seed is reported by PropertyViolationException).
$generators`callablearray<array-key,mixed>string
$maxShrinks?intNULLCap on the number of accepted shrink steps. Null (default) means no cap. 0 disables shrinking, reporting the original counterexample unchanged.
$examples`callablearray<array-key,mixed>string
$maxDiscards?intNULLMaximum number of discarded inputs before the property gives up. Null (default) uses ten times the resolved run count.
$timeoutMs?intNULLWall-clock deadline for a single run (random or example) in milliseconds. A body that takes longer fails the property with a DeadlineExceededException naming the offending input — protection against pathological inputs (catastrophic regex, deep recursion, unbounded backoff). Measured after the run returns, so a body that never returns cannot be interrupted; shrink trials are not measured. Null (default) disables the deadline.
$budgetMs?intNULLWall-clock budget for the whole random phase in milliseconds. When it runs out before $runs successful checks complete, the property fails with a TimeBudgetExceededException. Null (default) disables the budget.
$shrink?\Runner\ShrinkModeNULLHow hard to minimise a counterexample: Runner\ShrinkMode::Full (the default), Runner\ShrinkMode::Off to report the input as generated, or Runner\ShrinkMode::Bounded together with $shrinkBudgetMs.
$shrinkBudgetMs?intNULLWall-clock budget for the shrink descent in milliseconds — the one knob here that costs determinism, since how far the descent gets depends on how long the body takes. It answers "the descent hung", not "reproduce this exactly".
$phases?list<\Runner\Phase>NULLStages this property performs, in run order. Null (default) runs all of them; a subset trades coverage for time on purpose.
$derandomizeboolfalseDerives an unset seed from the property id instead of drawing one, so the same property on the same code always selects the same inputs. An explicit $seed still wins.
$path?stringNULLA recorded shrink descent (CounterExample::$path) followed instead of searched for again. It needs the $seed of the run that produced it — the steps mean nothing against another one — and it is a debugging aid, not a fixture: editing a generator orphans it, which is what the regression corpus is for.
$edgeCasesRunner\EdgeCasesRasuvaeff\PropertyTesting\Runner\EdgeCases::MixinWhether the numeric generators keep biasing toward their boundary values (Runner\EdgeCases::Mixin, the default) or generate uniformly (Runner\EdgeCases::None). Turn them off when the edges are what this property cannot use — a body discarding 0, a range end that violates a precondition — so the discard budget stops paying for one run in five.
$autoboolfalseDerive a generator from the property's signature for every parameter the provider does not cover — the @param psalm type when there is one (int&lt;1, 300&gt; beats a bare int), the native type otherwise, and an error naming the parameter for anything unreadable. The provider (explicit or conventional) becomes the overrides and may be partial; it may also cover everything, in which case auto derives nothing. Deliberately opt-in and deliberately without an environment knob: the environment dials the suite, while this changes what one property's arguments mean.
$throws?class-string<\Throwable>NULLThe exception class every run must throw. A run that throws it (or a subclass) passes; one that returns normally fails with Expected &lt;class&gt; to be thrown, but it was not and shrinks like any other counterexample; one that throws another class fails with that throw. A skip and an Assume::that() discard keep their meaning — never a pass earned by throwing. This is the per-run replacement for #[ExpectException], which observes the aggregate result and is refused on a property. The matching throw is recorded as an assertion, so a body that asserts nothing else is not reported as risky.
$exhaustiveboolfalseWalk the whole parameter domain instead of sampling it, when every generator has a finite domain (Enumerable) and the product fits $exhaustiveBudget; otherwise the phase samples and the report says why. runs is ignored when it walks. PROPERTY_EXHAUSTIVE turns it on for the suite.
$exhaustiveBudgetint10000The largest domain $exhaustive walks; at least 1.
$flakyReplaysint2Re-executions of the minimised counterexample after the descent; one that passes marks the counterexample flaky (a Flaky: line names the replay). 0 disables the check.
$searchRunsint0Bodies the targeted search may execute after the random phase, for a body that calls Target::maximize()/minimize(): the best-scoring inputs are mutated one parameter at a time. 0 (the default) performs no search. PROPERTY_SEARCH_RUNS overrides it for the suite.

Properties ​

PropertyTypeReadonlyDescription
generators`\Closurearray<array-key,mixed>string
examples`\Closurearray<array-key,mixed>string