assert

package
v1.1.0 Latest Latest
Warning

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

Go to latest
Published: Oct 11, 2024 License: MIT Imports: 12 Imported by: 58

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 the same API. Only thing you need to do is to add a PushTester line at the beginning of your unit tests and its sub-gouroutines:

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

     //                 v-----v Invite's control flow includes assertions
     alice.Node = root1.Invite(alice.Node, root1.Key, alice.PubKey, 1)
     assert.Equal(alice.Len(), 1) // assert anything normally
     ...
     go func() {
          assert.PushTester(t)() // <-- Needs to do again for a new goroutine

Merge Runtime And Unit Test Assertions

The next block is the actual Invite function's first two lines. Even if the assertion line is written more from a runtime detection point of view, it catches all assert violations in the unit tests as well:

func (c Chain) Invite(...) {
     assert.That(c.isLeaf(invitersKey), "only leaf can invite")

If some assertion violation occurs in the deep call stack, they are still reported as test failures. See the above code blocks. If assertion failure happens somewhere inside the Invite function's call stack, it's still reported correctly as a test failure of the TestInvite unit test. It doesn't matter how deep the recursion is or if parallel test runs are performed. The failure report includes all the locations of the meaningful call stack steps. See the next chapter.

Call Stack Traversal During Tests

The Assert package allows us to track assertion violations over the package and module boundaries. When an assertion fails during the unit testing, the whole call stack is brought to unit test logs. And some help with your IDE, such as transferring output to a location list in Neovim/Vim. For example, you can find a compatible test result parser for Neovim from this plugin nvim-go (fork).

The call stack traversal has proven to be very valuable for package and module development in Go, especially when following TDD and fast development feedback cycles.

Why Runtime Asserts Are So Important?

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

"Assertions are active comments"

The assert 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 a goroutine specific Asserter with PushAsserter function.

The assert package's default Asserter you can set with SetDefault or -asserter flag if Go's flag package (or similar) is in use. This allows developer, operator and every-day user share the exact same binary but get the error messages and diagnostic they need.

// Production asserter adds formatted caller info to normal errors.
// Information is transported thru error values when err2.Handle is in use.
assert.SetDefault(assert.Production)

Please see the code examples for more information.

Note that if an Asserter is set for a goroutine level, it cannot be changed with the -asserter flag or SetDefault. The GLS Asserter is used for a reason, so it's good that even a unit test asserter won't override it in those cases.

Flag Package Support

The assert package supports Go's flags. All you need to do is to call flag.Parse. And the following flags are supported (="default-value"):

-asserter="Prod"
    A name of the asserter Plain, Prod, Dev, Debug
    See more information from constants: Plain, Production, Development, Debug

And assert package's configuration flags are inserted.

Performance

The performance of the assert functions are equal to the if-statement thanks for inlining. All of the generics-based versions are the equally fast!

We should prefer specialized versions like assert.Equal that we get precise and readable error messages automatically. Error messagas follow Go idiom of 'got xx, want yy'. And we still can annotate error message if we want.

Naming

Because performance has been number one requirement and Go's generics cannot discrete slices, maps and channels we have used naming prefixes accordingly: S = slice, M = map, C = channel. No prefix is (currently) for the string type.

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

func CLen added in v0.9.52

func CLen[C ~chan T, T any](obj C, length int, a ...any)

CLen asserts that the length of the chan is equal to the given. If not it panics/errors (according the current Asserter) with the auto-generated message. You can append the generated got-want message by using optional message arguments.

Note that when Plain Asserter is used (PushAsserter or even SetDefault), optional arguments are used to override the auto-generated assert violation 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 chan int) (err error) {
		defer err2.Handle(&err, "sample")

		assert.CLonger(b, 1)  // OK
		assert.CShorter(b, 3) // OK
		assert.CLen(b, 3)     // Not OK
		return err
	}
	d := make(chan int, 2)
	d <- int(1)
	d <- int(1)
	err := sample(d)
	fmt.Printf("%v", err)
}
Output:

sample: assert_test.go:265: ExampleCLen.func1(): assertion failure: length: got '2', want '3'

func CLonger added in v0.9.52

func CLonger[C ~chan T, T any](obj C, length int, a ...any)

CLonger asserts that the length of the chan is longer to the given. If not it panics/errors (according the current Asserter) with the auto-generated message. You can append the generated got-want message by using optional message arguments.

Note that when Plain Asserter is used (PushAsserter or even SetDefault), optional arguments are used to override the auto-generated assert violation 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 CNil added in v1.1.0

func CNil[C ~chan T, T any](c C, a ...any)

CNil asserts that the channel is nil. If it is not it panics/errors (default Asserter) the auto-generated (args appended) message.

Note that when Plain Asserter is used (PushAsserter or even SetDefault), optional arguments are used to override the auto-generated assert violation message.

func CNotNil added in v0.8.0

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

CNotNil asserts that the channel is not nil. If it is it panics/errors (default Asserter) the auto-generated (args appended) message.

Note that when Plain Asserter is used (PushAsserter or even SetDefault), optional arguments are used to override the auto-generated assert violation 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.CNil(c)    // OK
		assert.CNotNil(c) // Not OK
		return err
	}
	var c chan byte
	err := sample(c)
	fmt.Printf("%v", err)
}
Output:

sample: assert_test.go:58: ExampleCNotNil.func1(): assertion failure: channel should not be nil

func CShorter added in v0.9.52

func CShorter[C ~chan T, T any](obj C, length int, a ...any)

CShorter asserts that the length of the chan is shorter to the given. If not it panics/errors (according the current Asserter) with the auto-generated message. You can append the generated got-want message by using optional message arguments.

Note that when Plain Asserter is used (PushAsserter or even SetDefault), optional arguments are used to override the auto-generated assert violation 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 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 (according the current Asserter) with the auto-generated message. You can append the generated got-want message by using optional message arguments.

Note that when Plain Asserter is used (PushAsserter or even SetDefault), optional arguments are used to override the auto-generated assert violation 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.NoError(err)
		assert.NotDeepEqual(len(b), 3) // OK, correct size is 2
		assert.DeepEqual(len(b), 3)    // Not OK, size is still 2
		return err
	}
	err := sample([]byte{1, 2})
	fmt.Printf("%v", err)
}
Output:

sample: assert_test.go:326: ExampleDeepEqual.func1(): assertion failure: got '2', want '3'

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 (according the current Asserter) with the auto-generated message. You can append the generated got-want message by using optional message arguments.

Note that when Plain Asserter is used (PushAsserter or even SetDefault), optional arguments are used to override the auto-generated assert violation 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 (according the current Asserter) with the auto-generated message. You can append the generated got-want message by using optional message arguments.

Note that when Plain Asserter is used (PushAsserter or even SetDefault), optional arguments are used to override the auto-generated assert violation 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.NotEqual(b[0], 3) // OK, b[0] != 3; (b[0] == 1)
		assert.Equal(b[1], 1)    // Not OK, b[1] == 2
		return err
	}
	err := sample([]byte{1, 2})
	fmt.Printf("%v", err)
}
Output:

sample: assert_test.go:87: ExampleEqual.func1(): assertion failure: equal: got '2', want '1'

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 and builds a violation message. Thanks to inlining, the performance penalty is equal to a single 'if-statement' that is almost nothing.

Note that when Plain Asserter is used (PushAsserter or even SetDefault), optional arguments are used to override the auto-generated assert violation message.

Example
package main

import (
	"fmt"

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

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

		assert.Error(b)   // OK
		assert.NoError(b) // Not OK
		return err
	}
	var b = fmt.Errorf("test")
	err := sample(b)
	fmt.Printf("%v", err)
}
Output:

sample: assert_test.go:339: ExampleError.func1(): assertion failure: test

func Greater added in v1.1.0

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

Greater asserts that the value is greater than want. If it is not it panics and builds a violation message. Thanks to inlining, the performance penalty is equal to a single 'if-statement' that is almost nothing.

