Skip to content

Generators

Why generators are in a separate method

PHP attribute arguments must be constant expressions, so #[Given('x', Gen::int())] is not expressible. Instead name a method that returns array<string, ArbitraryInterface> keyed by parameter name. When the generators argument is omitted the runner falls back to a method named <testMethod>Generators.

Declare generators (and examples) methods public static — or public if the body needs $this. Their only call site is this package's reflection, so static analysis sees them as unused: Rector's dead-code set deletes private ones (RemoveUnusedPrivateMethodRector). Public methods are safe, and Testo never treats a non-void-returning method as a test.

Generators

FactoryProducesShrinks
Gen::int()IntArbitrary, PHP_INT_MIN..PHP_INT_MAXtoward 0
Gen::intBetween($min, $max)IntArbitrary, [$min, $max]toward 0, clamped to range
Gen::intPositive()IntArbitrary, 1..PHP_INT_MAXtoward 1
Gen::float()FloatArbitrary, [0.0, 1.0)toward 0.0
Gen::floatBetween($min, $max)FloatArbitrary, [$min, $max]toward 0.0, clamped to range
Gen::bool()BoolArbitrary, true / falsetrue -> false
Gen::string()StringArbitrary, Unicode, length 0..100toward '', then by length, then each character toward a
Gen::stringAscii()StringArbitrary, printable ASCII, length 0..100toward '', then by length, then each character toward a
Gen::stringOf($min, $max)StringArbitrary, Unicode, bounded lengthtoward '', then by length, then each character toward a
Gen::stringFrom($alphabet, $min, $max)CharsetStringArbitrary, characters from a fixed alphabet (multibyte OK)toward '', then by length, then each character toward the first alphabet character
Gen::bytes($min, $max)BytesArbitrary, raw byte strings (bytes 0..255)toward '', then by length, then each byte toward "\x00"
Gen::arrayOf($element, $min, $max)ArrayArbitrary, lists of $element, size 0..100 by defaulttoward [], then by length, then each element
Gen::nonEmptyArrayOf($element, $max)ArrayArbitrary, non-empty listsby length (never below 1), then each element
Gen::uniqueArrayOf($element, $min, $max)UniqueArrayArbitrary, lists of pairwise-distinct elementslike arrayOf, but element candidates colliding with another element are skipped
Gen::dictOf($key, $value, $min, $max)DictionaryArbitrary, maps with distinct keys from $key (int/string) and values from $value, size 0..100 by defaulttoward [], then by size, then each value (keys fixed)
Gen::record($shape)RecordArbitrary, fixed-shape map ['field' => $arb, ...]each field via its arbitrary, key set fixed
Gen::elements($array)OneOfArbitrary, one value from an array (array form of oneOf)toward earlier-listed distinct values
Gen::enum(SomeEnum::class)OneOfArbitrary over the enum's casestoward earlier-declared cases (declare simpler cases first)
Gen::constant($value)ConstantArbitrary, always $valuedoes not shrink
Gen::char()StringArbitrary, a single printable ASCII charactertoward a
Gen::uuid()UuidArbitrary, RFC 4122 v4 UUID stringsdoes not shrink
Gen::datetime($min, $max)DateTimeArbitrary, UTC DateTimeImmutable, timestamp in [$min, $max]toward the Unix epoch, clamped
Gen::floatSpecial()OneOfArbitrary over NAN, ±INF, -0.0 and the float representation edgestoward earlier-listed specials
Gen::intRange($min, $max)FlatMappedArbitrary, ordered pairs [lo, hi] with lo <= hiboth bounds shrink, order always holds
Gen::recursive($leaf, $wrap, $maxDepth)bounded recursive structures: $wrap lifts the previous level's arbitrarywithin the branch that generated the value
Gen::oneOf(...$values)OneOfArbitrary, one of the given valuestoward earlier-listed distinct values (put simpler values first)
Gen::nullable($inner)NullableArbitrary, null or an $inner valueprefers null, then the inner tree
Gen::map($inner, $fn)MappedArbitrary, $inner transformed by $fnthrough the inner tree, re-applying $fn
Gen::flatMap($inner, $fn)FlatMappedArbitrary, dependent generator returned by $fn($innerValue)source value first (dependent value regenerated), then the dependent tree
Gen::filter($inner, $predicate)FilteredArbitrary, $inner values satisfying $predicate (throws GenerationExhausted after 100 rejected draws — never yields an out-of-domain value)inner tree, pruning candidates that fail the predicate
Gen::tuple(...$elements)TupleArbitrary, fixed-arity tuple, one value per elementeach position via its element, arity fixed
Gen::frequency($pairs)FrequencyArbitrary, weighted choice over [weight, arbitrary] pairswithin the branch that generated the value
Gen::ipv4()IPv4 dotted-quad stringseach octet toward 0
Gen::email()local@label.tld addressestoward the shortest local/label and first TLD
Gen::url()http(s)://host.tld[/path] URLstoward http://a.com
Gen::json($maxDepth)a JSON-encodable value (null/bool/int/float/string/list/object)within the generated structure
Gen::jsonString($maxDepth)the json_encode text of Gen::json()through the value's tree
Gen::regex($pattern) / Gen::stringMatching($pattern)strings matching a regex subset (compiled to combinators)shorter/simpler matches (via the compiled trees)

Numeric generators (int*, float*) are boundary-biased: roughly one draw in five returns an in-range edge value (0, ±1, min, max for ints; 0.0 or min for floats), where bugs cluster, instead of a uniform one. Shrinking is unaffected.

Sized generators guarantee their minimum: uniqueArrayOf/dictOf (distinct elements/keys) and commands (applicable steps) may fall short of the drawn size when the value space runs out, but never fall below $min — an unreachable minimum throws GenerationExhausted rather than hand the property a too-small value.