gopter

package module
Version: v0.2.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 6, 2018 License: MIT Imports: 13 Imported by: 0

README

GOPTER

... the GOlang Property TestER Build Status codecov GoDoc

Change Log

Synopsis

Gopter tries to bring the goodness of ScalaCheck (and implicitly, the goodness of QuickCheck) to Go. It can also be seen as a more sophisticated version of the testing/quick package.

Main differences to ScalaCheck:

  • It is Go ... duh
  • ... nevertheless: Do not expect the same typesafety and elegance as in ScalaCheck.
  • For simplicity Shrink has become part of the generators. They can still be easily changed if necessary.
  • There is no Pretty ... so far gopter feels quite comfortable being ugly.
  • A generator for regex matches
  • No parallel commands ... yet?

Main differences to the testing/quick package:

  • Much tighter control over generators
  • Shrinkers, i.e. automatically find the minimum value falsifying a property
  • A generator for regex matches (already mentioned that ... but it's cool)
  • Support for stateful tests

Documentation

Current godocs:

  • gopter: Main interfaces
  • gopter/gen: All commonly used generators
  • gopter/prop: Common helpers to create properties from a condition function and specific generators
  • gopter/arbitrary: Helpers automatically combine generators for arbitrary types
  • gopter/commands: Helpers to create stateful tests based on arbitrary commands
  • gopter/convey: Helpers used by gopter inside goconvey tests

License

MIT Licence

Documentation

Overview

Package gopter contain the main interfaces of the GOlang Property TestER.

A simple property test might look like this:

func TestSqrt(t *testing.T) {
	properties := gopter.NewProperties(nil)

	properties.Property("greater one of all greater one", prop.ForAll(
		func(v float64) bool {
			return math.Sqrt(v) >= 1
		},
		gen.Float64Range(1, math.MaxFloat64),
	))

	properties.Property("squared is equal to value", prop.ForAll(
		func(v float64) bool {
			r := math.Sqrt(v)
			return math.Abs(r*r-v) < 1e-10*v
		},
		gen.Float64Range(0, math.MaxFloat64),
	))

	properties.TestingRun(t)
}

Generally a property is just a function that takes GenParameters and produces a PropResult:

type Prop func(*GenParameters) *PropResult

but usually you will use prop.ForAll, prop.ForAllNoShrink or arbitrary.ForAll. There is also the commands package, which can be helpful for stateful testing.

Example (Fizzbuzz)
package main

import (
	"errors"
	"math"
	"strconv"
	"strings"

	"github.com/leanovate/gopter"
	"github.com/leanovate/gopter/gen"
	"github.com/leanovate/gopter/prop"
)

// Fizzbuzz: See https://wikipedia.org/wiki/Fizz_buzz
func fizzbuzz(number int) (string, error) {
	if number <= 0 {
		return "", errors.New("Undefined")
	}
	switch {
	case number%15 == 0:
		return "FizzBuzz", nil
	case number%3 == 0:
		return "Fizz", nil
	case number%5 == 0:
		return "Buzz", nil
	}
	return strconv.Itoa(number), nil
}

func main() {
	properties := gopter.NewProperties(nil)

	properties.Property("Undefined for all <= 0", prop.ForAll(
		func(number int) bool {
			result, err := fizzbuzz(number)
			return err != nil && result == ""
		},
		gen.IntRange(math.MinInt32, 0),
	))

	properties.Property("Start with Fizz for all multiples of 3", prop.ForAll(
		func(i int) bool {
			result, err := fizzbuzz(i * 3)
			return err == nil && strings.HasPrefix(result, "Fizz")
		},
		gen.IntRange(1, math.MaxInt32/3),
	))

	properties.Property("End with Buzz for all multiples of 5", prop.ForAll(
		func(i int) bool {
			result, err := fizzbuzz(i * 5)
			return err == nil && strings.HasSuffix(result, "Buzz")
		},
		gen.IntRange(1, math.MaxInt32/5),
	))

	properties.Property("Int as string for all non-divisible by 3 or 5", prop.ForAll(
		func(number int) bool {
			result, err := fizzbuzz(number)
			if err != nil {
				return false
			}
			parsed, err := strconv.ParseInt(result, 10, 64)
			return err == nil && parsed == int64(number)
		},
		gen.IntRange(1, math.MaxInt32).SuchThat(func(v interface{}) bool {
			return v.(int)%3 != 0 && v.(int)%5 != 0
		}),
	))

	// When using testing.T you might just use: properties.TestingRun(t)
	properties.Run(gopter.ConsoleReporter(false))
}
Output:

+ Undefined for all <= 0: OK, passed 100 tests.
+ Start with Fizz for all multiples of 3: OK, passed 100 tests.
+ End with Buzz for all multiples of 5: OK, passed 100 tests.
+ Int as string for all non-divisible by 3 or 5: OK, passed 100 tests.
Example (Labels)

Example_labels demonstrates how labels may help, in case of more complex conditions. The output will be:

! Check spooky: Falsified after 0 passed tests.
> Labels of failing property: even result
a: 3
a_ORIGINAL (44 shrinks): 861384713
b: 0
b_ORIGINAL (1 shrinks): -642623569
package main

import (
	"github.com/leanovate/gopter"
	"github.com/leanovate/gopter/gen"
	"github.com/leanovate/gopter/prop"
)

func spookyCalculation(a, b int) int {
	if a < 0 {
		a = -a
	}
	if b < 0 {
		b = -b
	}
	return 2*b + 3*(2+(a+1)+b*(b+1))
}

// Example_labels demonstrates how labels may help, in case of more complex
// conditions.
// The output will be:
//  ! Check spooky: Falsified after 0 passed tests.
//  > Labels of failing property: even result
//  a: 3
//  a_ORIGINAL (44 shrinks): 861384713
//  b: 0
//  b_ORIGINAL (1 shrinks): -642623569
func main() {
	parameters := gopter.DefaultTestParameters()
	parameters.Rng.Seed(1234) // Just for this example to generate reproducable results
	parameters.MinSuccessfulTests = 10000

	properties := gopter.NewProperties(parameters)

	properties.Property("Check spooky", prop.ForAll(
		func(a, b int) string {
			result := spookyCalculation(a, b)
			if result < 0 {
				return "negative result"
			}
			if result%2 == 0 {
				return "even result"
			}
			return ""
		},
		gen.Int().WithLabel("a"),
		gen.Int().WithLabel("b"),
	))

	// When using testing.T you might just use: properties.TestingRun(t)
	properties.Run(gopter.ConsoleReporter(false))
}
Output:

! Check spooky: Falsified after 0 passed tests.
> Labels of failing property: even result
a: 3
a_ORIGINAL (44 shrinks): 861384713
b: 0
b_ORIGINAL (1 shrinks): -642623569
Example (Sqrt)
package main

import (
	"math"

	"github.com/leanovate/gopter"
	"github.com/leanovate/gopter/gen"
	"github.com/leanovate/gopter/prop"
)

func main() {
	parameters := gopter.DefaultTestParameters()
	parameters.Rng.Seed(1234) // Just for this example to generate reproducable results

	properties := gopter.NewProperties(parameters)

	properties.Property("greater one of all greater one", prop.ForAll(
		func(v float64) bool {
			return math.Sqrt(v) >= 1
		},
		gen.Float64().SuchThat(func(x float64) bool { return x >= 1.0 }),
	))

	properties.Property("squared is equal to value", prop.ForAll(
		func(v float64) bool {
			r := math.Sqrt(v)
			return math.Abs(r*r-v) < 1e-10*v
		},
		gen.Float64().SuchThat(func(x float64) bool { return x >= 0.0 }),
	))

	// When using testing.T you might just use: properties.TestingRun(t)
	properties.Run(gopter.ConsoleReporter(false))
}
Output:

+ greater one of all greater one: OK, passed 100 tests.
+ squared is equal to value: OK, passed 100 tests.

Index

Examples

Constants

View Source
const (
	// PropProof THe property was proved (i.e. it is known to be correct and will be always true)
	PropProof propStatus = iota
	// PropTrue The property was true this time
	PropTrue
	// PropFalse The property was false this time
	PropFalse
	// PropUndecided The property has no clear outcome this time
	PropUndecided
	// PropError The property has generated an error
	PropError
)
View Source
const (
	// TestPassed indicates that the property check has passed.
	TestPassed testStatus = iota
	// TestProved indicates that the property has been proved.
	TestProved
	// TestFailed indicates that the property check has failed.
	TestFailed
	// TestExhausted indicates that the property check has exhausted, i.e. the generators have
	// generated too many empty results.
	TestExhausted
	// TestError indicates that the property check has finished with an error.
	TestError
)

Variables

View Source
var (
	// DefaultGenParams can be used as default für *GenParameters
	DefaultGenParams = DefaultGenParameters()
)
View Source
var NoShrink = Shrink(func() (interface{}, bool) {
	return nil, false
})

NoShrink is an empty shrink.

View Source
var NoShrinker = Shrinker(func(value interface{}) Shrink {
	return NoShrink
})

NoShrinker is a shrinker for NoShrink, i.e. a Shrinker that will not shrink any values. This is the default Shrinker if none is provided.

Functions

func NewLockedSource added in v0.2.2

func NewLockedSource(seed int64) *lockedSource

NewLockedSource takes a seed and returns a new lockedSource for use with rand.New

Types

type BiMapper

type BiMapper struct {
	UpTypes    []reflect.Type
	DownTypes  []reflect.Type
	Downstream reflect.Value
	Upstream   reflect.Value
}

BiMapper is a bi-directional (or bijective) mapper of a tuple of values (up) to another tuple of values (down).

func NewBiMapper

func NewBiMapper(downstream interface{}, upstream interface{}) *BiMapper

NewBiMapper creates a BiMapper of two functions `downstream` and its inverse `upstream`. That is: The return values of `downstream` must match the parameters of `upstream` and vice versa.

func (*BiMapper) ConvertDown

func (b *BiMapper) ConvertDown(up []interface{}) []interface{}

ConvertDown calls the Downstream function on the elements of the up array and returns the results.

func (*BiMapper) ConvertUp

func (b *BiMapper) ConvertUp(down []interface{}) []interface{}

ConvertUp calls the Upstream function on the arguments in the down array and returns the results.

type Flag

type Flag struct {
	// contains filtered or unexported fields
}

Flag is a convenient helper for an atomic boolean

func (*Flag) Get

func (f *Flag) Get() bool

Get the value of the flag

func (*Flag) Set

func (f *Flag) Set()

Set the the flag

func (*Flag) Unset

func (f *Flag) Unset()

Unset the flag

type FormatedReporter

type FormatedReporter struct {
	// contains filtered or unexported fields
}

FormatedReporter reports test results in a human readable manager.

func (*FormatedReporter) ReportTestResult

func (r *FormatedReporter) ReportTestResult(propName string, result *TestResult)

ReportTestResult reports a single property result

type Gen

type Gen func(*GenParameters) *GenResult

Gen generator of arbitrary values. Usually properties are checked by verifing a condition holds true for arbitrary input parameters generated by a Gen.

IMPORTANT: Even though a generator is supposed to generate random values, it should do this in a reproducable way. Therefore a generator has to create the same result for the same GenParameters, i.e. ensure that you just use the RNG provided by GenParameters and no external one. If you just plug generators together you do not have to worry about this.

func CombineGens

func CombineGens(gens ...Gen) Gen

CombineGens creates a generators from a list of generators. The result type will be a []interface{} containing the generated values of each generators in the list. Note: The combined generator will not have a sieve or shrinker.

func DeriveGen

func DeriveGen(downstream interface{}, upstream interface{}, gens ...Gen) Gen

DeriveGen derives a generator with shrinkers from a sequence of other generators mapped by a bijective function (BiMapper)

func (Gen) FlatMap

func (g Gen) FlatMap(f func(interface{}) Gen, resultType reflect.Type) Gen

FlatMap creates a derived generator by passing a generated value to a function which itself creates a generator.

func (Gen) Map

func (g Gen) Map(f interface{}) Gen

Map creates a derived generators by mapping all generatored values with a given function. f: has to be a function with one parameter (matching the generated value) and a single return. Note: The derived generator will not have a sieve or shrinker. Note: The mapping function may have a second parameter "*GenParameters" Note: The first parameter of the mapping function and its return may be a *GenResult (this makes MapResult obsolete)

func (Gen) MapResult

func (g Gen) MapResult(f func(*GenResult) *GenResult) Gen

MapResult creates a derived generator by mapping the GenResult directly. Contrary to `Map` and `FlatMap` this also allow the conversion of shrinkers and sieves, but implementation is more cumbersome. Deprecation note: Map now has the same functionality

func (Gen) Sample

func (g Gen) Sample() (interface{}, bool)

Sample generate a sample value. Depending on the state of the RNG the generate might fail to provide a sample

func (Gen) SuchThat

func (g Gen) SuchThat(f interface{}) Gen

SuchThat creates a derived generator by adding a sieve. f: has to be a function with one parameter (matching the generated value) returning a bool. All generated values are expected to satisfy

f(value) == true.

Use this care, if the sieve to to fine the generator will have many misses which results in an undecided property.

func (Gen) WithLabel

func (g Gen) WithLabel(label string) Gen

WithLabel adds a label to a generated value. Labels are usually used for reporting for the arguments of a property check.

func (Gen) WithShrinker

func (g Gen) WithShrinker(shrinker Shrinker) Gen

WithShrinker creates a derived generator with a specific shrinker

type GenParameters

type GenParameters struct {
	MinSize        int
	MaxSize        int
	MaxShrinkCount int
	Rng            *rand.Rand
}

GenParameters encapsulates the parameters for all generators.

func DefaultGenParameters

func DefaultGenParameters() *GenParameters

DefaultGenParameters creates default GenParameters.

func (*GenParameters) CloneWithSeed

func (p *GenParameters) CloneWithSeed(seed int64) *GenParameters

CloneWithSeed clone the current parameters with a new seed. This is useful to create subsections that can rerun (provided you keep the seed)

func (*GenParameters) NextBool

func (p *GenParameters) NextBool() bool

NextBool create a random boolean using the underlying Rng.

func (*GenParameters) NextInt64

func (p *GenParameters) NextInt64() int64

NextInt64 create a random int64 using the underlying Rng.

func (*GenParameters) NextUint64

func (p *GenParameters) NextUint64() uint64

NextUint64 create a random uint64 using the underlying Rng.

func (*GenParameters) WithSize

func (p *GenParameters) WithSize(size int) *GenParameters

WithSize modifies the size parameter. The size parameter defines an upper bound for the size of generated slices or strings.

type GenResult

type GenResult struct {
	Labels     []string
	Shrinker   Shrinker
	ResultType reflect.Type
	Result     interface{}
	Sieve      func(interface{}) bool
}

GenResult contains the result of a generator.

func NewEmptyResult

func NewEmptyResult(resultType reflect.Type) *GenResult

NewEmptyResult creates an empty generator result. Unless the sieve does not explicitly allow it, empty (i.e. nil-valued) results are considered invalid.

func NewGenResult

func NewGenResult(result interface{}, shrinker Shrinker) *GenResult

NewGenResult creates a new generator result from for a concrete value and shrinker. Note: The concrete value "result" not be nil

func (*GenResult) Retrieve

func (r *GenResult) Retrieve() (interface{}, bool)

Retrieve gets the concrete generator result. If the result is invalid or does not pass the sieve there is no concrete value and the property using the generator should be undecided.

func (*GenResult) RetrieveAsValue

func (r *GenResult) RetrieveAsValue() (reflect.Value, bool)

RetrieveAsValue get the concrete generator result as reflect value. If the result is invalid or does not pass the sieve there is no concrete value and the property using the generator should be undecided.

type Prop

type Prop func(*GenParameters) *PropResult

Prop represent some kind of property that (drums please) can and should be checked

func SaveProp

func SaveProp(prop Prop) Prop

SaveProp creates s save property by handling all panics from an inner property

func (Prop) Check

func (prop Prop) Check(parameters *TestParameters) *TestResult

Check the property using specific parameters

type PropArg

type PropArg struct {
	Arg     interface{}
	OrigArg interface{}
	Label   string
	Shrinks int
}

PropArg contains information about the specific values for a certain property check. This is mostly used for reporting when a property has falsified.

func NewPropArg

func NewPropArg(genResult *GenResult, shrinks int, value, origValue interface{}) *PropArg

NewPropArg creates a new PropArg.

func (*PropArg) String

func (p *PropArg) String() string

type PropArgs

type PropArgs []*PropArg

PropArgs is a list of PropArg.

type PropResult

type PropResult struct {
	Status propStatus
	Error  error
	Args   []*PropArg
	Labels []string
}

PropResult contains the result of a property

func NewPropResult

func NewPropResult(success bool, label string) *PropResult

NewPropResult create a PropResult with label

func (*PropResult) AddArgs

func (r *PropResult) AddArgs(args ...*PropArg) *PropResult

AddArgs add argument descriptors to the PropResult for reporting

func (*PropResult) And

func (r *PropResult) And(other *PropResult) *PropResult

And combines two PropResult by an and operation. The resulting PropResult will be only true if both PropResults are true.

func (*PropResult) Success

func (r *PropResult) Success() bool

Success checks if the result was successful

func (*PropResult) WithArgs

func (r *PropResult) WithArgs(args []*PropArg) *PropResult

WithArgs sets argument descriptors to the PropResult for reporting

type Properties

type Properties struct {
	// contains filtered or unexported fields
}

Properties is a collection of properties that should be checked in a test

func NewProperties

func NewProperties(parameters *TestParameters) *Properties

NewProperties create new Properties with given test parameters. If parameters is nil default test parameters will be used

func (*Properties) Property

func (p *Properties) Property(name string, prop Prop)

Property add/defines a property in a test.

func (*Properties) Run

func (p *Properties) Run(reporter Reporter) bool

Run checks all definied propertiesand reports the result

func (*Properties) TestingRun

func (p *Properties) TestingRun(t *testing.T, opts ...interface{})

TestingRun checks all definied properties with a testing.T context. This the preferred wait to run property tests as part of a go unit test.

type Reporter

type Reporter interface {
	// ReportTestResult reports a single property result
	ReportTestResult(propName string, result *TestResult)
}

Reporter is a simple interface to report/format the results of a property check.

func ConsoleReporter

func ConsoleReporter(verbose bool) Reporter

ConsoleReporter creates a FormatedReporter writing to the console (i.e. stdout)

func NewFormatedReporter

func NewFormatedReporter(verbose bool, width int, output io.Writer) Reporter

NewFormatedReporter create a new formated reporter verbose toggles verbose output of the property results width is the maximal width per line output is the writer were the report will be written to

type Shrink

type Shrink func() (interface{}, bool)

Shrink is a stream of shrinked down values. Once the result of a shrink is false, it is considered to be exhausted. Important notes for implementors:

* Ensure that the returned stream is finite, even though shrinking will
  eventually be aborted, infinite streams may result in very slow running
  test.
* Ensure that modifications to the returned value will not affect the
  internal state of your Shrink. If in doubt return by value not by reference

func ConcatShrinks

func ConcatShrinks(shrinks ...Shrink) Shrink

ConcatShrinks concats an array of shrinks to a single shrinks

func (Shrink) All

func (s Shrink) All() []interface{}

All collects all shrinks as a slice. Use with care as this might create large results depending on the complexity of the shrink

func (Shrink) Filter

func (s Shrink) Filter(condition func(interface{}) bool) Shrink

Filter creates a shrink filtered by a condition

func (Shrink) Interleave

func (s Shrink) Interleave(other Shrink) Shrink

Interleave this shrink with another Both shrinks are expected to produce the same result

func (Shrink) Map

func (s Shrink) Map(f interface{}) Shrink

Map creates a shrink by applying a converter to each element of a shrink. f: has to be a function with one parameter (matching the generated value) and a single return.

type Shrinker

type Shrinker func(value interface{}) Shrink

Shrinker creates a shrink for a given value

func CombineShrinker

func CombineShrinker(shrinkers ...Shrinker) Shrinker

CombineShrinker create a shrinker by combining a list of shrinkers. The resulting shrinker will shrink an []interface{} where each element will be shrinked by the corresonding shrinker in 'shrinkers'. This method is implicitly used by CombineGens.

type TestParameters

type TestParameters struct {
	MinSuccessfulTests int
	// MinSize is an (inclusive) lower limit on the size of the parameters
	MinSize int
	// MaxSize is an (exclusive) upper limit on the size of the parameters
	MaxSize         int
	MaxShrinkCount  int
	Seed            int64
	Rng             *rand.Rand
	Workers         int
	MaxDiscardRatio float64
}

TestParameters to run property tests

func DefaultTestParameters

func DefaultTestParameters() *TestParameters

DefaultTestParameterWithSeeds creates reasonable default Parameters for most cases with an undefined RNG-seed

func DefaultTestParametersWithSeed added in v0.2.2

func DefaultTestParametersWithSeed(seed int64) *TestParameters

DefaultTestParameterWithSeeds creates reasonable default Parameters for most cases based on a fixed RNG-seed

type TestResult

type TestResult struct {
	Status    testStatus
	Succeeded int
	Discarded int
	Labels    []string
	Error     error
	Args      PropArgs
	Time      time.Duration
}

TestResult contains the result of a property property check.

func (*TestResult) Passed

func (r *TestResult) Passed() bool

Passed checks if the check has passed

Directories

Path Synopsis
Package arbitrary contains helpers to create contexts of arbitrary values, i.e.
Package arbitrary contains helpers to create contexts of arbitrary values, i.e.
Package commands conains helpers to create stateful tests based on commands.
Package commands conains helpers to create stateful tests based on commands.
Package convey contains special assertion that come handy when using groper properties with goconvey.
Package convey contains special assertion that come handy when using groper properties with goconvey.
Package gen contains all commonly used generators and shrinkers.
Package gen contains all commonly used generators and shrinkers.
Package prop contains the most common implementations of a gopter.Prop.
Package prop contains the most common implementations of a gopter.Prop.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
t or T : Toggle theme light dark auto
y or Y : Canonical URL