gotest

package module
v0.2.2 Latest Latest
Warning

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

Go to latest
Published: Nov 21, 2025 License: MIT Imports: 14 Imported by: 0

README

gotest

Go Reference Test status

A unit testing library for Golang

Based on GoogleTest. Built with, and interoperable with, GoMock.

This library includes a collection of composable Matcher implementations, which can be put together to form partial matches, match elements of collection, and so on. They can be used to match calls to a mock object, or as standalone test assertions.

This package is also natively protobuf-aware. Protobufs in Go contain control fields and mutexes that are not comparable, and so must be compared with their own reflection library. Go-native operations such as reflect.DeepEqual() do not know how to do so. The implementation of Eq() included in this package can handle protos, including fields of type proto nested inside other structs, so that matchers like Eq() work as intended.

Assertions

The library provides two ways to set test expectations/assertions:

  • Expect for non-fatal assertions (failure will continue the test)
  • Assert for fatal assertions (failure will terminate the test). Useful for checking preconditions without which later test code will panic.

Each family has a few variants:

  • [Expect|Assert]That(t, value, matcher): the most general form, takes any Matcher and checks value against it.
  • [Expect|Assert]Eq(t, value, expected): shorthand for equality matching (with Eq())
  • [Expect|Assert]Fatal(t, errMatcher, f): runs function f and checks that it causes a panic matching errMatcher.

Matcher Composition

Matcher implementations are provided for common conditions - equality, string operations, numeric comparisons.

More powerful matchers can be composed to express complex structural expectations on container types (slices and maps). Any composable matcher supports either sub-matchers to match elements, or raw values to be checked by equality (wrapped in an Eq() matcher).

For example, validating a nested map-of-slices structure where different slices have different ordering requirements:

teams := map[string][]User{
    "engineering": {{"Alice", 30}, {"Bob", 25}},
    "product": {{"Charlie", 35}},
    "exec": {{"Eve", 40}, {"Dave", 45}},
}

ExpectThat(t, teams, MapIs(map[string]any{
    "engineering": ElementsAreUnordered(
        User{"Bob", 25},
        User{"Alice", 30},
    ),
    "product": []User{{"Charlie", 35}},
    "exec": Contains(User{"Eve", 40}),
}))

This validates that:

  • the map has exactly these three keys,
  • that "engineering" contains these two users in any order,
  • that "product" contains exactly this one user (note that we did not need to wrap the slice in Eq() or ElementsAre(); raw values are automatically wrapped),
  • that "exec" contains at least the user "Eve" (other users may be present).

As shown in the example, some matchers support partial matching. For instance, the MapContains matcher checks for the presence of specific keys while ignoring others:

config := map[string]map[string]int{
    "group1": {"a": 1, "b": 2, "c": 3},
    "group2": {"x": 10, "y": 20, "z": 30},
    "group3": {"m": 100, "n": 200},
}

ExpectThat(t, config, MapContains(map[string]any{
    "group1": MapContains(map[string]any{"a": Lt(5)}),
    "group3": MapIs(map[string]any{"m": 100, "n": 200}),
}))

This checks that "group1" exists and contains a key "a" with a value less than 5 (other keys in "group1" are ignored), and that "group3" contains exactly the keys "m" and "n" with those specific values. The "group2" entry is not checked at all.

For a full list of supported matchers, see the GoDoc.

Mocks

gotest matchers integrate with GoMock EXPECT() calls. Given an interface:

//go:generate mockgen -destination=mocks/storage_mock.go -package=mocks . Storage

type Storage interface {
    Save(userID string, data map[string]any) error
    Query(filters map[string]any) ([]map[string]any, error)
}

Matchers can specify argument expectations that go beyond exact equality:

mockStorage.EXPECT().Save(
    StartsWith("user_"),
    MapContains(map[string]any{
        "email":    ContainsRegex(".*@example\\.com"),
        "age":      Gt(18),
        "verified": true,
    }),
).Return(nil)

This expects any call where the first argument starts with "user_" and the second argument is a map containing an "email" key matching the regex, an "age" key greater than 18, and a "verified" key equal to true. Other keys in the map are ignored.

A longer example, validating a full protocol:

gomock.InOrder(
    // First call: save batch metadata
    mockStorage.EXPECT().Save(
        Regex("batch_[0-9]+"),
        MapIs(map[string]any{
            "status":     "pending",
            "item_count": Gt(0),
            "started_at": Any(),
        }),
    ).Return(nil),

    // Multiple calls: save items
    mockStorage.EXPECT().Save(
        StartsWith("item_"),
        MapContains(map[string]any{
            "batch_id": StartsWith("batch_"),
        }),
    ).MinTimes(1),

    // Final call: mark complete
    mockStorage.EXPECT().Save(
        Regex("batch_[0-9]+"),
        MapContains(map[string]any{"status": "completed"}),
    ).Return(nil),
)

Documentation

Index

Constants

This section is empty.

Variables

View Source
var (
	Any = gomock.Any
)

Functions

func AssertEq

func AssertEq[T any](t gomock.TestHelper, actual T, expected T)

Same as ExpectEq(), but causes the test to immediately terminate on failure.

func AssertFatal

func AssertFatal(t gomock.TestHelper, errMatcher Matcher, f func())

Same as ExpectFatal(), but causes the test to immediately terminate on failure.

func AssertThat

func AssertThat(t gomock.TestHelper, val any, expected any)

Same as ExpectThat, but causes the test to immediately terminate on failure.

Useful for checking preconditions that would cause fatal errors in further test cases if violated.

Example:

AssertThat(t, someList, Not(Empty()))
ExpectThat(t, someList[0], HasSubstr("val1"))

In this scenario, the first test acts as a guard; if it fails, the second test, and any further code, shouldn't be run.

func CompareProtos added in v0.1.0

func CompareProtos() cmp.Option

func ExpectEq

func ExpectEq[T any](t gomock.TestHelper, actual T, expected T) bool

Same as ExpectThat, but more explicitly tests values of the same type for equality.

func ExpectFatal

func ExpectFatal(t gomock.TestHelper, errMatcher Matcher, f func()) (success bool)

Tests that `f()` causes a fatal error that fulfills `errMatcher`.

If the function does not panic, or if it panics with an error that doesn't match `errMatcher`, then the test represented by `t` will fail.

Example:

ExpectFatal(t, ErrorMessage(HasSubstr("bad thing")), func() {
  panic("a bad thing happened")
})  // succeeds

ExpectFatal(t, Any(), func() {
  fmt.Println("a-ok!")
})  // fails, because the function didn't panic

func ExpectThat

func ExpectThat(t gomock.TestHelper, val any, expected any) bool

Tests that `val` fulfills `expected`. If not, causes the test (`t`) to fail.

Returns the result of the check - true on success, false on failure. This can be useful to avoid running redundant checks in failure scenarios.

Normally, `expected` should be a Matcher. As a convenience, if `expected` is not a matcher, then the expectation will be Eq(expected).

Examples:

ExpectThat(t, "ab", "ab")           // succeeds
ExpectThat(t, "ab", "a")            // fails
ExpectThat(t, "ab", HasSubstr("a")) // succeeds
if !ExpectThat(t, someList, Not(Empty())) {
	// If the list is non-empty, run extra checks on the contents
	// ...else, the test fails anyway
}

func ExportFieldsFrom added in v0.1.0

func ExportFieldsFrom(pkg string) cmp.Option

func GetCallerPkg added in v0.1.0

func GetCallerPkg() (string, bool)

func IgnoreHiddenFieldsExceptFrom added in v0.1.0

func IgnoreHiddenFieldsExceptFrom(pkg string) cmp.Option

Types

type KeyValT added in v0.1.0

type KeyValT struct {
	K any
	V any
}

func KeyVal

func KeyVal(k, v any) KeyValT

type Matcher added in v0.0.2

type Matcher = gomock.Matcher

In order to ensure compatibility with generated mocks, the Matcher type in this package is the same as the one from gomock. It's re-exported here for convenience, and to improve the formatting on pkg.go.dev.

func AsMatcher

func AsMatcher(x any) Matcher

func AssignableToTypeOf

func AssignableToTypeOf(x any) Matcher

AssignableToTypeOf is a Matcher that matches if the parameter to the mock function is assignable to the type of the parameter to this function.

Example usage:

var s fmt.Stringer = &bytes.Buffer{}
AssignableToTypeOf(s).Matches(time.Second) // returns true
AssignableToTypeOf(s).Matches(99) // returns false

var ctx = reflect.TypeOf((*context.Context)(nil)).Elem()
AssignableToTypeOf(ctx).Matches(context.Background()) // returns true

(This matcher is the same as gomock.AssignableToTypeOf. It's re-exported here for convenience with `import _ ` so that users don't need to remember which package particular matchers come from.)

func Contains

func Contains(elements ...any) Matcher

Matches slices or arrays containing all of the provided elements, in any order, potentially with additional elements as well.

If any of `elements` could match multiple of the elements of the value, then there must be a mapping such that a different element of the value fulfills each of `elements`. (As a corollary, matching values must have length at least greater than or equal to the length of `elements`.)

This is a weaker test than either ElemensAreUnordered() or ElemensAre(). That is, if either ElementsAre(els...).Matches(x) or ElementsAreUnordered(els...).Matches(x), then Contains(els...).Matches(x) is guaranteed to be true.

Examples:

// matches against 'bb' and 'a'
ExpectThat(t, []string{"a", "bb", "ccc", "dd"}, Contains("bb", "a"))
// matches against 'bb' and 'a'
ExpectThat(t, []string{"a", "bb", "ccc", "dd"}, Contains("bb", Len(1)))
// matches against 'bb' and 'dd'
ExpectThat(t, []string{"a", "bb", "ccc", "dd"}, Contains("bb", Len(2)))
// no match, because 'bb' is the only element that fulfills either matcher
ExpectThat(t, []string{"a", "bb", "ccc", "dd"}, Not(Contains("bb", StartsWith("b"))))

func ContainsRegex

func ContainsRegex(r string) Matcher

Matches strings and byte-arrays that contain a match for the given regexp. Similar behavior to gomock.Regex().

This is a strictly weaker matcher than Regex() - that is, if ContainsRegex(r).Matches(x), then Regex(r).Matches(x) is guaranteed to be true.

Examples:

ExpectThat(t, "hello", ContainsRegex("hello"))
ExpectThat(t, "hello", ContainsRegex("\\w+"))
ExpectThat(t, "hello, world", ContainsRegex("\\w+"))
ExpectThat(t, "hello, world", Not(ContainsRegex("\\d")))

func ElementsAre

func ElementsAre(elements ...any) Matcher

Tests that a slice or array contains exactly the provided elements, in order, with no others.

Each element can be either an exact value (tested by equality) or a matcher that must succeed for that element.

This is a stronger test than either ElementsAreUnordered() or Contains(). That is, ElementsAre(els...).Matches(x) implies both Contains(els...).Matches(x) and ElementsAreUnordered(els...).Matches(x).

Examples:

ExpectThat(t, []string{"a", "b", "c"}, ElementsAre("a", "b", "c"))
ExpectThat(t, []string{"a", "b", "c"}, ElementsAre("a', Len(1), Any()))
ExpectThat(t, []string{"a", "b", "c"}, Not(ElementsAre(Any(), "c")))
ExpectThat(t, []string{"a", "b", "c"}, Not(ElementsAre("b", "a", "c")))

func ElementsAreUnordered

func ElementsAreUnordered(elements ...any) Matcher

Tests that a slice or array contains exactly the provided elements, in any order, but with no others.

Specifically, there must be a possible 1:1 mapping between `elements` and the value's elements such that all matchers are satisfied. (As a corollary, only values with the same length as `elements` can possibly match.)

This is a strictly weaker test than ElementsAre(), but a stronger test than Contains(). That is:

  • ElementsAreUnordered(els...).Matches(x) implies Contains(els...).Matches(x)
  • ElementsAre(els...).Matches(x) implies ElementsAreUnordered(els...).Matches(x)

Examples:

ExpectThat(t, []string{"a", "b", "ccc"}, ElementsAreUnordered("b", "ccc", "a"))
ExpectThat(t, []string{"a", "b", "ccc"}, ElementsAreUnordered("b", Any(), "a"))
ExpectThat(t, []string{"a", "b", "ccc"}, Not(ElementsAreUnordered(Any(), Any())))
ExpectThat(t, []string{"a", "b", "ccc"}, Not(ElementsAreUnordered("a", "ccc", Len(Gt(1)))))

func Empty

func Empty() Matcher

Same behavior as Len(0), but with better error-message reporting.

Examples:

ExpectThat(t, []int{}, Empty())
ExpectThat(t, "", Empty())
ExpectThat(t, map[string]int{}, Empty())
ExpectThat(t, []int{1, 2, 3}, Not(Empty()))

func Eq

func Eq(x any) Matcher

Tests whether x is "equal" to the expected value.

Equality is defined as follows:

  • Primitive types (ints, strings, etc) are compared using ==.
  • All protos are compared using proto.Equal, including when nested within other structs, slices, or maps.
  • Non-proto structs are compared field-by-field. Unexported fields are compared only for types that are defined in the same package as the matcher is used.
  • Any type that has a custom Equal method will use that method for comparison.

This is the default matcher used by all other matchers to compare nested values when passed values directly instead of matchers. If you want to customize the comparison behavior, use Equiv().

Examples:

ExpectThat(t, 42, Eq(42))
ExpectThat(t, "hello", Eq("hello"))
ExpectThat(t, Name("hello"), Eq("hello"))  // same underlying type
ExpectThat(t, []int{1, 2, 3}, Eq([]int{1, 2, 3}))
ExpectThat(t, 42, Not(Eq(43)))
ExpectThat(t, 42, Not(43)) // same as above

You can also use ExpectEq() as a shorthand:

ExpectEq(t, 42, 42)

func Equiv added in v0.1.0

func Equiv(x any, options ...cmp.Option) Matcher

Like Eq, but allows customizing the comparison behavior using cmp.Options.

Note that this matcher does not automatically inject any Export or Ignore options, so using it with types that have unexported fields requires setting some options.

Examples:

type Person struct { Name string; Age int }
p1 := Person{"Alice", 30}
p2 := Person{"Alice", 31}
ExpectThat(t, p1, Equiv(p2, cmpopts.IgnoreFields(Person{}, "Age")))
ExpectThat(t, p1, Not(Equiv(p2)))

func ErrorIs

func ErrorIs(err error) Matcher

ErrorIs matches errors that wrap the expected error, using errors.Is().

Examples:

var ErrNotFound = errors.New("not found")
err := fmt.Errorf("failed: %w", ErrNotFound)
ExpectThat(t, err, ErrorIs(ErrNotFound))
ExpectThat(t, err, Not(ErrorIs(errors.New("other error"))))

func ErrorMessage

func ErrorMessage(innerMatcher any) Matcher

ErrorMessage matches errors whose error message fulfills the innerMatcher.

Examples:

err := errors.New("file not found")
ExpectThat(t, err, ErrorMessage("file not found"))
ExpectThat(t, err, ErrorMessage(HasSubstr("not found")))
ExpectThat(t, err, Not(ErrorMessage("success")))

func Ge added in v0.2.1

func Ge[T cmp.Ordered](threshold T) Matcher

Matches values that are greater than or equal to the threshold.

Works with any ordered types including:

  • All numeric types
  • strings (lexicographic comparison)

Examples:

ExpectThat(t, 5, Ge(5))
ExpectThat(t, 10.5, Ge(10.0))

func Gt added in v0.2.0

func Gt[T cmp.Ordered](threshold T) Matcher

Matches values that are greater than the threshold.

Works with any ordered types including:

  • All numeric types
  • strings (lexicographic comparison)

Examples:

ExpectThat(t, 5, Gt(3))
ExpectThat(t, 10.5, Gt(10.0))
ExpectThat(t, "banana", Gt("apple"))

func HasSubstr

func HasSubstr(s string) Matcher

Matches strings and byte-arrays containing the given substring.

Examples:

ExpectThat(t, "hello, world", HasSubstr("hello"))
ExpectThat(t, []byte{"a", "b", "c"}, HasSubstr("a"))

func Le added in v0.2.1

func Le[T cmp.Ordered](threshold T) Matcher

Matches values that are less than or equal to the threshold.

Works with any ordered types including:

  • All numeric types
  • strings (lexicographic comparison)

Examples:

ExpectThat(t, 5, Le(5))
ExpectThat(t, 10.0, Le(10.5))

func Len

func Len(innerMatcher any) Matcher

Matches values whose length fulfills `innerMatcher`. Length is defined by calling a `Len()` method for objects that have one, or using the `len()` builtin when available (i.e. for arrays, slices, maps, strings, and channels).

Examples:

ExpectThat(t, "asdf", Len(4))
ExpectThat(t, "asdf", Len(Gt(2)))
ExpectThat(t, map[string]int{"a": 1, "b": 2}, Len(Lt(10)))

type CustomLen struct {}
func (CustomLen) Len() int { return 2 }
c := CustomLen{}
ExpectThat(t, c, Len(2))

func Lt added in v0.2.0

func Lt[T cmp.Ordered](threshold T) Matcher

Matches values that are less than the threshold.

Works with any ordered types including:

  • All numeric types
  • strings (lexicographic comparison)

Examples:

ExpectThat(t, 3, Lt(5))
ExpectThat(t, 10.0, Lt(10.5))
ExpectThat(t, "apple", Lt("banana"))

func MapContains

func MapContains[K comparable, V any](mapValues map[K]V) Matcher

Tests that a map contains the elements in `mapValues`, and potentially others as well.

This is a weaker test than MapIs(). That is, if MapIs(m).Matches(x), then MapContains(m).Matches(x) is guaranteed to be true.

Examples:

m := map[string]int{"a": 1, "b": 2, "c": 3}
ExpectThat(t, m, MapContains(map[string]int{"a": 1, "b": 2}))
ExpectThat(t, m, MapContains(map[string]any{"a": 1, "c": Gt(2)}))
ExpectThat(t, m, Not(MapContains(map[string]int{"d": 4})))

func MapContainsKVs

func MapContainsKVs(pairs ...KeyValT) Matcher

Tests that a map contains the key-value pairs in `pairs`.

This is very similar to MapContains(), but allows using fuzzy matchers on the keys as well. If keys in `pairs` are Matchers, then this matcher tests that there is some mapping between `pairs` and the key-value pairs in the value such that each of `pairs` is satisfied by a different element of the map.

In other words, this is the same as `Contains()`, but for maps - if the map were converted into a list of key-value pairs.

Examples:

	ExpectThat(t, map[string]int{"a": 1, "bxy": 10}, MapIsKVs(
     	KeyVal(StartsWith("b"), Gt(5)),
 	))

func MapIs

func MapIs[K comparable, V any](mapValues map[K]V) Matcher

Tests that a map contains exactly the elements of `mapValues`, and no others.

The map passed to this function must have keys that are convertible to the key type of the value that will be tested. In most normal cases, the keys here should be exactly the same as those in the value to be tested.

The values of the map passed to this function could be exact values, or matchers, or a mix.

This is a stronger test than MapContains(). That is, if MapIs(m).Matches(x), then MapContains(m).Matches(x) is guaranteed to be true.

Examples:

ExpectThat(t, map[string]int{"a": 1, "b": 10}, MapIs(map[string]int{
	"a": 1,
	"b": 10,
})
ExpectThat(t, map[string]int{"a": 1, "b": 10}, MapIs(map[string]any{
	"a": 1,
	"b": Gt(5),
})
ExpectThat(t, map[string]int{"a": 1, "b": 10}, Not(MapIs(map[string]any{
	"a": 1,
}))
ExpectThat(t, map[string]int{"a": 1, "b": 10}, Not(MapIs(map[string]any{
	"a": 1,
	"b": Gt(5),
	"c": 3,
}))

func MapIsKVs

func MapIsKVs(pairs ...KeyValT) Matcher

Tests that a map contains the key-value pairs in `pairs`, and no others.

This is very similar to MapIs(), but allows using fuzzy matchers on the keys as well. If keys in `pairs` are Matchers, then this matcher tests that there is some 1:1 mapping between `pairs` and the key-value pairs in the value such that each of `pairs` is satisfied by a different element of the map.

In other words, this is the same as `ElementsAreUnordered()`, but for maps - if the map were converted into a list of key-value pairs.

Examples:

	ExpectThat(t, map[string]int{"a": 1, "bxy": 10}, MapIsKVs(
		KeyVal("a", Lt(2)),
     	KeyVal(StartsWith("b"), Gt(5)),
 	))

func Nil

func Nil() Matcher

Nil returns a matcher that matches if the received value is nil.

Example usage:

var x *bytes.Buffer
Nil().Matches(x) // returns true
x = &bytes.Buffer{}
Nil().Matches(x) // returns false

(This matcher is the same as gomock.Nil. It's re-exported here for convenience with `import _ ` so that users don't need to remember which package particular matchers come from.)

func Not

func Not(x any) Matcher

Negates the inner condition. If `x` is a matcher, then Not(x) will match conditions where x doesn't match. If `x` is a value, then Not(x) will match conditions where the value is equal.

Examples:

ExpectThat(t, 4, Not(Eq(5)))
ExpectThat(t, 4, Not(5))
ExpectThat(t, 4, Not(Gt(5)))

This is exactly the same as gomock.Not, except that it uses our implementation of Eq() when `x` is a value.

func Regex

func Regex(r string) Matcher

Matches strings and byte-arrays that match exactly the given regexp.

Note that this is not the same behavior as gomock.Regex. This implementation is stricter by default; gomock.Regex has the behavior of our ContainsRegex().

Examples:

ExpectThat(t, "hello", Regex("hello"))
ExpectThat(t, "hello", Regex("\\w+"))
ExpectThat(t, "hello, world", Not(Regex("\\w+")))

func StartsWith added in v0.1.0

func StartsWith(s string) Matcher

Matches strings and byte-arrays that start with the given prefix.

Examples:

ExpectThat(t, "hello", StartsWith("h"))
ExpectThat(t, "hello", StartsWith("hello"))

type MismatchExplainer

type MismatchExplainer interface {
	// Returns an explanation if one is useful, or ("", false) if no further
	// explanation should be provided
	ExplainFailure(val any) (string, bool)
}

An optional interface that matchers can implement to provide more information about failures. Useful for compound matchers - e.g., those that match many elements of a container - to aid in debugging.

Jump to

Keyboard shortcuts

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