rapid

package module
v0.2.2 Latest Latest
Warning

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

Go to latest
Published: Jan 17, 2020 License: MPL-2.0 Imports: 27 Imported by: 0

README

Rapid GoDoc

Rapid is a Go library for property-based testing.

Rapid checks that properties you define hold for a large number of automatically generated test cases. If a failure is found, rapid automatically minimizes the failing test case before presenting it.

Property-based testing emphasizes thinking about high level properties the program should satisfy rather than coming up with a list of individual examples of desired behavior (test cases). This results in concise and powerful tests that are a pleasure to write.

Design and implementation of rapid are heavily inspired by Hypothesis, which is itself a descendant of QuickCheck.

Features

  • Idiomatic Go API
  • Automatic minimization of failing test cases
  • No dependencies outside of the Go standard library
Planned features
  • Automatic persistence of failing test cases

Examples

Here is what a trivial test using rapid looks like:

func TestParseValidIPv4(t *testing.T) {
	const ipv4re = `(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])` +
		`\.(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])` +
		`\.(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])` +
		`\.(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])`

	rapid.Check(t, func(t *rapid.T) {
		addr := rapid.StringMatching(ipv4re).Draw(t, "addr").(string)
		ip := net.ParseIP(addr)
		if ip == nil || ip.String() != addr {
			t.Fatalf("parsed %q into %v", addr, ip)
		}
	})
}

You can play around with the IPv4 regexp to see what happens when it is generating invalid addresses (or try to pass the test with your own ParseIP implementation). More complete function (source code, playground) and state machine (source code, playground) example tests are provided. They both fail. Making them pass is a good way to get first real experience of working with rapid.

Usage

Just run go test as usual, it will pick up also all rapid tests.

There are a number of optional flags to influence rapid behavior, run go test -args -h and look at the flags with the -rapid. prefix. You can then pass such flags as usual. For example:

go test -rapid.checks=1000

Comparison

Rapid aims to bring to Go the power and convenience Hypothesis brings to Python.

Compared to gopter, rapid:

  • has a much simpler API (queue test in rapid vs gopter)
  • does not require any user code to minimize failing test cases
  • generates biased data to explore "small" values and edge cases more thoroughly (inspired by SmallCheck)
  • enables interactive tests by allowing data generation and test code to arbitrarily intermix

Compared to testing/quick, rapid:

  • provides much more control over test case generation
  • supports state machine ("stateful" or "model-based") testing
  • automatically minimizes any failing test case
  • has to settle for rapid.Check being the main exported function instead of much more stylish quick.Check

Status

Rapid is alpha software. Important pieces of functionality are missing; API breakage and bugs should be expected.

If rapid fails to find a bug you believe it should, or the failing test case that rapid reports does not look like a minimal one, please open an issue.

License

Rapid is licensed under the Mozilla Public License version 2.0.

Documentation

Overview

Package rapid implements utilities for property-based testing.

Rapid checks that properties you define hold for a large number of automatically generated test cases. If a failure is found, rapid fails the current test and presents an automatically minimized version of the failing test case.

Please note that rapid is alpha software; the documentation is very incomplete, unclear and probably full of grammatical errors.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func Check

func Check(t *testing.T, prop func(*T))

Check fails the current test if rapid can find a test case which falsifies prop.

Property is falsified in case of a panic or a call to (*T).Fatalf, (*T).Fatal, (*T).Errorf, (*T).Error, (*T).FailNow or (*T).Fail.

func MakeCheck

func MakeCheck(prop func(*T)) func(*testing.T)

MakeCheck is a convenience function for defining subtests suitable for (*testing.T).Run. It allows you to write this:

t.Run("subtest name", rapid.MakeCheck(func(t *rapid.T) {
    // test code
}))

instead of this:

t.Run("subtest name", func(t *testing.T) {
    rapid.Check(t, func(t *rapid.T) {
        // test code
    })
})

func StateMachine

func StateMachine(i interface{}) func(*T)

StateMachine is a convenience function for defining "state machine" tests, to be run by Check or MakeCheck.

State machine test is a pattern for testing stateful systems that looks like this:

s := new(STATE_MACHINE_DEFINITION_TYPE)
RUN_RANDOM_INITIALIZER_ACTION(s)
defer CLEANUP(s)
CHECK_INVARIANTS(s)
for {
    RUN_RANDOM_ACTION(s)
    CHECK_INVARIANTS(s)
}