Note that when Plain Asserter is used (PushAsserter or even SetDefault), optional arguments are used to override the auto-generated assert violation message.

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.Equal(b, 2)   // ok
		assert.Greater(b, 1) // ok
		assert.Greater(b, 2) // not ok
		return err
	}
	var b int8 = 2
	err := sample(b)
	fmt.Printf("%v", err)
}
Output:

sample: assert_test.go:223: ExampleGreater.func1(): assertion failure: got '2', want <= '2'

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) the auto-generated (args appended) message.

Note that when Plain Asserter is used (PushAsserter or even SetDefault), optional arguments are used to override the auto-generated assert violation message.

Note, use this only for real interface types. Go's interface's has two values so this won't work e.g. slices! Read more information about the interface type.

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) the auto-generated (args appended) message.

Note that when Plain Asserter is used (PushAsserter or even SetDefault), optional arguments are used to override the auto-generated assert violation message.

Note, use this only for real interface types. Go's interface's has two values so this won't work e.g. slices! Read more information about the interface type.

Example
package main

import (
	"fmt"

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

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

		assert.INotNil(b) // OK
		assert.INil(b)    // Not OK
		return err
	}
	var b = fmt.Errorf("test")
	err := sample(b)
	fmt.Printf("%v", err)
}
Output:

sample: assert_test.go:297: ExampleINotNil.func1(): assertion failure: interface should be nil

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 (according the current Asserter) with the auto-generated message. You can append the generated got-want message by using optional message arguments.

Note that when Plain Asserter is used (PushAsserter or even SetDefault), optional arguments are used to override the auto-generated assert violation 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 string) (err error) {
		defer err2.Handle(&err, "sample")

		assert.Shorter(b, 3) // OK
		assert.Longer(b, 1)  // OK
		assert.Len(b, 3)     // Not OK
		return err
	}
	err := sample("12")
	fmt.Printf("%v", err)
}
Output:

sample: assert_test.go:312: ExampleLen.func1(): assertion failure: length: got '2', want '3'

func Less added in v1.1.0

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

Less asserts that the value is less than want. If it is not it panics and builds a violation message. Thanks to inlining, the performance penalty is equal to a single 'if-statement' that is almost nothing.

Note that when Plain Asserter is used (PushAsserter or even SetDefault), optional arguments are used to override the auto-generated assert violation message.

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.Equal(b, 1) // ok
		assert.Less(b, 2)  // ok
		assert.Less(b, 1)  // not ok
		return err
	}
	var b int8 = 1
	err := sample(b)
	fmt.Printf("%v", err)
}
Output:

sample: assert_test.go:208: ExampleLess.func1(): assertion failure: got '1', want >= '1'

func Longer added in v0.9.52

func Longer(s string, length int, a ...any)

Longer asserts that the length of the string is longer to the given. If not it panics/errors (according the current Asserter) with the auto-generated message. You can append the generated got-want message by using optional message arguments.

Note that when Plain Asserter is used (PushAsserter or even SetDefault), optional arguments are used to override the auto-generated assert violation 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 MEmpty added in v1.1.0

func MEmpty[M ~map[T]U, T comparable, U any](obj M, a ...any)

MEmpty asserts that the map is empty. If it is NOT, it panics/errors (according the current Asserter) with the auto-generated message. You can append the generated got-want message by using optional message arguments. You can append the generated got-want message by using optional message arguments.

Note that when Plain Asserter is used (PushAsserter or even SetDefault), optional arguments are used to override the auto-generated assert violation 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[M ~map[T]U, T comparable, U any](
	obj M,
	key T,
	a ...any,
) (val U)

MKeyExists asserts that the map key exists. If not it panics/errors (current Asserter) the auto-generated (args appended) message.

Note that when Plain Asserter is used (PushAsserter or even SetDefault), optional arguments are used to override the auto-generated assert violation 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") // OK, 1 --> one
		assert.Equal(v, "one")         // OK
		_ = assert.MKeyExists(m, b)    // fails with b = 2
		return err
	}
	err := sample("2")
	fmt.Printf("%v", err)
}
Output:

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

func MLen added in v0.8.0

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

