Documentation
¶
Overview ¶
Package check provide helpers to complement Go testing package.
Features ¶
This package is like testify/assert on steroids. :)
- Compelling output from failed tests:
- Very easy-to-read dumps for expected and actual values.
- Same text diff you loved in testify/assert.
- Also visual diff in GoConvey web UI, if you use it (recommended).
- Statistics with amount of passed/failed checks.
- Colored output in terminal.
- 100% compatible with testing package - check package just provide convenient wrappers for *testing.T methods and doesn't introduce new concepts like BDD, custom test suite or unusual execution flow.
- All checks you may ever need! :)
- Very easy to add your own check functions.
- Concise, handy and consistent API, without dot-import!
Quickstart ¶
Just wrap each (including subtests) *testing.T using check.T() and write tests as usually with testing package. Call new methods provided by this package to have more clean/concise test code and cool dump/diff.
import "github.com/powerman/check" func TestSomething(tt *testing.T) { t := check.T(tt) t.Equal(2, 2) t.Log("You can use new t just like usual *testing.T") t.Run("Subtests/Parallel example", func(tt *testing.T) { t := check.T(tt) t.Parallel() t.NotEqual(2, 3, "should not be 3!") obj, err := NewObj() if t.Nil(err) { t.Match(obj.field, `^\d+$`) } }) }
To get optional statistics about executed checkers add:
func TestMain(m *testing.M) { check.TestMain(m) }
When use goconvey tool, to get nice diff in web UI add:
import _ "github.com/smartystreets/goconvey/convey"
Hints ¶
★ How to check for errors:
// If you just want nil: t.Nil(err) t.Err(err, nil) // Check for (absence of) concrete (possibly wrapped) error: t.Err(err, io.EOF) t.NotErr(err, io.EOF) // nil is not io.EOF, so it's ok too // When need to match by error's text: t.Match(err, "file.*permission") // Use Equal ONLY when checking for same instance: t.Equal(io.EOF, io.EOF) // this works t.Equal(io.EOF, errors.New("EOF")) // this doesn't work! t.Err(io.EOF, errors.New("EOF")) // this works t.DeepEqual(io.EOF, errors.New("EOF")) // this works too
★ Each check returns bool, so you can easily skip problematic code:
if t.Nil(err) { t.Match(obj.field, `^\d+$`) }
★ You can turn any check into assertion to stop test immediately:
t.Must(t.Nil(err))
★ You can turn all checks into assertions to stop test immediately:
t = t.MustAll() t.Nil(err)
★ You can provide extra description to each check:
t.Equal(got, want, "Just msg: will Print(), % isn't special") t.Equal(got, want, "Msg with args: will Printf(): %v", extra)
★ There are short synonyms for checks implementing usual ==, !=, etc.:
t.EQ(got, want) // same as t.Equal t.NE(got, want) // same as t.NotEqual t.LT(got, want) // same as t.Less t.LE(got, want) // same as t.LessOrEqual t.GT(got, want) // same as t.Greater t.GE(got, want) // same as t.GreaterOrEqual
★ If you need custom check, which isn't available out-of-box - see Should checker, it'll let you plug in your own checker with ease.
★ It will panic when called with arg of wrong type - because this means bug in your test.
★ If you don't see colors in `go test` output it may happens because of two reasons: either your $TERM doesn't contain substring "color" or you're running `go test path/to/your/package`. To force colored output in last case just set this environment variable:
export GO_TEST_COLOR=1
Contents ¶
There are few special functions (assertion, custom checkers, etc.).
Error Must MustAll Should TODO
Everything else are just trivial (mostly) checkers which works in obvious way and accept values of any types which makes sense (and panics on everything else).
Nil NotNil Zero NotZero True False Equal NotEqual EQ NE DeepEqual NotDeepEqual Err NotErr BytesEqual NotBytesEqual JSONEqual Greater LessOrEqual GT LE Less GreaterOrEqual LT GE Between NotBetween BetweenOrEqual NotBetweenOrEqual InDelta NotInDelta InSMAPE NotInSMAPE Len NotLen Match NotMatch HasPrefix NotHasPrefix HasSuffix NotHasSuffix HasKey NotHasKey Contains NotContains HasType NotHasType Implements NotImplements Panic NotPanic PanicMatch PanicNotMatch
Index ¶
- func Report()
- func TestMain(m *testing.M)
- type C
- func (t *C) Between(actual, minimum, maximum any, msg ...any) bool
- func (t *C) BetweenOrEqual(actual, minimum, maximum any, msg ...any) bool
- func (t *C) BytesEqual(actual, expected []byte, msg ...any) bool
- func (t *C) Contains(actual, expected any, msg ...any) bool
- func (t *C) DeepEqual(actual, expected any, msg ...any) bool
- func (t *C) EQ(actual, expected any, msg ...any) bool
- func (t *C) Equal(actual, expected any, msg ...any) bool
- func (t *C) Err(actual, expected error, msg ...any) bool
- func (t *C) Error(msg ...any)
- func (t *C) False(cond bool, msg ...any) bool
- func (t *C) GE(actual, expected any, msg ...any) bool
- func (t *C) GT(actual, expected any, msg ...any) bool
- func (t *C) Greater(actual, expected any, msg ...any) bool
- func (t *C) GreaterOrEqual(actual, expected any, msg ...any) bool
- func (t *C) HasKey(actual, expected any, msg ...any) bool
- func (t *C) HasPrefix(actual, expected any, msg ...any) bool
- func (t *C) HasSuffix(actual, expected any, msg ...any) bool
- func (t *C) HasType(actual, expected any, msg ...any) bool
- func (t *C) Implements(actual, expected any, msg ...any) bool
- func (t *C) InDelta(actual, expected, delta any, msg ...any) bool
- func (t *C) InSMAPE(actual, expected any, smape float64, msg ...any) bool
- func (t *C) JSONEqual(actual, expected any, msg ...any) bool
- func (t *C) LE(actual, expected any, msg ...any) bool
- func (t *C) LT(actual, expected any, msg ...any) bool
- func (t *C) Len(actual any, expected int, msg ...any) bool
- func (t *C) Less(actual, expected any, msg ...any) bool
- func (t *C) LessOrEqual(actual, expected any, msg ...any) bool
- func (t *C) Match(actual, regex any, msg ...any) bool
- func (t *C) Must(continueTest bool, msg ...any)
- func (t *C) MustAll() *C
- func (t *C) NE(actual, expected any, msg ...any) bool
- func (t *C) Nil(actual any, msg ...any) bool
- func (t *C) NotBetween(actual, minimum, maximum any, msg ...any) bool
- func (t *C) NotBetweenOrEqual(actual, minimum, maximum any, msg ...any) bool
- func (t *C) NotBytesEqual(actual, expected []byte, msg ...any) bool
- func (t *C) NotContains(actual, expected any, msg ...any) bool
- func (t *C) NotDeepEqual(actual, expected any, msg ...any) bool
- func (t *C) NotEqual(actual, expected any, msg ...any) bool
- func (t *C) NotErr(actual, expected error, msg ...any) bool
- func (t *C) NotHasKey(actual, expected any, msg ...any) bool
- func (t *C) NotHasPrefix(actual, expected any, msg ...any) bool
- func (t *C) NotHasSuffix(actual, expected any, msg ...any) bool
- func (t *C) NotHasType(actual, expected any, msg ...any) bool
- func (t *C) NotImplements(actual, expected any, msg ...any) bool
- func (t *C) NotInDelta(actual, expected, delta any, msg ...any) bool
- func (t *C) NotInSMAPE(actual, expected any, smape float64, msg ...any) bool
- func (t *C) NotLen(actual any, expected int, msg ...any) bool
- func (t *C) NotMatch(actual, regex any, msg ...any) bool
- func (t *C) NotNil(actual any, msg ...any) bool
- func (t *C) NotPanic(actual func(), msg ...any) bool
- func (t *C) NotZero(actual any, msg ...any) bool
- func (t *C) Panic(actual func(), msg ...any) bool
- func (t *C) PanicMatch(actual func(), regex any, msg ...any) bool
- func (t *C) PanicNotMatch(actual func(), regex any, msg ...any) bool
- func (t *C) Parallel()
- func (t *C) Should(anyShouldFunc any, args ...any) bool
- func (t *C) TODO() *C
- func (t *C) True(cond bool, msg ...any) bool
- func (t *C) Zero(actual any, msg ...any) bool
- type ShouldFunc1
- type ShouldFunc2
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
Types ¶
type C ¶ added in v1.0.0
C wraps *testing.T to make it convenient to call checkers in test.
func T ¶
T creates and returns new *C, which wraps given tt and supposed to be used inplace of it, providing you with access to many useful helpers in addition to standard methods of *testing.T.
It's convenient to rename Test function's arg from t to something else, create wrapped variable with usual name t and use only t:
func TestSomething(tt *testing.T) { t := check.T(tt) // use only t in test and don't touch tt anymore }
func (*C) Between ¶ added in v1.0.0
Between checks for min < actual < max.
All three actual, min and max must be either:
- signed integers
- unsigned integers
- floats
- strings
- time.Time
func (*C) BetweenOrEqual ¶ added in v1.0.0
BetweenOrEqual checks for min <= actual <= max.
All three actual, min and max must be either:
- signed integers
- unsigned integers
- floats
- strings
- time.Time
func (*C) BytesEqual ¶ added in v1.0.0
BytesEqual checks for bytes.Equal(actual, expected).
Hint: BytesEqual([]byte{}, []byte(nil)) is true (unlike DeepEqual).
func (*C) Contains ¶ added in v1.0.0
Contains checks is actual contains substring/element expected.
Element of array/slice/map is checked using == expected.
Type of expected depends on type of actual:
- if actual is a string, then expected should be a string
- if actual is an array, then expected should have array's element type
- if actual is a slice, then expected should have slice's element type
- if actual is a map, then expected should have map's value type
Hint: In a map it looks for a value, if you need to look for a key - use HasKey instead.
func (*C) DeepEqual ¶ added in v1.0.0
DeepEqual checks for reflect.DeepEqual(actual, expected). It will also use Equal method for types which implements it (e.g. time.Time, decimal.Decimal, etc.). It will use proto.Equal for protobuf messages.
func (*C) Equal ¶ added in v1.0.0
Equal checks for actual == expected.
Note: For time.Time it uses actual.Equal(expected) instead.
func (*C) Err ¶ added in v1.0.0
Err checks is actual error is the same as expected error.
If errors.Is() fails then it'll use more sofiscated logic:
It tries to recursively unwrap actual before checking using errors.Unwrap() and github.com/pkg/errors.Cause(). In case of multi-error (Unwrap() []error) it use only first error.
It will use proto.Equal for gRPC status errors.
They may be a different instances, but must have same type and value.
Checking for nil is okay, but using Nil(actual) instead is more clean.
func (*C) Error ¶ added in v1.4.0
Error is equivalent to Log followed by Fail.
It is like t.Errorf with TODO() and statistics support.
func (*C) Greater ¶ added in v1.0.0
Greater checks for actual > expected.
Both actual and expected must be either:
- signed integers
- unsigned integers
- floats
- strings
- time.Time
func (*C) GreaterOrEqual ¶ added in v1.0.0
GreaterOrEqual checks for actual >= expected.
Both actual and expected must be either:
- signed integers
- unsigned integers
- floats
- strings
- time.Time
func (*C) HasPrefix ¶ added in v1.0.0
HasPrefix checks for strings.HasPrefix(actual, expected).
Both actual and expected may have any of these types:
- string - will use as is
- []byte - will convert with string()
- []rune - will convert with string()
- fmt.Stringer - will convert with actual.String()
- error - will convert with actual.Error()
- nil - check will always fail
func (*C) HasSuffix ¶ added in v1.0.0
HasSuffix checks for strings.HasSuffix(actual, expected).
Both actual and expected may have any of these types:
- string - will use as is
- []byte - will convert with string()
- []rune - will convert with string()
- fmt.Stringer - will convert with actual.String()
- error - will convert with actual.Error()
- nil - check will always fail
func (*C) Implements ¶ added in v1.0.0
Implements checks is actual implements interface pointed by expected.
You must use pointer to interface type in expected:
t.Implements(os.Stdin, (*io.Reader)(nil))
func (*C) InDelta ¶ added in v1.0.0
InDelta checks for expected-delta <= actual <= expected+delta.
All three actual, expected and delta must be either:
- signed integers
- unsigned integers
- floats
- time.Time (in this case delta must be time.Duration)
func (*C) InSMAPE ¶ added in v1.0.0
InSMAPE checks that actual and expected have a symmetric mean absolute percentage error (SMAPE) is less than given smape.
Both actual and expected must be either:
- signed integers
- unsigned integers
- floats
Allowed smape values are: 0.0 < smape < 100.0.
Used formula returns SMAPE value between 0 and 100 (percents):
- 0.0 when actual == expected
- ~0.5 when they differs in ~1%
- ~5 when they differs in ~10%
- ~20 when they differs in 1.5 times
- ~33 when they differs in 2 times
- 50.0 when they differs in 3 times
- ~82 when they differs in 10 times
- 99.0+ when actual and expected differs in 200+ times
- 100.0 when only one of actual or expected is 0 or one of them is positive while another is negative
func (*C) JSONEqual ¶ added in v1.0.0
JSONEqual normalize formatting of actual and expected (if they're valid JSON) and then checks for bytes.Equal(actual, expected).
Both actual and expected may have any of these types:
- string
- []byte
- json.RawMessage
- *json.RawMessage
- nil
In case any of actual or expected is nil or empty or (for string or []byte) is invalid JSON - check will fail.
func (*C) Less ¶ added in v1.0.0
Less checks for actual < expected.
Both actual and expected must be either:
- signed integers
- unsigned integers
- floats
- strings
- time.Time
func (*C) LessOrEqual ¶ added in v1.0.0
LessOrEqual checks for actual <= expected.
Both actual and expected must be either:
- signed integers
- unsigned integers
- floats
- strings
- time.Time
func (*C) Match ¶ added in v1.0.0
Match checks for regex.MatchString(actual).
Regex type can be either *regexp.Regexp or string.
Actual type can be:
- string - will match with actual
- []byte - will match with string(actual)
- []rune - will match with string(actual)
- fmt.Stringer - will match with actual.String()
- error - will match with actual.Error()
- nil - will not match (even with empty regex)
func (*C) Must ¶ added in v1.0.0
Must interrupt test using t.FailNow if called with false value.
This provides an easy way to turn any check into assertion:
t.Must(t.Nil(err))
func (*C) MustAll ¶ added in v1.5.0
MustAll creates and returns new *C, which have only one difference from original one: every failed check will interrupt test using t.FailNow. You can continue using both old and new *C at same time.
This provides an easy way to turn all checks into assertion.
func (*C) Nil ¶ added in v1.0.0
Nil checks for actual == nil.
There is one subtle difference between this check and Go `== nil` (if this surprises you then you should read https://golang.org/doc/faq#nil_error first):
var intPtr *int var empty interface{} var notEmpty interface{} = intPtr t.True(intPtr == nil) // TRUE t.True(empty == nil) // TRUE t.True(notEmpty == nil) // FALSE
When you call this function your actual value will be stored in interface{} argument, and this makes any typed nil pointer value `!= nil` inside this function (just like in example above happens with notEmpty variable).
As it is very common case to check some typed pointer using Nil this check has to work around and detect nil even if usual `== nil` return false. But this has nasty side effect: if actual value already was of interface type and contains some typed nil pointer (which is usually bad thing and should be avoid) then Nil check will pass (which may be not what you want/expect):
t.Nil(nil) // TRUE t.Nil(intPtr) // TRUE t.Nil(empty) // TRUE t.Nil(notEmpty) // WARNING: also TRUE!
Second subtle case is less usual: uintptr(0) is sorta nil, but not really, so Nil(uintptr(0)) will fail. Nil(unsafe.Pointer(nil)) will also fail, for the same reason. Please do not use this and consider this behaviour undefined, because it may change in the future.
func (*C) NotBetween ¶ added in v1.0.0
NotBetween checks for actual <= min or max <= actual.
All three actual, min and max must be either:
- signed integers
- unsigned integers
- floats
- strings
- time.Time
func (*C) NotBetweenOrEqual ¶ added in v1.0.0
NotBetweenOrEqual checks for actual < min or max < actual.
All three actual, min and max must be either:
- signed integers
- unsigned integers
- floats
- strings
- time.Time
func (*C) NotBytesEqual ¶ added in v1.0.0
NotBytesEqual checks for !bytes.Equal(actual, expected).
Hint: NotBytesEqual([]byte{}, []byte(nil)) is false (unlike NotDeepEqual).
func (*C) NotContains ¶ added in v1.0.0
NotContains checks is actual not contains substring/element expected.
See Contains about supported actual/expected types and check logic.
func (*C) NotDeepEqual ¶ added in v1.0.0
NotDeepEqual checks for !reflect.DeepEqual(actual, expected). It will also use Equal method for types which implements it (e.g. time.Time, decimal.Decimal, etc.). It will use proto.Equal for protobuf messages.
func (*C) NotErr ¶ added in v1.0.0
NotErr checks is actual error is not the same as expected error.
It tries to recursively unwrap actual before checking using errors.Unwrap() and github.com/pkg/errors.Cause(). In case of multi-error (Unwrap() []error) it use only first error.
It will use !proto.Equal for gRPC status errors.
They must have either different types or values (or one should be nil). Different instances with same type and value will be considered the same error, and so is both nil.
Finally it'll use !errors.Is().
func (*C) NotHasPrefix ¶ added in v1.0.0
NotHasPrefix checks for !strings.HasPrefix(actual, expected).
See HasPrefix about supported actual/expected types and check logic.
func (*C) NotHasSuffix ¶ added in v1.0.0
NotHasSuffix checks for !strings.HasSuffix(actual, expected).
See HasSuffix about supported actual/expected types and check logic.
func (*C) NotHasType ¶ added in v1.0.0
NotHasType checks is actual has not same type as expected.
func (*C) NotImplements ¶ added in v1.0.0
NotImplements checks is actual does not implements interface pointed by expected.
You must use pointer to interface type in expected:
t.NotImplements(os.Stdin, (*fmt.Stringer)(nil))
func (*C) NotInDelta ¶ added in v1.0.0
NotInDelta checks for actual < expected-delta or expected+delta < actual.
All three actual, expected and delta must be either:
- signed integers
- unsigned integers
- floats
- time.Time (in this case delta must be time.Duration)
func (*C) NotInSMAPE ¶ added in v1.0.0
NotInSMAPE checks that actual and expected have a symmetric mean absolute percentage error (SMAPE) is greater than or equal to given smape.
See InSMAPE about supported actual/expected types and check logic.
func (*C) NotMatch ¶ added in v1.0.0
NotMatch checks for !regex.MatchString(actual).
See Match about supported actual/regex types and check logic.
func (*C) NotNil ¶ added in v1.0.0
NotNil checks for actual != nil.
See Nil about subtle case in check logic.
func (*C) NotPanic ¶ added in v1.0.0
NotPanic checks is actual() don't panics.
It is able to detect panic(nil)… but you should try to avoid using this.
func (*C) Panic ¶ added in v1.0.0
Panic checks is actual() panics.
It is able to detect panic(nil)… but you should try to avoid using this.
func (*C) PanicMatch ¶ added in v1.0.0
PanicMatch checks is actual() panics and panic text match regex.
Regex type can be either *regexp.Regexp or string.
In case of panic(nil) it will match like panic("<nil>").
func (*C) PanicNotMatch ¶ added in v1.0.0
PanicNotMatch checks is actual() panics and panic text not match regex.
Regex type can be either *regexp.Regexp or string.
In case of panic(nil) it will match like panic("<nil>").
func (*C) Parallel ¶ added in v1.0.0
func (t *C) Parallel()
Parallel implements an internal workaround which have no visible effect, so you should just call t.Parallel() as you usually do - it will work as expected.
func (*C) Should ¶ added in v1.0.0
Should use user-provided check function to do actual check.
anyShouldFunc must have type ShouldFunc1 or ShouldFunc2. It should return true if check was successful. There is no need to call t.Error in anyShouldFunc - this will be done automatically when it returns.
args must contain at least 1 element for ShouldFunc1 and at least 2 elements for ShouldFunc2. Rest of elements will be processed as usual msg ...interface{} param.
Example:
func bePositive(_ *check.C, actual interface{}) bool { return actual.(int) > 0 } func TestCustomCheck(tt *testing.T) { t := check.T(tt) t.Should(bePositive, 42, "custom check!!!") }
func (*C) TODO ¶ added in v1.0.0
TODO creates and returns new *C, which have only one difference from original one: every passing check is now handled as failed and vice versa (this doesn't affect boolean value returned by check). You can continue using both old and new *C at same time.
Swapping passed/failed gives you ability to temporary mark some failed test as passed. For example, this may be useful to avoid broken builds in CI. This is often better than commenting, deleting or skipping broken test because it will continue to execute, and eventually when reason why it fails will be fixed this test will became failed again - notifying you the mark can and should be removed from this test now.
func TestSomething(tt *testing.T) { t := check.T(tt) // Normal tests. t.True(true) // If you need to mark just one/few broken tests: t.TODO().True(false) t.True(true) // If there are several broken tests mixed with working ones: todo := t.TODO() t.True(true) todo.True(false) t.True(true) if todo.True(false) { panic("never here") } // If all tests below this point are broken: t = t.TODO() t.True(false) ... }
type ShouldFunc2 ¶
ShouldFunc2 is like Equal or Match.