StateMachine synthesizes such test from the type of its argument, which must be a pointer to an arbitrary state machine definition type, whose public methods are treated as follows:

- Init(t *rapid.T), InitAnySuffixHere(t *rapid.T), if present, are used as "initializer" actions; exactly one is ran at the beginning of each test case;

- Cleanup(), if present, is called at the end of each test case;

- Check(t *rapid.T), if present, is ran after every action;

- All other public methods should have a form ActionName(t *rapid.T) and are used as possible actions. At least one action has to be specified.

Types

type Generator

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

func ArrayOf added in v0.2.0

func ArrayOf(count int, elem *Generator) *Generator

func Boolean added in v0.2.0

func Boolean() *Generator

func Byte added in v0.2.0

func Byte() *Generator

func ByteMax added in v0.2.0

func ByteMax(max byte) *Generator

func ByteMin added in v0.2.0

func ByteMin(min byte) *Generator

func ByteRange added in v0.2.0

func ByteRange(min byte, max byte) *Generator

func Custom

func Custom(fn interface{}) *Generator

func Float32 added in v0.2.0

func Float32() *Generator

func Float32Max added in v0.2.0

func Float32Max(max float32) *Generator

func Float32Min added in v0.2.0

func Float32Min(min float32) *Generator

func Float32Range added in v0.2.0

func Float32Range(min float32, max float32) *Generator

func Float64 added in v0.2.0

func Float64() *Generator

func Float64Max added in v0.2.0

func Float64Max(max float64) *Generator

func Float64Min added in v0.2.0

func Float64Min(min float64) *Generator

func Float64Range added in v0.2.0

func Float64Range(min float64, max float64) *Generator

func Int added in v0.2.0

func Int() *Generator

func Int16 added in v0.2.0

func Int16() *Generator

func Int16Max added in v0.2.0

func Int16Max(max int16) *Generator

func Int16Min added in v0.2.0

func Int16Min(min int16) *Generator

func Int16Range added in v0.2.0

func Int16Range(min int16, max int16) *Generator

func Int32 added in v0.2.0

func Int32() *Generator

func Int32Max added in v0.2.0

func Int32Max(max int32) *Generator

func Int32Min added in v0.2.0

func Int32Min(min int32) *Generator

func Int32Range added in v0.2.0

func Int32Range(min int32, max int32) *Generator

func Int64 added in v0.2.0

func Int64() *Generator

func Int64Max added in v0.2.0

func Int64Max(max int64) *Generator

func Int64Min added in v0.2.0

func Int64Min(min int64) *Generator

func Int64Range added in v0.2.0

func Int64Range(min int64, max int64) *Generator

func Int8 added in v0.2.0

func Int8() *Generator

func Int8Max added in v0.2.0

func Int8Max(max int8) *Generator

func Int8Min added in v0.2.0

func Int8Min(min int8) *Generator

func Int8Range added in v0.2.0

func Int8Range(min int8, max int8) *Generator

func IntMax added in v0.2.0

func IntMax(max int) *Generator

func IntMin added in v0.2.0

func IntMin(min int) *Generator

func IntRange added in v0.2.0

func IntRange(min int, max int) *Generator

func Just

func Just(val interface{}) *Generator

func MapOf added in v0.2.0

func MapOf(key *Generator, val *Generator) *Generator

func MapOfN added in v0.2.0

func MapOfN(key *Generator, val *Generator, minLen int, maxLen int) *Generator

func MapOfNValues added in v0.2.0

func MapOfNValues(val *Generator, minLen int, maxLen int, keyFn interface{}) *Generator

func MapOfValues added in v0.2.0

func MapOfValues(val *Generator, keyFn interface{}) *Generator

func OneOf

func OneOf(gens ...*Generator) *Generator

func Ptr added in v0.2.0

func Ptr(elem *Generator, allowNil bool) *Generator

func Rune added in v0.2.0

func Rune() *Generator

func RuneFrom added in v0.2.0

func RuneFrom(runes []rune, tables ...*unicode.RangeTable) *Generator

func SampledFrom

func SampledFrom(slice interface{}) *Generator

func SliceOf added in v0.2.0

func SliceOf(elem *Generator) *Generator

func SliceOfBytesMatching added in v0.2.0

func SliceOfBytesMatching(expr string) *Generator