MLen asserts that the length of the map is equal to the given. If not it panics/errors (according the current Asserter) with the auto-generated message. You can append the generated got-want message by using optional message arguments.

Note that when Plain Asserter is used (PushAsserter or even SetDefault), optional arguments are used to override the auto-generated assert violation 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 map[int]byte) (err error) {
		defer err2.Handle(&err, "sample")

		assert.MLonger(b, 1)  // OK
		assert.MShorter(b, 3) // OK
		assert.MLen(b, 3)     // Not OK
		return err
	}
	err := sample(map[int]byte{1: 1, 2: 2})
	fmt.Printf("%v", err)
}
Output:

sample: assert_test.go:251: ExampleMLen.func1(): assertion failure: length: got '2', want '3'

func MLonger added in v0.9.52

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

MLonger asserts that the length of the map is longer to the given. If not it panics/errors (according the current Asserter) with the auto-generated message. You can append the generated got-want message by using optional message arguments.

Note that when Plain Asserter is used (PushAsserter or even SetDefault), optional arguments are used to override the auto-generated assert violation 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 MNil added in v1.1.0

func MNil[M ~map[T]U, T comparable, U any](m M, a ...any)

MNil asserts that the map is nil. If it is not it panics/errors (default Asserter) the auto-generated (args appended) message.

Note that when Plain Asserter is used (PushAsserter or even SetDefault), optional arguments are used to override the auto-generated assert violation message.

func MNotEmpty added in v0.8.3

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

MNotEmpty asserts that the map is not empty. If it is, it panics/errors (according the current Asserter) with the auto-generated message. You can append the generated got-want message by using optional message arguments. You can append the generated got-want message by using optional message arguments.

Note that when Plain Asserter is used (PushAsserter or even SetDefault), optional arguments are used to override the auto-generated assert violation 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[M ~map[T]U, T comparable, U any](m M, a ...any)

MNotNil asserts that the map is not nil. If it is it panics/errors (default Asserter) the auto-generated (args appended) message.

Note that when Plain Asserter is used (PushAsserter or even SetDefault), optional arguments are used to override the auto-generated assert violation 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.MEmpty(b)  // OK
		assert.MNil(b)    // OK
		assert.MNotNil(b) // Not OK
		return err
	}
	var b map[string]byte
	err := sample(b)
	fmt.Printf("%v", err)
}
Output:

sample: assert_test.go:44: ExampleMNotNil.func1(): assertion failure: map should not be nil

func MShorter added in v0.9.52

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

MShorter asserts that the length of the map is shorter to the given. If not it panics/errors (according the current Asserter) with the auto-generated message. You can append the generated got-want message by using optional message arguments.

Note that when Plain Asserter is used (PushAsserter or even SetDefault), optional arguments are used to override the auto-generated assert violation 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 map[byte]byte) (err error) {
		defer err2.Handle(&err, "sample")

		assert.MNotEmpty(b)   // OK
		assert.MShorter(b, 1) // OK
		assert.MShorter(b, 0) // Not OK
		return err
	}
	err := sample(map[byte]byte{01: 01}) // len = 1
	fmt.Printf("%v", err)
}
Output:

sample: assert_test.go:180: ExampleMShorter.func1(): assertion failure: got '1', should be shorter than '1'

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) the auto-generated (args appended) message.

Note that when Plain Asserter is used (PushAsserter or even SetDefault), optional arguments are used to override the auto-generated assert violation 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.

Note. We recommend that you prefer github.com/lainio/err2/try.To. They work exactly the same during the test runs and you can use the same code for both: runtime and tests. However, there are cases that you want assert that there is no error in cases where fast fail and immediate stop of execution is wanted at runtime. With asserts (Production, Development, Debug) you get the file location as well.

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 (according the current Asserter) with the auto-generated message. You can append the generated got-want message by using optional message arguments.

Note that when Plain Asserter is used (PushAsserter or even SetDefault), optional arguments are used to override the auto-generated assert violation 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 (according the current Asserter) with the auto-generated message. You can append the generated got-want message by using optional message arguments.

