Documentation
¶
Overview ¶
Package attest is a small, type-safe library of assertion helpers.
Under the hood, attest uses cmp to test equality and diff values. All of attest's assertions work with any cmp.Option.
Index ¶
- func Approximately[T Number](tb TB, got, want, delta T, opts ...Option) bool
- func Contains[T any](tb TB, got []T, want T, opts ...Option) bool
- func Equal[T any](tb TB, got, want T, opts ...Option) bool
- func Error(tb TB, err error, opts ...Option) bool
- func ErrorIs(tb TB, got, want error, opts ...Option) bool
- func False(tb TB, got bool, opts ...Option) bool
- func NotEqual[T any](tb TB, got, want T, opts ...Option) bool
- func NotZero[T any](tb TB, got T, opts ...Option) bool
- func Ok(tb TB, err error, opts ...Option) bool
- func Panics(tb TB, f func(), opts ...Option) (ret bool)
- func Subsequence[T ~string | ~[]byte](tb TB, got, want T, opts ...Option) bool
- func True(tb TB, got bool, opts ...Option) bool
- func Zero[T any](tb TB, got T, opts ...Option) bool
- type Number
- type Option
- type TB
Examples ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func Approximately ¶
Approximately asserts that got is within delta of want. For example,
pi := float64(22)/7 Approximately(t, pi, 3.14, 0.01)
asserts that our estimate of pi is between 3.13 and 3.15, exclusive.
Approximately works with any type whose underlying type is numeric, so it also works with time.Duration.
func ErrorIs ¶
ErrorIs asserts that got wraps want, using the same logic as the standard library's errors.Is.
func Subsequence ¶
Subsequence asserts that got contains the subsequence want.
Subsequence(t, "hello world", "hello") Subsequence(t, []byte("deadbeef"), []byte("ee"))
Types ¶
type Number ¶
type Number interface { ~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 | ~uintptr | ~int | ~int8 | ~int16 | ~int32 | ~int64 | ~float32 | ~float64 }
A Number is any type whose underlying type is one of Go's built-in integral or floating-point types.
type Option ¶
type Option interface {
// contains filtered or unexported methods
}
An Option configures assertions.
func Allow ¶
Allow configures the underlying cmp package to forcibly introspect unexported fields of the specified struct types. (By default, cmp errors when comparing structs with unexported fields.) Allow fails the test if called with anything other than a struct type (for example, a pointer or slice).
Allow is useful as a quick hack but is usually a bad idea: changes in the internals of some other package may break your tests. If you control the type in question, implement an Equal method instead. If you don't control the type, Comparer is usually safer.
Example ¶
package main import ( "fmt" "go.akshayshah.org/attest" ) // logTB implements the portions of the [testing.TB] interface that attest uses. // It writes assertion failures to stdout. type logTB struct{} func (*logTB) Helper() {} func (*logTB) Errorf(tmpl string, args ...any) { fmt.Printf("ERROR: "+tmpl, args...) } func (*logTB) Fatalf(tmpl string, args ...any) { fmt.Printf("FATAL: "+tmpl, args...) } type point struct { x, y float64 } func main() { attest.Equal( &logTB{}, point{1.0, 1.0}, point{1.0, 1.0}, // Without Allow, cmp errors because point has unexported fields. We could // also use [Comparer], or we could implement an Equal method on point. attest.Allow(point{}), ) }
Output:
func Cmp ¶
Cmp configures the underlying equality assertion, if any. See the cmp package documentation for an explanation of the default logic.
In particular, note that the cmp package's default behavior is to error when comparing structs with unexported fields. If you control the type in question, implement an Equal method and cmp will use it by default. If you don't control the type, use Allow or Comparer. If none of those approaches fit your needs, cmp and its cmpopts subpackage offer many other ways to relax this safety check.
If you're comparing types generated from a Protocol Buffer schema, protocmp.Transform safely transforms them to a comparable, diffable type.
func Comparer ¶
Comparer configures the underlying cmp package to compare values of type T using the provided function. This is especially useful when comparing third-party types with unexported fields.
The equality function must be symmetric (the order of the two arguments doesn't matter), deterministic (it always returns the same result), and pure (it may not mutate its arguments).
Example ¶
package main import ( "fmt" "go.akshayshah.org/attest" ) // logTB implements the portions of the [testing.TB] interface that attest uses. // It writes assertion failures to stdout. type logTB struct{} func (*logTB) Helper() {} func (*logTB) Errorf(tmpl string, args ...any) { fmt.Printf("ERROR: "+tmpl, args...) } func (*logTB) Fatalf(tmpl string, args ...any) { fmt.Printf("FATAL: "+tmpl, args...) } type point struct { x, y float64 } func main() { attest.Equal( &logTB{}, point{1.0, 1.0}, point{1.0, 1.0}, // Without Comparer, cmp errors because point has unexported fields. We // could also use [Allow], or we could implement an Equal method on point. attest.Comparer(func(left, right point) bool { return left.x == right.x && left.y == right.y }), ) }
Output:
func Continue ¶
func Continue() Option
Continue allows the test to continue executing when an assertion fails. By default, failed assertions stop the test immediately.
func Fatal ¶
func Fatal() Option
Fatal stops the test immediately when an assertion fails. This is the default behavior, but Fatal may still be useful to reverse the effect of Continue.
func Options ¶
Options composes multiple Options into one. This may be useful if you're writing a helper package that bundles several options together, or if most assertions in your tests use a common set of options.
Example ¶
package main import ( "fmt" "go.akshayshah.org/attest" ) // logTB implements the portions of the [testing.TB] interface that attest uses. // It writes assertion failures to stdout. type logTB struct{} func (*logTB) Helper() {} func (*logTB) Errorf(tmpl string, args ...any) { fmt.Printf("ERROR: "+tmpl, args...) } func (*logTB) Fatalf(tmpl string, args ...any) { fmt.Printf("FATAL: "+tmpl, args...) } type point struct { x, y float64 } func main() { // If all our tests have some options in common, it's nice to extract them // into a named bundle. defaults := attest.Options( attest.Continue(), attest.Comparer(func(left, right point) bool { return left.x == right.x && left.y == right.y }), ) // We can reuse our default options in each test, and we can add more options // without an ugly cascade of appends. attest.Zero( &logTB{}, point{}, defaults, // our defaults attest.Fatal(), // override Continue from defaults ) }
Output:
func Sprint ¶
Sprint adds an explanation to the default failure message. If your tests make many similar assertions, the additional explanation may clarify the test output.
Arguments are passed to fmt.Sprint for formatting.
Example ¶
package main import ( "fmt" "time" "go.akshayshah.org/attest" ) // logTB implements the portions of the [testing.TB] interface that attest uses. // It writes assertion failures to stdout. type logTB struct{} func (*logTB) Helper() {} func (*logTB) Errorf(tmpl string, args ...any) { fmt.Printf("ERROR: "+tmpl, args...) } func (*logTB) Fatalf(tmpl string, args ...any) { fmt.Printf("FATAL: "+tmpl, args...) } func main() { today := time.Now() tomorrow := today.Add(24 * time.Hour) attest.False( &logTB{}, today.Before(tomorrow), attest.Sprint("alas, time", " marches on"), ) }
Output: FATAL: got true, want false: alas, time marches on
func Sprintf ¶
Sprintf adds an explanation to the default failure message. If your tests make many similar assertions, the additional explanation may clarify the test output.
Arguments are passed to fmt.Sprintf for formatting.
Example ¶
package main import ( "fmt" "time" "go.akshayshah.org/attest" ) // logTB implements the portions of the [testing.TB] interface that attest uses. // It writes assertion failures to stdout. type logTB struct{} func (*logTB) Helper() {} func (*logTB) Errorf(tmpl string, args ...any) { fmt.Printf("ERROR: "+tmpl, args...) } func (*logTB) Fatalf(tmpl string, args ...any) { fmt.Printf("FATAL: "+tmpl, args...) } func main() { today := time.Now() tomorrow := today.Add(24 * time.Hour) attest.False( &logTB{}, today.Before(tomorrow), attest.Sprintf("%s, time marches on", "alas"), ) }
Output: FATAL: got true, want false: alas, time marches on