func SliceOfDistinct added in v0.2.0

func SliceOfDistinct(elem *Generator, keyFn interface{}) *Generator

func SliceOfN added in v0.2.0

func SliceOfN(elem *Generator, minLen int, maxLen int) *Generator

func SliceOfNDistinct added in v0.2.0

func SliceOfNDistinct(elem *Generator, minLen int, maxLen int, keyFn interface{}) *Generator

func String added in v0.2.0

func String() *Generator

func StringMatching added in v0.2.0

func StringMatching(expr string) *Generator

func StringN added in v0.2.0

func StringN(minRunes int, maxRunes int, maxLen int) *Generator

func StringOf added in v0.2.0

func StringOf(elem *Generator) *Generator

func StringOfN added in v0.2.0

func StringOfN(elem *Generator, minElems int, maxElems int, maxLen int) *Generator

func Uint added in v0.2.0

func Uint() *Generator

func Uint16 added in v0.2.0

func Uint16() *Generator

func Uint16Max added in v0.2.0

func Uint16Max(max uint16) *Generator

func Uint16Min added in v0.2.0

func Uint16Min(min uint16) *Generator

func Uint16Range added in v0.2.0

func Uint16Range(min uint16, max uint16) *Generator

func Uint32 added in v0.2.0

func Uint32() *Generator

func Uint32Max added in v0.2.0

func Uint32Max(max uint32) *Generator

func Uint32Min added in v0.2.0

func Uint32Min(min uint32) *Generator

func Uint32Range added in v0.2.0

func Uint32Range(min uint32, max uint32) *Generator

func Uint64 added in v0.2.0

func Uint64() *Generator

func Uint64Max added in v0.2.0

func Uint64Max(max uint64) *Generator

func Uint64Min added in v0.2.0

func Uint64Min(min uint64) *Generator

func Uint64Range added in v0.2.0

func Uint64Range(min uint64, max uint64) *Generator

func Uint8 added in v0.2.0

func Uint8() *Generator

func Uint8Max added in v0.2.0

func Uint8Max(max uint8) *Generator

func Uint8Min added in v0.2.0

func Uint8Min(min uint8) *Generator

func Uint8Range added in v0.2.0

func Uint8Range(min uint8, max uint8) *Generator

func UintMax added in v0.2.0

func UintMax(max uint) *Generator

func UintMin added in v0.2.0

func UintMin(min uint) *Generator

func UintRange added in v0.2.0

func UintRange(min uint, max uint) *Generator

func Uintptr added in v0.2.0

func Uintptr() *Generator

func UintptrMax added in v0.2.0

func UintptrMax(max uintptr) *Generator

func UintptrMin added in v0.2.0

func UintptrMin(min uintptr) *Generator

func UintptrRange added in v0.2.0

func UintptrRange(min uintptr, max uintptr) *Generator

func (*Generator) Draw added in v0.2.0

func (g *Generator) Draw(t *T, label string) interface{}

func (*Generator) Example

func (g *Generator) Example(seed ...uint64) (interface{}, int, error)

func (*Generator) Filter

func (g *Generator) Filter(fn interface{}) *Generator

func (*Generator) Map

func (g *Generator) Map(fn interface{}) *Generator

func (*Generator) String

func (g *Generator) String() string

type T

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

func (*T) Error

func (t *T) Error(args ...interface{})

func (*T) Errorf

func (t *T) Errorf(format string, args ...interface{})

func (*T) Fail

func (t *T) Fail()

func (*T) FailNow

func (t *T) FailNow()

func (*T) Failed

func (t *T) Failed() bool

func (*T) Fatal

func (t *T) Fatal(args ...interface{})

func (*T) Fatalf

func (t *T) Fatalf(format string, args ...interface{})

func (*T) Log

func (t *T) Log(args ...interface{})

func (*T) Logf

func (t *T) Logf(format string, args ...interface{})

func (*T) Skip added in v0.2.0

func (t *T) Skip(args ...interface{})

func (*T) SkipNow added in v0.2.0

func (t *T) SkipNow()

SkipNow marks the current test case as invalid. If too many test cases are skipped, rapid will mark the test as failing due to inability to generate enough valid test cases.

Prefer Filter to SkipNow, and prefer generators that always produce valid test cases to Filter.

func (*T) Skipf added in v0.2.0

func (t *T) Skipf(format string, args ...interface{})

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL