Property-based testing for Go. A property pairs a generator with a predicate. The library runs the predicate against generated inputs, shrinks counterexamples, and records the seed so any failure replays exactly.
go get github.com/Quad4-Software/pbt@latestFor local development against a checkout, point a replace directive at it:
replace github.com/Quad4-Software/pbt => ../pbtimport "github.com/Quad4-Software/pbt/pkg/pbt"package mypkg_test
import (
"testing"
"github.com/Quad4-Software/pbt/pkg/pbt"
)
func TestStringRoundTrip(t *testing.T) {
property := pbt.ForAll(
"double reverse preserves value",
pbt.StringASCII(0, 64),
func(in string) bool {
return reverse(reverse(in)) == in
},
pbt.WithShrinker[string](pbt.StringShrinker()),
)
pbt.Check(t, property, pbt.WithRuns(1000), pbt.WithSeed(42))
}WithShrinker is optional: built-in generators carry their own shrinkers, so counterexamples are minimized automatically. NewGenerator leaves a generator unshrunk; use NewShrinkableGenerator to attach one to a custom generator.
Outside the testing package, call pbt.CheckResult and inspect the returned result.
ForAllbuilds a property from a generator and a predicate.ForAll2andForAll3take two or three generators and pass the values to the predicate directly.Checkruns it and fails the test on the first counterexample.CheckResultruns it and returns a structured result.WithPreconditionrejects generated cases before the predicate runs, like QuickCheck implication. Rejected cases count inResult.Skippedand generation continues untilRunsevaluated cases; if rejects exceedMaxDiscardsthe run fails withResult.Exhausted.WithRuns,WithMaxSize,WithSeed,WithTimeout, andWithMaxDiscardsconfigure the run.WithParallelismsplits seed partitions across workers.WithShrinkParallelismtunes shrink workers.- Generators:
Int,IntRange,Int64,Int64Range,Uint64,Bool,Float64,StringASCII,String,Bytes,SliceOf,MapOf,Map,DurationRange,PtrOf. - Conditional generation:
SuchThatandSuchThatFallbackfilter source values by predicate.WithPreconditiondiscards cases at the property level. - Combinators:
Tuple2,Tuple3,Product2,OneOf,Frequency,FlatMap,Recursive.FlatMapbinds the output of one generator into the next, which builds correlated structures. - Shrinkers:
IntShrinker,IntShrinkerToward,Int64Shrinker,Int64ShrinkerToward,Uint64Shrinker,StringShrinker,SliceShrinker,SliceShrinkerOf,BytesShrinker,MapShrinker,PtrShrinker,DurationShrinkerToward,Tuple2Shrinker,Tuple3Shrinker.SliceShrinkerOfshrinks elements in place after removing what it can. Shrinking is integrated: generators attach their own shrinkers andWithShrinkeroverrides them. Sampledraws n values from a generator with a seed for inspecting distributions.- Failure triage:
WithClassifier,WithLabeler,WithBucketer. Coverage thresholds:WithLabelCoverageRules,WithBucketCoverageRules. Distribution checks:AnalyzeDistribution,ValidateDistribution. - Stateful testing:
CheckStateful,CheckStatefulResult,CommandSequence. - Replay:
SerializeCounterexample,DeserializeCounterexample,Result.ToReplayFixture,ReplayFixtureFile,ReplayStatefulFixtureFile. - Hooks:
Hook,HookFuncs,WithHook,WithHooks. Stateful hooks:StatefulHook,StatefulHookFuncs,CommandModel.Hooks.
Pass a seed with WithSeed to reproduce a failure exactly. The Result and the failure message both record the seed. A failing Result converts to a ReplayFixture for persisting to disk.
pkg/pbtholds the public API: generators, properties, shrinking, execution.cmd/pbt-exampleis a runnable example program.
Taskfile.yml provides task fmt, task vet, task lint, task test, task test:cov (fails below 75% coverage), and task ci.
0BSD. See LICENSE.