assert

package
v0.9.0 Latest Latest
Warning

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

Go to latest
Published: Mar 26, 2023 License: MIT Imports: 11 Imported by: 60

Documentation

Overview

Package assert includes runtime assertion helpers both for normal execution as well as a assertion package for Go's testing. What makes solution unique is its capable to support both modes with same API. Only thing you need to do is to add following two lines at the beginning of your unit tests:

func TestInvite(t *testing.T) {
     assert.PushTester(t) // push testing variable t beginning of any test
     defer assert.PopTester()

     alice.Node = root1.Invite(alice.Node, root1.Key, alice.PubKey, 1)
     assert.Equal(alice.Len(), 1) // assert anything normally

Merge Runtime And Test Assertions

Especially powerful feature is that even if some assertion violation happens during the execution of called functions not the test it self. See the above example. If assertion failure happens inside of the Invite() function instead of the actual Test function, TestInvite, it's still reported correctly as normal test failure when TestInvite is executed. It doesn't matter how deep the recursion is, or if parallel test runs are performed. It works just as you hoped.

Instead of mocking or other mechanisms we can integrate our preconditions and raise up quality of our software.

"Assertions are active comments"

The package offers a convenient way to set preconditions to code which allow us detect programming errors and API violations faster. Still allowing production-time error handling if needed. And everything is automatic. You can set proper asserter according to flag or environment variable. This allows developer, operator and every-day user share the exact same binary but get the error messages and diagnostic they need.

// add formatted caller info for normal errors coming from assertions
assert.SetDefaultAsserter(AsserterToError | AsserterFormattedCallerInfo)

Please see the code examples for more information.

Note. assert.That's performance has been (<go 1.20) equal to the if-statement. Most of the generics-based versions are almost as fast. If your algorithm is performance-critical please run `make bench` in the err2 repo and decide case by case.

Note. Format string functions need to be own instances because of Go's vet and test tool integration.

Index

Examples

Constants

This section is empty.

Variables

View Source
var (
	// P is a production Asserter that sets panic objects to errors which
	// allows err2 handlers to catch them.
	P = AsserterToError

	// D is a development Asserter that sets panic objects to strings that
	// doesn't by caught by err2 handlers.
	D = AsserterDebug
)

Functions

func CNotNil added in v0.8.0

func CNotNil[T any](c chan T, a ...any)

CNotNil asserts that the channel is not nil. If it is it panics/errors (default Asserter) with the given message.

Example
package main

import (
	"fmt"

	"github.com/lainio/err2"
	"github.com/lainio/err2/assert"
)

func main() {
	sample := func(c chan byte) (err error) {
		defer err2.Handle(&err, "sample")

		assert.CNotNil(c)
		return err
	}
	var c chan byte
	err := sample(c)
	fmt.Printf("%v", err)
}
Output:

sample: assert_test.go:90 ExampleCNotNil.func1(): assertion violation: channel shouldn't be nil

func DeepEqual added in v0.8.9

func DeepEqual(val, want any, a ...any)

DeepEqual asserts that the (whatever) values are equal. If not it panics/errors (current Asserter) with the given message.

func Empty added in v0.8.9

func Empty(obj string, a ...any)

Empty asserts that the string is empty. If it is NOT, it panics/errors (current Asserter) with the given message.

func Equal added in v0.8.0

func Equal[T comparable](val, want T, a ...any)

Equal asserts that the values are equal. If not it panics/errors (current Asserter) with the given message.

Example
package main

import (
	"fmt"

	"github.com/lainio/err2"
	"github.com/lainio/err2/assert"
)

func main() {
	sample := func(b []byte) (err error) {
		defer err2.Handle(&err, "sample")

		assert.Equal(len(b), 3)
		return err
	}
	err := sample([]byte{1, 2})
	fmt.Printf("%v", err)
}
Output:

sample: assert_test.go:116 ExampleEqual.func1(): assertion violation: got 2, want 3

func Error added in v0.8.8

func Error(err error, a ...any)

Error asserts that the err is not nil. If it is it panics with the given formatting string. Thanks to inlining, the performance penalty is equal to a single 'if-statement' that is almost nothing.

func INil added in v0.8.10

func INil(i any, a ...any)

INil asserts that the interface value IS nil. If it is it panics/errors (default Asserter) with the given message.

func INotNil added in v0.8.9

func INotNil(i any, a ...any)

INotNil asserts that the interface value is NOT nil. If it is it panics/errors (default Asserter) with the given message.

func Len added in v0.8.14

func Len(obj string, length int, a ...any)

Len asserts that the length of the string is equal to the given. If not it panics/errors (current Asserter) with the given message. Note! This is reasonably fast but not as fast as 'That' because of lacking inlining for the current implementation of Go's type parametric functions.

func MKeyExists added in v0.8.10

func MKeyExists[T comparable, U any](obj map[T]U, key T, a ...any) (val U)

MKeyExists asserts that the map key exists. If not it panics/errors (current Asserter) with the given message.

Example
package main

import (
	"fmt"

	"github.com/lainio/err2"
	"github.com/lainio/err2/assert"
)

func main() {
	sample := func(b string) (err error) {
		defer err2.Handle(&err, "sample")

		m := map[string]string{
			"1": "one",
		}
		v := assert.MKeyExists(m, "1")
		assert.Equal(v, "one")
		_ = assert.MKeyExists(m, b)
		return err
	}
	err := sample("2")
	fmt.Printf("%v", err)
}
Output:

sample: assert_test.go:211 ExampleMKeyExists.func1(): assertion violation: key '2' doesn't exist

func MLen added in v0.8.0

func MLen[T comparable, U any](obj map[T]U, length int, a ...any)

MLen asserts that the length of the map is equal to the given. If not it panics/errors (current Asserter) with the given message. Note! This is reasonably fast but not as fast as 'That' because of lacking inlining for the current implementation of Go's type parametric functions.

func MNotEmpty added in v0.8.3

func MNotEmpty[T comparable, U any](obj map[T]U, a ...any)

MNotEmpty asserts that the map is not empty. If it is, it panics/errors (current Asserter) with the given message. Note! This is reasonably fast but not as fast as 'That' because of lacking inlining for the current implementation of Go's type parametric functions.

func MNotNil added in v0.8.0

func MNotNil[T comparable, U any](m map[T]U, a ...any)

MNotNil asserts that the map is not nil. If it is it panics/errors (default Asserter) with the given message.

Example
package main

import (
	"fmt"

	"github.com/lainio/err2"
	"github.com/lainio/err2/assert"
)

func main() {
	sample := func(b map[string]byte) (err error) {
		defer err2.Handle(&err, "sample")

		assert.MNotNil(b)
		return err
	}
	var b map[string]byte
	err := sample(b)
	fmt.Printf("%v", err)
}
Output:

sample: assert_test.go:77 ExampleMNotNil.func1(): assertion violation: map shouldn't be nil

func Nil added in v0.8.14

func Nil[T any](p *T, a ...any)

Nil asserts that the pointer IS nil. If it is not it panics/errors (default Asserter) with the given message.

func NoError added in v0.8.8

func NoError(err error, a ...any)

NoError asserts that the error is nil. If is not it panics with the given formatting string. Thanks to inlining, the performance penalty is equal to a single 'if-statement' that is almost nothing.

func NotDeepEqual added in v0.8.10

func NotDeepEqual(val, want any, a ...any)

NotDeepEqual asserts that the (whatever) values are equal. If not it panics/errors (current Asserter) with the given message. NOTE, it uses reflect.DeepEqual which means that also the types must be the same:

assert.DeepEqual(pubKey, ed25519.PublicKey(pubKeyBytes))

func NotEmpty added in v0.8.3

func NotEmpty(obj string, a ...any)

NotEmpty asserts that the string is not empty. If it is, it panics/errors (current Asserter) with the given message.

Example
package main

import (
	"fmt"

	"github.com/lainio/err2"
	"github.com/lainio/err2/assert"
)

func main() {
	sample := func(b string) (err error) {
		defer err2.Handle(&err, "sample")

		assert.Empty(b)
		assert.NotEmpty(b)
		return err
	}
	err := sample("")
	fmt.Printf("%v", err)
}
Output:

sample: assert_test.go:194 ExampleNotEmpty.func1(): assertion violation: string shouldn't be empty

func NotEqual added in v0.8.0

func NotEqual[T comparable](val, want T, a ...any)

NotEqual asserts that the values aren't equal. If they are it panics/errors (current Asserter) with the given message.

func NotImplemented added in v0.8.3

func NotImplemented(a ...any)

NotImplemented always panics with 'not implemented' assertion message.

func NotNil added in v0.8.0

func NotNil[T any](p *T, a ...any)

NotNil asserts that the pointer IS NOT nil. If it is it panics/errors (default Asserter) with the given message.

Example
package main

import (
	"fmt"

	"github.com/lainio/err2"
	"github.com/lainio/err2/assert"
)

func main() {
	sample := func(b *byte) (err error) {
		defer err2.Handle(&err, "sample")

		assert.NotNil(b)
		return err
	}
	var b *byte
	err := sample(b)
	fmt.Printf("%v", err)
}
Output:

sample: assert_test.go:64 ExampleNotNil.func1(): assertion violation: pointer shouldn't be nil

func NotZero added in v0.9.0

func NotZero[T Number](val T, a ...any)

NotZero asserts that the value != 0. If it is not it panics with the given formatting string. Thanks to inlining, the performance penalty is equal to a single 'if-statement' that is almost nothing.

func PopTester added in v0.8.8

func PopTester()

PopTester pops the testing context reference from the memory. This isn't totally necessary, but if you want play by book, please do it. Usually done by defer after PushTester.

for _, tt := range tests {
	t.Run(tt.name, func(t *testing.T) {
		assert.PushTester(t) // <- important!
		defer assert.PopTester() // <- for good girls and not so bad boys
		...
		assert.That(something, "test won't work")
	})
}

func PushTester added in v0.8.8

func PushTester(t testing.TB)

PushTester sets the current testing context for default asserter. This must be called at the beginning of every test.

for _, tt := range tests {
	t.Run(tt.name, func(t *testing.T) {
		assert.PushTester(t) // <- IMPORTANT!
		defer assert.PopTester()
		...
		assert.That(something, "test won't work")
	})
}

func SLen added in v0.8.0

func SLen[T any](obj []T, length int, a ...any)

SLen asserts that the length of the slice is equal to the given. If not it panics/errors (current Asserter) with the given message. Note! This is reasonably fast but not as fast as 'That' because of lacking inlining for the current implementation of Go's type parametric functions.

Example
package main

import (
	"fmt"

	"github.com/lainio/err2"
	"github.com/lainio/err2/assert"
)

func main() {
	sample := func(b []byte) (err error) {
		defer err2.Handle(&err, "sample")

		assert.SLen(b, 3)
		return err
	}
	err := sample([]byte{1, 2})
	fmt.Printf("%v", err)
}
Output:

sample: assert_test.go:128 ExampleSLen.func1(): assertion violation: got 2, want 3

func SNil added in v0.8.8

func SNil[T any](s []T, a ...any)

SNil asserts that the slice IS nil. If it is it panics/errors (default Asserter) with the given message.

func SNotEmpty added in v0.8.3

func SNotEmpty[T any](obj []T, a ...any)

SNotEmpty asserts that the slice is not empty. If it is, it panics/errors (current Asserter) with the given message. Note! This is reasonably fast but not as fast as 'That' because of lacking inlining for the current implementation of Go's type parametric functions.

Example
package main

import (
	"fmt"

	"github.com/lainio/err2"
	"github.com/lainio/err2/assert"
)

func main() {
	sample := func(b []byte) (err error) {
		defer err2.Handle(&err, "sample")

		assert.SNotEmpty(b)
		return err
	}
	err := sample([]byte{})
	fmt.Printf("%v", err)
}
Output:

sample: assert_test.go:181 ExampleSNotEmpty.func1(): assertion violation: slice shouldn't be empty

func SNotNil added in v0.8.0

func SNotNil[T any](s []T, a ...any)

SNotNil asserts that the slice is not nil. If it is it panics/errors (default Asserter) with the given message.

Example
package main

import (
	"fmt"

	"github.com/lainio/err2"
	"github.com/lainio/err2/assert"
)

func main() {
	sample := func(b []byte) (err error) {
		defer err2.Handle(&err, "sample")

		assert.SNotNil(b)
		return err
	}
	var b []byte
	err := sample(b)
	fmt.Printf("%v", err)
}
Output:

sample: assert_test.go:103 ExampleSNotNil.func1(): assertion violation: slice shouldn't be nil

func That added in v0.8.0

func That(term bool, a ...any)

That asserts that the term is true. If not it panics with the given formatting string. Thanks to inlining, the performance penalty is equal to a single 'if-statement' that is almost nothing.

func ThatNot added in v0.8.8

func ThatNot(term bool, a ...any)

ThatNot asserts that the term is NOT true. If is it panics with the given formatting string. Thanks to inlining, the performance penalty is equal to a single 'if-statement' that is almost nothing.

func Zero added in v0.8.14

func Zero[T Number](val T, a ...any)

Zero asserts that the value is 0. If it is not it panics with the given formatting string. Thanks to inlining, the performance penalty is equal to a single 'if-statement' that is almost nothing.

Example
package main

import (
	"fmt"

	"github.com/lainio/err2"
	"github.com/lainio/err2/assert"
)

func main() {
	sample := func(b int8) (err error) {
		defer err2.Handle(&err, "sample")

		assert.Zero(b)
		return err
	}
	var b int8 = 1 // we want sample to assert the violation.
	err := sample(b)
	fmt.Printf("%v", err)
}
Output:

sample: assert_test.go:223 ExampleZero.func1(): assertion violation: got 1, want (== 0)

Types

type Asserter

type Asserter uint32

Asserter is type for asserter object guided by its flags.

const (
	// AsserterDebug is the default mode where all asserts are treaded as
	// panics
	AsserterDebug Asserter = 0

	// AsserterToError is Asserter flag to guide asserter to use Go's error
	// type for panics.
	AsserterToError Asserter = 1 << iota

	// AsserterStackTrace is Asserter flag to print call stack to stdout.
	AsserterStackTrace

	// AsserterCallerInfo is an asserter flag to add info of the function
	// asserting. It includes filename, line number and function name.
	AsserterCallerInfo

	// AsserterFormattedCallerInfo is an asserter flag to add info of the function
	// asserting. It includes filename, line number and function name in
	// multi-line formatted string output.
	AsserterFormattedCallerInfo

	// AsserterUnitTesting is an asserter only for unit testing. It's exclusive.
	AsserterUnitTesting
)

func DefaultAsserter added in v0.8.0

func DefaultAsserter() Asserter

You are free to set it according to your current preferences with the SetDefaultAsserter function.

func SetDefaultAsserter added in v0.8.14

func SetDefaultAsserter(a Asserter) Asserter

SetDefaultAsserter set the current default asserter for the package. For example, you might set it to panic about every assertion fault, and in other cases, throw an error, and print the call stack immediately when assert occurs. Note, that if you are using tracers you might get two call stacks, so test what's best for your case.

SetDefaultAsserter(AsserterDebug | AsserterStackTrace)

func (Asserter) Empty

func (asserter Asserter) Empty(obj any, msg ...any)

Empty asserts that length of the object is zero. If not it panics with the given formatting string. Note! This is slow.

Example
package main

import (
	"fmt"

	"github.com/lainio/err2"
	"github.com/lainio/err2/assert"
)

func main() {
	sample := func(b []byte) (err error) {
		defer err2.Handle(&err, "sample")

		assert.P.Empty(b)
		return err
	}
	err := sample([]byte{1, 2})
	fmt.Printf("%v", err)
}
Output:

sample: got 2, want == 0

func (Asserter) EqualInt

func (asserter Asserter) EqualInt(val, want int, a ...any)

EqualInt asserts that integers are equal. If not it panics/errors (current Asserter) with the given msg.

Example
package main

import (
	"fmt"

	"github.com/lainio/err2"
	"github.com/lainio/err2/assert"
)

func main() {
	sample := func(b []byte) (err error) {
		defer err2.Handle(&err, "sample")

		assert.P.EqualInt(len(b), 3)
		return err
	}
	err := sample([]byte{1, 2})
	fmt.Printf("%v", err)
}
Output:

sample: got 2, want 3

func (Asserter) Len

func (asserter Asserter) Len(obj any, length int, a ...any)

Len asserts that length of the object is equal to given. If not it panics/errors (current Asserter) with the given msg. Note! This is very slow (before we have generics). If you need performance use EqualInt. It's not so convenient, though.

Example
package main

import (
	"fmt"

	"github.com/lainio/err2"
	"github.com/lainio/err2/assert"
)

func main() {
	sample := func(b []byte) (err error) {
		defer err2.Handle(&err, "sample")

		assert.P.Len(b, 3)
		return err
	}
	err := sample([]byte{1, 2})
	fmt.Printf("%v", err)
}
Output:

sample: got 2, want 3

func (Asserter) Lenf

func (asserter Asserter) Lenf(obj any, length int, format string, a ...any)

Lenf asserts that length of the object is equal to given. If not it panics/errors (current Asserter) with the given msg. Note! This is very slow (before we have generics). If you need performance use EqualInt. It's not so convenient, though.

Example
package main

import (
	"fmt"

	"github.com/lainio/err2"
	"github.com/lainio/err2/assert"
)

func main() {
	sample := func(b []byte) (err error) {
		defer err2.Handle(&err, "sample")

		assert.P.Lenf(b, 3, "actual len = %d", len(b))
		return err
	}
	err := sample([]byte{1, 2})
	fmt.Printf("%v", err)
}
Output:

sample: actual len = 2

func (Asserter) NoImplementation

func (asserter Asserter) NoImplementation(a ...any)

NoImplementation always fails with no implementation.

Example
package main

import (
	"fmt"

	"github.com/lainio/err2"
	"github.com/lainio/err2/assert"
)

func main() {
	sample := func(m int) (err error) {
		defer err2.Handle(&err, "sample")

		switch m {
		case 1:
			return nil
		default:
			assert.P.NoImplementation()
		}
		return err
	}
	err := sample(0)
	fmt.Printf("%v", err)
}
Output:

sample: not implemented

func (Asserter) NotEmpty

func (asserter Asserter) NotEmpty(obj any, msg ...any)

NotEmpty asserts that length of the object greater than zero. If not it panics with the given formatting string. Note! This is slow.

func (Asserter) NotEmptyf

func (asserter Asserter) NotEmptyf(obj any, format string, msg ...any)

NotEmptyf asserts that length of the object greater than zero. If not it panics with the given formatting string. Note! This is slow.

func (Asserter) True

func (asserter Asserter) True(term bool, a ...any)

True asserts that term is true. If not it panics with the given formatting string. Note! This and Truef are the most performant of all the assertion functions.

Example
package main

import (
	"fmt"

	"github.com/lainio/err2"
	"github.com/lainio/err2/assert"
)

func main() {
	sample := func() (err error) {
		defer err2.Handle(&err)

		assert.That(false, "assertion test")
		return err
	}
	err := sample()
	fmt.Printf("%v", err)
}
Output:

testing run example: assertion test

func (Asserter) Truef

func (asserter Asserter) Truef(term bool, format string, a ...any)

Truef asserts that term is true. If not it panics with the given formatting string.

Example
package main

import (
	"fmt"

	"github.com/lainio/err2"
	"github.com/lainio/err2/assert"
)

func main() {
	sample := func() (err error) {
		defer err2.Handle(&err, "sample")

		assert.P.Truef(false, "assertion test %d", 2)
		return err
	}
	err := sample()
	fmt.Printf("%v", err)
}
Output:

sample: assertion test 2

type Number added in v0.8.14

type Number interface {
	constraints.Float | constraints.Integer
}

Jump to

Keyboard shortcuts

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