assert

package module
v0.26.2 Latest Latest
Warning

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

Go to latest
Published: Sep 4, 2026 License: MIT Imports: 5 Imported by: 0

README

Assert Go Reference Coverage Status

Package assert is the minimalist testing helper for Go.

Inspired by Mat Ryer and Alex Edwards.

Features

  • Simple and readable test assertions using generics, chainable off a Tester
  • Built-in helpers for common cases like Tester.OK and Tester.Equal
  • Choose Fail or FailNow semantics depending on how you construct your Tester (assert.Continue(t) or assert.FailNow(t))
  • Helpers for testing against golden files with the testfile subpackage
  • No sub-dependencies: just uses standard library

Example usage

Test for simple equality using generics:

// First create a testing helper
be := assert.Continue(t)

// Test two unequal strings
be.Equal("hello", "world")    // bad
// t.Fail(); t.Log("want: world; got: hello")
// Test two equal strings
be.Equal("goodbye", "goodbye") // good
// Test equal integers, etc.
be.Equal(resp.StatusCode, 200)
be.Equal(gotPtr, tc.wantPtr)

// Test for inequality
be.NotEqual("hello", "world")    // good
be.NotEqual("goodbye", "goodbye") // bad
// t.Fail(); t.Log("got: goodbye")

Chain related tests:

be.
	Equal(x, 1).
	Equal(y, 2)

Test for equality of slices:

s := []int{1, 2, 3}
be.SlicesEqual([]int{1, 2, 3}, s) // good
be.SlicesEqual([]int{3, 2, 1}, s) // bad
// t.Fail(); t.Log("got: [3 2 1]; want: [1 2 3]")

Handle errors:

f := be.OK(os.Open("nosuchfile")) // bad, and also returns nil *os.File
be.Zero(f)                        // good

var err error
be.Zero(err)                      // good
be.ErrorIs(nil, err)              // good
be.NotZero(err)                   // bad
be.ErrorIs(err, os.ErrPermission) // bad

err = errors.New("(O_o)")
be.ErrorAsType[*os.PathError](err) // bad
be.NotZero(err)                    // good

Check for regexp matching:

be.Match(mystring, `world`)               // good
be.Match(mystring, `World`)               // bad
// t.Fail(); t.Log(`missing match: /World/ !~ "hello, world"`)
be.Match([]byte("\a\b\x00\r\t"), `^\W*$`)    // good
be.NotMatch([]byte("\a\b\x00\r\t"), `^\W*$`) // bad

Check how long something rangeable is:

seq := strings.FieldsSeq("1 2 3 4")
be.EqualLength(seq, 4)     // good
be.EqualLength(seq, 1)     // bad
be.AtLeastLength(seq, 1)   // good
be.AtLeastLength(seq, 5)   // bad
be.AtLeastLength("123", 3) // good
be.AtLeastLength("123", 4) // bad

Test anything else:

be.True(o.IsValid())

Test using goldenfiles:

// Start a sub-test for each .txt file
testfile.Run(t, "testdata/*.txt", func(t *testing.T, path string) {
	// Read the file
	input := testfile.Read(t, path)

	// Do some conversion on it
	type myStruct struct{ Whatever string }
	got := myStruct{strings.ToUpper(input)}

	// See if the struct is equivalent to a .json file
	wantFile := testfile.Ext(path, ".json")
	testfile.EqualJSON(t, wantFile, got)

	// If it's not equivalent,
	// the got struct will be dumped
	// to a file named testdata/-failed-test-name.json
})

Philosophy

Tests usually should not fail. When they do fail, the failure should be repeatable. Therefore, it doesn't make sense to spend a lot of time writing good test messages. (This is unlike error messages, which should happen fairly often, and in production, irrepeatably.) Package assert is designed to simply fail a test quickly and quietly if a condition is not met with a reference to the line number of the failing test. If the reason for having the test is not immediately clear from context, you can write a comment, just like in normal code. If you do need more extensive reporting to figure out why a test is failing, use testing.TB.Log to capture more information.

The assertions in package assert are methods of the Tester type, which captures a *testing.T or *testing.B and can either call testing.TB.Fail or testing.TB.FailNow on failure depending on how you want the assertions to work.

Most tests just need simple equality testing, which is handled by Tester.Equal (for comparable types), Tester.SlicesEqual (for slices of comparable types), and Tester.DeepEqual (which relies on reflect.DeepEqual). Another common test is that a string or byte slice should contain or not some substring, which is handled by Tester.Match and Tester.NotMatch. Rather than package assert providing every possible test helper, you are encouraged to write your own advanced helpers for use with Tester.True, while package assert takes away the drudgery of writing yet another simple func nilErr(t *testing.T, err) { ... }.

The github.com/earthboundkid/assert/testfile subpackage has functions that make it easy to write file-based tests that ensure that the output of some transformation matches a golden file. Subtests can automatically be run for all files matching a glob pattern, such as testfile.Run(t, "testdata/*/input.txt", ...). If the test fails, the failure output will be written to a file, such as "testdata/basic-test/-failed-output.txt", and then the output can be examined via diff testing with standard tools. Set the environmental variable TESTFILE_UPDATE to update the golden file.