Note that when Plain Asserter is used (PushAsserter or even SetDefault), optional arguments are used to override the auto-generated assert violation 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)    // OK
		assert.NotEmpty(b) // not OK
		return err
	}
	err := sample("")
	fmt.Printf("%v", err)
}
Output:

sample: assert_test.go:124: ExampleNotEmpty.func1(): assertion failure: string should not 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 (according the current Asserter) with the auto-generated message. You can append the generated got-want message by using optional message arguments.

Note that when Plain Asserter is used (PushAsserter or even SetDefault), optional arguments are used to override the auto-generated assert violation message.

Note, when Asserter is Plain, optional arguments are used to build a new assert violation message.

func NotImplemented added in v0.8.3

func NotImplemented(a ...any)

NotImplemented always panics with 'not implemented' assertion message.

Note that when Plain Asserter is used (PushAsserter or even SetDefault), optional arguments are used to override the auto-generated assert violation message.

Example
package main

import (
	"fmt"

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

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

		assert.NotImplemented() // Not OK
		return err
	}
	var b = fmt.Errorf("test")
	err := sample(b)
	fmt.Printf("%v", err)
}
Output:

sample: assert_test.go:352: ExampleNotImplemented.func1(): assertion failure: not implemented

func NotNil added in v0.8.0

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

NotNil asserts that the pointer IS NOT nil. If it is it panics/errors (default Asserter) the auto-generated (args appended) message.

Note that when Plain Asserter is used (PushAsserter or even SetDefault), optional arguments are used to override the auto-generated assert violation 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.Nil(b)    // OK
		assert.NotNil(b) // Not OK
		return err
	}
	var b *byte
	err := sample(b)
	fmt.Printf("%v", err)
}
Output:

sample: assert_test.go:29: ExampleNotNil.func1(): assertion failure: pointer should not 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 and builds a violation message. Thanks to inlining, the performance penalty is equal to a single 'if-statement' that is almost nothing.

Note that when Plain Asserter is used (PushAsserter or even SetDefault), optional arguments are used to override the auto-generated assert violation message.

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.NotZero(b)
		return err
	}
	var b int8
	err := sample(b)
	fmt.Printf("%v", err)
}
Output:

sample: assert_test.go:236: ExampleNotZero.func1(): assertion failure: got '0', want (!= 0)

func PopAsserter added in v1.1.0

func PopAsserter()

PopAsserter pops current gorounine specific Asserter from packages memory. Gorounine specific Asserter can be set with PushAsserter.

When gorounine Asserter isn't set package's default Asserter is used. See SetDefault for more information.

func PopTester added in v0.8.8

func PopTester()

PopTester pops the testing context reference from the memory. This is for memory cleanup and adding similar to err2.Catch error/panic safety for tests. By using PopTester you get error logs tuned for unit testing.

You have two ways to call PopTester. With defer right 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, "won't work")
     })
}

If you want, you can combine PushTester and PopTester to one-liner:

defer assert.PushTester(t)()

func PushAsserter added in v1.1.0

func PushAsserter(i Asserter) (retFn function)

PushAsserter set Asserter for the current GLS (Gorounine Local Storage). That allows us to have multiple different Asserter in use in the same process.

Let's say that in some function you want to return plain error messages instead of the panic asserts, you can use following in the top-level function:

defer assert.PushAsserter(assert.Plain)()

func PushTester added in v0.8.8

func PushTester(t testing.TB, a ...Asserter) function

PushTester sets the current testing context for default Asserter. This must be called at the beginning of every test. There is two way of doing it:

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

Because PushTester returns PopTester it allows us to merge these two calls to one line. See the first t.Run call above. See more information in PopTester.

PushTester allows you to change the current default Asserter by accepting it as a second argument.

Note that you MUST call PushTester for sub-goroutines:

