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) { defer assert.PushTester(t)() // push testing variable t beginning of any test 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.
Call Stack Traversal During tests ¶
The asserter package has super powerful feature. It allows us track assertion violations over package and even module boundaries. When using err2 assert package for runtime Asserts and assert violation happens in what ever package and module, the whole call stack is brougth to unit test logs. Naturally this is optinal. Only thing you need to do is set proper asserter and call PushTester.
// use unit testing asserter assert.SetDefault(assert.TestFull)
With large multi repo environment this has proven to be valuable.
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 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.SetDefault(assert.Production)
Please see the code examples for more information.
Note. assert.That's performance is 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. Also you can make an issue or even PR if you would like to have something similar like glog.V() function.
Note. Format string functions need to be own instances because of Go's vet and test tool integration.
Index ¶
- Constants
- Variables
- func CNotNil[C ~chan T, T any](c C, a ...any)
- func DeepEqual(val, want any, a ...any)
- func Empty(obj string, a ...any)
- func Equal[T comparable](val, want T, a ...any)
- func Error(err error, a ...any)
- func INil(i any, a ...any)
- func INotNil(i any, a ...any)
- func Len(obj string, length int, a ...any)
- func MKeyExists[M ~map[T]U, T comparable, U any](obj M, key T, a ...any) (val U)
- func MLen[M ~map[T]U, T comparable, U any](obj M, length int, a ...any)
- func MNotEmpty[M ~map[T]U, T comparable, U any](obj M, a ...any)
- func MNotNil[M ~map[T]U, T comparable, U any](m M, a ...any)
- func Nil[T any](p *T, a ...any)
- func NoError(err error, a ...any)
- func NotDeepEqual(val, want any, a ...any)
- func NotEmpty(obj string, a ...any)
- func NotEqual[T comparable](val, want T, a ...any)
- func NotImplemented(a ...any)
- func NotNil[P ~*T, T any](p P, a ...any)
- func NotZero[T Number](val T, a ...any)
- func PopTester()
- func PushTester(t testing.TB, a ...defInd) function
- func SLen[S ~[]T, T any](obj S, length int, a ...any)
- func SNil[S ~[]T, T any](s S, a ...any)
- func SNotEmpty[S ~[]T, T any](obj S, a ...any)
- func SNotNil[S ~[]T, T any](s S, a ...any)
- func That(term bool, a ...any)
- func ThatNot(term bool, a ...any)
- func Zero[T Number](val T, a ...any)
- type Asserter
- func (asserter Asserter) Empty(obj any, msg ...any)
- func (asserter Asserter) EqualInt(val, want int, a ...any)
- func (asserter Asserter) Len(obj any, length int, a ...any)
- func (asserter Asserter) Lenf(obj any, length int, format string, a ...any)
- func (asserter Asserter) NoImplementation(a ...any)
- func (asserter Asserter) NotEmpty(obj any, msg ...any)
- func (asserter Asserter) NotEmptyf(obj any, format string, msg ...any)
- func (asserter Asserter) True(term bool, a ...any)
- func (asserter Asserter) Truef(term bool, format string, a ...any)
- type Number
Examples ¶
Constants ¶
const ( Production defInd = 0 + iota Development Test TestFull Debug )
Variables ¶
var ( // P is a production Asserter that sets panic objects to errors which // allows err2 handlers to catch them. P = AsserterToError | AsserterCallerInfo B = AsserterToError | AsserterFormattedCallerInfo T = AsserterUnitTesting TF = AsserterUnitTesting | AsserterStackTrace | AsserterCallerInfo // D is a development Asserter that sets panic objects to strings that // doesn't by caught by err2 handlers. // Deprecated: use e.g. assert.That(), only default asserter is used. D = AsserterDebug )
Deprecated: use e.g. assert.That(), only default asserter is used.
Functions ¶
func CNotNil ¶ added in v0.8.0
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:54: ExampleCNotNil.func1(): assertion violation: channel shouldn't be nil
func DeepEqual ¶ added in v0.8.9
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
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:80: ExampleEqual.func1(): assertion violation: got '2', want '3'
func Error ¶ added in v0.8.8
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
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
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
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[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) 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:134: ExampleMKeyExists.func1(): assertion violation: 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 (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[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 (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[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) 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:41: ExampleMNotNil.func1(): assertion violation: map shouldn't be nil
func Nil ¶ added in v0.8.14
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
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
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
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:117: 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
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:28: ExampleNotNil.func1(): assertion violation: pointer shouldn't be nil
func NotZero ¶ added in v0.9.0
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 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, "test won't work") }) }
If you want to have one liner to combine Push/PopTester:
defer assert.PushTester(t)()
func PushTester ¶ added in v0.8.8
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. NOTE. More information in PopTester.
func SLen ¶ added in v0.8.0
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:92: ExampleSLen.func1(): assertion violation: got '2', want '3'
func SNil ¶ added in v0.8.8
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
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:104: ExampleSNotEmpty.func1(): assertion violation: slice shouldn't be empty
func SNotNil ¶ added in v0.8.0
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:67: ExampleSNotNil.func1(): assertion violation: slice shouldn't be nil
func That ¶ added in v0.8.0
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.
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 ThatNot ¶ added in v0.8.8
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
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:146: 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 OR if // in AsserterUnitTesting mode the call stack is printed to test result // output if there is any assertion failures. AsserterStackTrace // AsserterCallerInfo is an asserter flag to add info of the function // asserting. It includes filename, line number and function name. // This is especially powerful with AsserterUnitTesting where it allows get // information where the assertion violation happens even over modules! 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 can be // compined with AsserterCallerInfo and/or AsserterStackTrace. There is // variable T which have all of these three asserters. AsserterUnitTesting )
func Default ¶ added in v0.9.1
func Default() Asserter
You are free to set it according to your current preferences with the SetDefault function.
func SetDefault ¶ added in v0.9.1
func SetDefault(i defInd) Asserter
SetDefault 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. Tip. If our own packages (client packages for assert) have lots of parallel testing and race detection, please try to use same asserter for allo foot hem and do it only one in TestMain, or in init.
SetDefault(assert.TestFull)
func (Asserter) Empty ¶
Empty asserts that length of the object is zero. If not it panics with the given formatting string. Note! This is slow.
func (Asserter) EqualInt ¶
EqualInt asserts that integers are equal. If not it panics/errors (current Asserter) with the given msg.
func (Asserter) Len ¶
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.
func (Asserter) Lenf ¶
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.
func (Asserter) NoImplementation ¶
NoImplementation always fails with no implementation.
func (Asserter) NotEmpty ¶
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 ¶
NotEmptyf asserts that length of the object greater than zero. If not it panics with the given formatting string. Note! This is slow.
type Number ¶ added in v0.8.14
type Number interface { constraints.Float | constraints.Integer }