Documentation

Overview

Package assert is a minimalist test assertion helper library.

Philosophy

Tests usually should not fail. When they do fail, the failure should be repeatable. Therefore, it doesn't make sense to spend a lot of time writing good test messages. (This is unlike error messages, which should happen fairly often, and in production, irrepeatably.) Package assert is designed to simply fail a test quickly and quietly if a condition is not met with a reference to the line number of the failing test. If the reason for having the test is not immediately clear from context, you can write a comment, like normal code. If you do need more extensive reporting to figure out why a test is failing, use *testing.T.Log to capture more information.

The assertions in assert package are methods of the Tester type, which captures a *testing.T or *testing.B and can either call testing.TB.Fail or testing.TB.FailNow on failure depending on how you want the assertions to work.

Most tests just need simple equality testing, which is handled by Tester.Equal (for comparable types), Tester.SlicesEqual (for slices of comparable types), and Tester.DeepEqual (which relies on reflect.DeepEqual). Another common test is that a string or byte slice should contain or not some substring, which is handled by Tester.Match and Tester.NotMatch. Rather than package assert providing every possible test helper, you are encouraged to write your own advanced helpers for use with Tester.True, while package assert takes away the drudgery of writing yet another simple func nilErr(t *testing.T, err) { ... }.

The github.com/earthboundkid/assert/testfile subpackage has functions that make it easy to write file-based tests that ensure that the output of some transformation matches a golden file. Subtests can automatically be run for all files matching a glob pattern, such as testfile.Run(t, "testdata/*/input.txt", ...). If the test fails, the failure output will be written to a file, such as "testdata/basic-test/-failed-output.txt", and then the output can be examined via diff testing with standard tools. Set the environmental variable TESTFILE_UPDATE to update the golden file.

Example
be := assert.Continue(&mockingT{})

be.
	Equal("hello", "world").    // bad
	Equal("goodbye", "goodbye") // good
be.
	NotEqual("hello", "world").    // good
	NotEqual("goodbye", "goodbye") // bad

s := []int{1, 2, 3}
be.
	SlicesEqual([]int{1, 2, 3}, s). // good
	SlicesEqual([]int{3, 2, 1}, s)  // bad

f := be.OK(os.Open("nosuchfile")) // bad
be.Zero(f)                        // good

var err error
be.
	Zero(err).                     // good
	ErrorIs(nil, err).             // good
	NotZero(err).                  // bad
	ErrorIs(err, os.ErrPermission) // bad

err = errors.New("(O_o)")
be.ErrorAsType[*os.PathError](err) // bad
be.NotZero(err)                    // good

type mytype string
var mystring mytype = "hello, world"
be.
	Match(mystring, `world`).                 // good
	Match(mystring, `World`).                 // bad
	Match([]byte("\a\b\x00\r\t"), `^\W*$`).   // good
	NotMatch([]byte("\a\b\x00\r\t"), `^\W*$`) // bad

seq := strings.FieldsSeq("1 2 3 4")
be.
	EqualLength(seq, 4).     // good
	EqualLength(seq, 1).     // bad
	AtLeastLength(seq, 1).   // good
	AtLeastLength(seq, 5).   // bad
	AtLeastLength("123", 3). // good
	AtLeastLength("123", 4)  // bad
Output:
want: world; got: hello
got: goodbye
got: [3 2 1]; want: [1 2 3]
err != nil: open nosuchfile: no such file or directory
got: <nil>
got errors.Is(<nil>, permission denied) == false
got errors.AsType[*fs.PathError]((O_o)) == false
missing match: /World/ !~ "hello, world"
unexpected match: /^\W*$/ =~ "\a\b\x00\r\t"
want len(seq) == 1; got at least 2
want len(seq) >= 5; got 4
want len(seq) >= 4; got 3

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

func Catch

func Catch(fn func()) (r any)

Catch runs the callback and returns the recovered panic, if any.

func Truthy

func Truthy[T any](v T) bool

Truthy returns

  • !v.IsZero(), for types with an IsZero() method.
  • len(v) != 0, for slices and maps.
  • v != the zero value of T, for all other types.
Example
package main

import (
	"fmt"

	"github.com/earthboundkid/assert"
)

func main() {
	fmt.Printf("%#v is %t-ish\n", 0, assert.Truthy(0))
	fmt.Printf("%#v is %t-ish\n", 1, assert.Truthy(1))
	fmt.Printf("%#v is %t-ish\n", "", assert.Truthy(""))
	fmt.Printf("%#v is %t-ish\n", "hi", assert.Truthy("hi"))
	fmt.Printf("%#v is %t-ish\n", error(nil), assert.Truthy[error](nil))
}
Output:
0 is false-ish
1 is true-ish
"" is false-ish
"hi" is true-ish
<nil> is false-ish