defer assert.PushTester(t)() // does the cleanup
...
go func() {
     assert.PushTester(t)()
     ...

Note that the second argument, if given, changes the default Asserter for whole package. The argument is mainly for temporary development use and isn't not preferred API usage.

func SEmpty added in v1.1.0

func SEmpty[S ~[]T, T any](obj S, a ...any)

SEmpty asserts that the slice is empty. If it is NOT, it panics/errors (according the current Asserter) with the auto-generated message. You can append the generated got-want message by using optional message arguments.

Note that when Plain Asserter is used (PushAsserter or even SetDefault), optional arguments are used to override the auto-generated assert violation 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 SLen added in v0.8.0

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

SLen asserts that the length of the slice is equal to the given. If not it panics/errors (according the current Asserter) with the auto-generated message. You can append the generated got-want message by using optional message arguments.

Note that when Plain Asserter is used (PushAsserter or even SetDefault), optional arguments are used to override the auto-generated assert violation 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:99: ExampleSLen.func1(): assertion failure: length: got '2', want '3'

func SLonger added in v0.9.52

func SLonger[S ~[]T, T any](obj S, length int, a ...any)

SLonger asserts that the length of the slice is equal to the given. If not it panics/errors (according the current Asserter) with the auto-generated message. You can append the generated got-want message by using optional message arguments.

Note that when Plain Asserter is used (PushAsserter or even SetDefault), optional arguments are used to override the auto-generated assert violation 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.SLonger(b, 0) // OK
		assert.SLonger(b, 1) // Not OK
		return err
	}
	err := sample([]byte{01}) // len = 1
	fmt.Printf("%v", err)
}
Output:

sample: assert_test.go:167: ExampleSLonger.func1(): assertion failure: got '1', should be longer than '1'

func SNil added in v0.8.8

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

SNil asserts that the slice IS nil. If it is it panics/errors (default Asserter) the auto-generated (args appended) message.

Note that when Plain Asserter is used (PushAsserter or even SetDefault), optional arguments are used to override the auto-generated assert violation message.

func SNotEmpty added in v0.8.3

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

SNotEmpty asserts that the slice is not empty. If it is, it panics/errors (according the current Asserter) with the auto-generated message. You can append the generated got-want message by using optional message arguments.

Note that when Plain Asserter is used (PushAsserter or even SetDefault), optional arguments are used to override the auto-generated assert violation 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:111: ExampleSNotEmpty.func1(): assertion failure: slice should not be empty

func SNotNil added in v0.8.0

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

SNotNil asserts that the slice is not nil. If it is it panics/errors (default Asserter) the auto-generated (args appended) message.

Note that when Plain Asserter is used (PushAsserter or even SetDefault), optional arguments are used to override the auto-generated assert violation 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.SEmpty(b)  // OK
		assert.SNil(b)    // OK
		assert.SNotNil(b) // Not OK
		return err
	}
	var b []byte
	err := sample(b)
	fmt.Printf("%v", err)
}
Output:

sample: assert_test.go:73: ExampleSNotNil.func1(): assertion failure: slice should not be nil

func SShorter added in v0.9.52

func SShorter[S ~[]T, T any](obj S, length int, a ...any)

SShorter asserts that the length of the slice is equal to the given. If not it panics/errors (according the current Asserter) with the auto-generated message. You can append the generated got-want message by using optional message arguments.

Note that when Plain Asserter is used (PushAsserter or even SetDefault), optional arguments are used to override the auto-generated assert violation 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.SShorter(b, 2)                                      // ok
		assert.SShorter(b, 0, "optional message (%s)", "test_str") // not ok
		return err
	}
	err := sample([]byte{01}) // len = 1
	fmt.Printf("%v", err)
}
Output:

sample: assert_test.go:194: ExampleSShorter.func1(): assertion failure: got '1', should be shorter than '0': optional message (test_str)

func Shorter added in v0.9.52

func Shorter(str string, length int, a ...any)

Shorter asserts that the length of the string is shorter to the given. If not it panics/errors (according the current Asserter) with the auto-generated message. You can append the generated got-want message by using optional message arguments.

Note that when Plain Asserter is used (PushAsserter or even SetDefault), optional arguments are used to override the auto-generated assert violation 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 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.

Note that when Plain Asserter is used (PushAsserter or even SetDefault), optional arguments are used to override the auto-generated assert violation message.

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, "optional message")
		return err
	}
	err := sample()
	fmt.Printf("%v", err)
}
Output:

testing: run example: assert_test.go:16: ExampleThat.func1(): assertion failure: optional message

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.

Note that when Plain Asserter is used (PushAsserter or even SetDefault), optional arguments are used to override the auto-generated assert violation message.

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.ThatNot(true, "overrides if Plain asserter")
		return err
	}

	// Set a specific asserter for this goroutine only, we want plain errors
	defer assert.PushAsserter(assert.Plain)()

	err := sample()
	fmt.Printf("%v", err)
}
Output:

testing: run example: overrides if Plain asserter

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 and builds a violation message. Thanks to inlining, the performance penalty is equal to a single 'if-statement' that is almost nothing.

Note that when Plain Asserter is used (PushAsserter or even SetDefault), optional arguments are used to override the auto-generated assert violation message.

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:153: ExampleZero.func1(): assertion failure: got '1', want (== '0')

Types

type Asserter

type Asserter = uint32
const (
	Plain Asserter = 0 + iota
	Production
	Development
	Test
	TestFull
	Debug
)

Asserters are the way to set what kind of messages assert package outputs if assertion is violated.

Plain converts asserts just plain K&D error messages without extra information. That's useful for apps that want to use assert package to validate e.g. command fields:

assert.NotEmpty(c.PoolName, "pool name cannot be empty")

Note that Plain is only Asserter that override auto-generated assertion messages with given arguments like 'pool name cannot be empty'. Others add given arguments at the end of the auto-generated assert message.

Production (pkg's default) is the best Asserter for most cases. The assertion violations are treated as Go error values. And only a pragmatic caller info is included into the error values like source filename, line number, and caller function, all in one line:

copy file: main.go:37: CopyFile(): assertion violation: string shouldn't be empty

Development is the best Asserter for development use. The assertion violations are treated as Go error values. And a formatted caller info is included to the error message like source filename , line number, and caller function. Everything in a noticeable multi-line message:

--------------------------------
Assertion Fault at:
main.go:37 CopyFile():
assertion violation: string shouldn't be empty
--------------------------------

Test minimalistic Asserter for unit test use. More pragmatic is the TestFull Asserter (test default).

Use this Asserter if your IDE/editor doesn't support full file names and it relies a relative path (Go standard). You can use this also if you need temporary problem solving for your programming environment.

TestFull Asserter (test default). The TestFull Asserter includes the caller info and the call stack for unit testing, similarly like err2's error traces. Note that PushTester set's TestFull if it's not yet set.

The call stack produced by the test asserts can be used over Go module boundaries. For example, if your app and it's sub packages both use err2/assert for unit testing and runtime checks, the runtime assertions will be automatically converted to test asserts. If any of the runtime asserts of the sub packages fails during the app tests, the app test fails as well.

Note that the cross-module assertions produce long file names (path included), and some of the Go test result parsers cannot handle that. A proper test result parser like nvim-go (fork) works very well. Also most of the make result parsers can process the output properly and allow traverse of locations of the error trace.

Debug Asserter transforms assertion violations to panic calls where panic object's type is string, i.e., err2 package treats it as a normal panic, not an error.

For example, the pattern that e.g. Go's standard library uses:

if p == nil {
     panic("pkg: ptr cannot be nil")
}

is equal to:

assert.NotNil(p)

func SetDefault added in v0.9.1

func SetDefault(i Asserter) (old Asserter)

SetDefault sets the current default Asserter for assert pkg. It also returns the previous Asserter.

Note that you should use this in TestMain function, and use flag package to set it for the app. For the tests you can set it to panic about every assertion fault, or to throw an error, or/and print the call stack immediately when assert occurs. The err2 package helps you to catch and report all types of the asserts.

Note that if you are using tracers you might get two call stacks, so test what's best for your case.

Tip. If our own packages (client packages for assert) have lots of parallel testing and race detection, please try to use same Asserter for all of them and set Asserter only one in TestMain, or in init.

func TestMain(m *testing.M) {
     SetDefault(assert.TestFull)

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