Types

type Tester

type Tester struct {
	// contains filtered or unexported fields
}

Tester is a type that wraps a *testing.T or *testing.B and adds methods for doing assertion tests against that test manager.

The methods of testing.TB that Tester uses are Helper, Logf, and FailNow (for FailNow) or Fail (for Continue).

func Continue

func Continue(t testing.TB) Tester

Continue returns a Tester will continue testing even after an assertion failure. It calls *testing.T.Fail.

func FailNow

func FailNow(t testing.TB) Tester

FailNow returns a Tester will end the test after an assertion failure with *testing.T.FailNow.

func (Tester) AtLeastLength

func (be Tester) AtLeastLength(seq any, want int) Tester

AtLeastLength asserts that seq has a length that is at least want.

The type of seq must be array, array pointer, slice, map, string, channel, iter.Seq, or iter.Seq2. For channels and iterators, the values are consumed to get the sequence length.

func (Tester) DeepEqual

func (be Tester) DeepEqual[T any](got, want T) Tester

DeepEqual asserts got is reflect.DeepEqual to want.

Prefer to use Tester.SlicesEqual if possible.

Example
be := assert.Continue(&mockingT{})

// good
m1 := map[int]bool{1: true, 2: false}
m2 := map[int]bool{1: true, 2: false}
be.DeepEqual(m1, m2)

// bad
var s1 []int
s2 := []int{}
be.DeepEqual(s1, s2) // DeepEqual is picky about nil vs. len 0
Output:
reflect.DeepEqual([]int(nil), []int{}) == false

func (Tester) Equal

func (be Tester) Equal[T comparable](got, want T) Tester

Equal asserts that got == want.

func (Tester) EqualLength

func (be Tester) EqualLength(seq any, want int) Tester

EqualLength asserts that seq has a length that is exactly want.

The type of seq must be array, array pointer, slice, map, string, channel, iter.Seq, or iter.Seq2. For channels and iterators, the values are consumed to get the sequence length.

func (Tester) ErrorAsType

func (be Tester) ErrorAsType[T error](got error) T

ErrorAsType asserts that errors.AsType can unwrap got as T.

func (Tester) ErrorIs

func (be Tester) ErrorIs(got, target error) Tester

ErrorIs asserts that got errors.Is target.

func (Tester) False

func (be Tester) False(value bool) Tester

False asserts that value is false.

func (Tester) Match

func (be Tester) Match[byteseq ~string | ~[]byte](got byteseq, pattern string) Tester

Match asserts got matches the regexp pattern.

The pattern must compile.

func (Tester) NotEqual

func (be Tester) NotEqual[T comparable](got, bad T) Tester

NotEqual asserts that got != want.

func (Tester) NotMatch

func (be Tester) NotMatch[byteseq ~string | ~[]byte](got byteseq, pattern string) Tester

NotMatch asserts got does not matches the regexp pattern.

The pattern must compile.

func (Tester) NotZero

func (be Tester) NotZero[T any](value T) Tester

NotZero asserts value is Truthy.

func (Tester) OK

func (be Tester) OK[T any](value T, err error) T

OK asserts that error is nil and returns value. Typical use is like

v := be.OK(canFail())
Example
be := assert.Continue(&mockingT{})

// be.OK asserts the error returned is nil
// and returns the value for subsequent testing.
f := be.OK(os.Open("nosuchfile"))
if f != nil {
	f.Close()
}
Output:
err != nil: open nosuchfile: no such file or directory

func (Tester) Panicked

func (be Tester) Panicked(fn func()) Tester

Panicked asserts that fn panics when run.

Example
be := assert.Continue(&mockingT{})

divide := func(num, denom int) int {
	return num / denom
}

// Test that division by zero panics
be.Panicked(func() {
	divide(1, 0)
})

// Because a panic fails a test by default,
// testing that an operation does not panic is less necessary,
// but may be helpful in a table test.
for _, testcase := range []struct {
	num, denom, want int
	shouldPanic      bool
}{
	{0, 1, 0, false},
	{1, 1, 1, false},
	{1, 0, 0xbadc0ffee, true},
	{0, 0, 0xbadc0ffee, true},
} {
	got := 0xbadc0ffee
	panicVal := assert.Catch(func() {
		got = divide(testcase.num, testcase.denom)
	})
	be.Equal(got, testcase.want)
	be.Equal(panicVal != nil, testcase.shouldPanic)
}

func (Tester) SlicesEqual

func (be Tester) SlicesEqual[T comparable](got, want []T) Tester

SlicesEqual asserts that slices.Equal(got, want).

func (Tester) True

func (be Tester) True(value bool) Tester

True asserts that value is true.

func (Tester) Zero

func (be Tester) Zero[T any](value T) Tester

Zero asserts value is not Truthy.

Directories

Path Synopsis
Package testfile has test helpers that work by comparing files.
Package testfile has test helpers that work by comparing files.

Jump to

Keyboard shortcuts

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