Import: github.com/onsi/gomega/gstruct
The gstruct package provides matchers for structural matching of complex Go types including structs, slices, arrays, and maps. These matchers allow you to specify precise validation rules for nested data structures with flexible options for handling extras, missing values, and duplicates.
func MatchAllFields(fields Fields) types.GomegaMatcherMatches all struct fields strictly. Every field in the struct must have a corresponding matcher in the Fields map, and every matcher must match. No extra fields are allowed.
Example:
import (
. "github.com/onsi/gomega"
. "github.com/onsi/gomega/gstruct"
)
actual := struct{
A int
B []bool
C string
}{
A: 5,
B: []bool{true, false},
C: "foo",
}
Expect(actual).To(MatchAllFields(Fields{
"A": Equal(5),
"B": ConsistOf(true, false),
"C": Equal("foo"),
}))func MatchFields(options Options, fields Fields) types.GomegaMatcherMatches struct fields with flexible options. Can ignore extra fields, missing fields, or unexported extra fields based on the provided Options.
Example:
actual := struct{
A int
B []bool
C string
}{
A: 5,
B: []bool{true, false},
C: "foo",
}
// Ignore extra fields (C is not validated)
Expect(actual).To(MatchFields(IgnoreExtras, Fields{
"A": Equal(5),
"B": ConsistOf(true, false),
}))
// Ignore missing fields (D doesn't exist in actual)
Expect(actual).To(MatchFields(IgnoreMissing, Fields{
"A": Equal(5),
"B": ConsistOf(true, false),
"C": Equal("foo"),
"D": Equal("extra"),
}))
// Combine options with bitwise OR
Expect(actual).To(MatchFields(IgnoreExtras|IgnoreMissing, Fields{
"A": Equal(5),
}))type Fields map[string]types.GomegaMatcherMaps struct field names to matchers. Each key is the name of a struct field, and each value is a matcher that validates that field.
func MatchAllElements(identifier Identifier, elements Elements) types.GomegaMatcherMatches all slice or array elements strictly. Uses an Identifier function to map each element to a string key, then validates each element against the matcher associated with that key. Every element must match, and every matcher must be matched.
Example:
idFn := func(element any) string {
return fmt.Sprintf("%v", element)
}
Expect([]string{"a", "b"}).To(MatchAllElements(idFn, Elements{
"a": Equal("a"),
"b": Equal("b"),
}))func MatchAllElementsWithIndex(identifier IdentifierWithIndex, elements Elements) types.GomegaMatcherLike MatchAllElements but the identifier function receives both the index and the element.
Example:
idFn := func(index int, element any) string {
return strconv.Itoa(index)
}
Expect([]string{"a", "b"}).To(MatchAllElementsWithIndex(idFn, Elements{
"0": Equal("a"),
"1": Equal("b"),
}))func MatchElements(identifier Identifier, options Options, elements Elements) types.GomegaMatcherMatches slice or array elements with flexible options. Can ignore extra elements and/or missing elements.
Example:
idFn := func(element any) string {
return fmt.Sprintf("%v", element)
}
// Ignore extra elements
Expect([]string{"a", "b", "c"}).To(MatchElements(idFn, IgnoreExtras, Elements{
"a": Equal("a"),
"b": Equal("b"),
}))
// Ignore missing elements
Expect([]string{"a", "c"}).To(MatchElements(idFn, IgnoreMissing, Elements{
"a": Equal("a"),
"b": Equal("b"),
"c": Equal("c"),
"d": Equal("d"),
}))
// Allow duplicate identifiers
nonUniqueID := func(element any) string {
return element.(string)[0:1] // Use first character as ID
}
Expect([]string{"a123", "a456", "b789"}).To(
MatchElements(nonUniqueID, AllowDuplicates, Elements{
"a": ContainSubstring("a"),
"b": ContainSubstring("b"),
}))func MatchElementsWithIndex(identifier IdentifierWithIndex, options Options, elements Elements) types.GomegaMatcherLike MatchElements but the identifier function receives both the index and the element.
Example:
idFn := func(index int, element any) string {
return strconv.Itoa(index)
}
Expect([]string{"a", "b", "c"}).To(MatchElementsWithIndex(idFn, IgnoreExtras, Elements{
"0": Equal("a"),
"1": Equal("b"),
}))type Elements map[string]types.GomegaMatcherMaps element identifier keys to matchers. The keys are strings returned by the Identifier function, and values are matchers that validate elements with those keys.
type Identifier func(element any) stringFunction that maps a slice/array element to a string key used for matching.
type IdentifierWithIndex func(index int, element any) stringFunction that maps a slice/array element and its index to a string key used for matching.
func IndexIdentity(index int, _ any) stringHelper identifier function that uses the array/slice index as the element key. Converts the index to a string.
Example:
Expect([]string{"a", "b", "c"}).To(
MatchAllElementsWithIndex(IndexIdentity, Elements{
"0": Equal("a"),
"1": Equal("b"),
"2": Equal("c"),
}))func MatchAllKeys(keys Keys) types.GomegaMatcherMatches all map keys strictly. Every key in the map must have a corresponding matcher in the Keys map, and every matcher must match. No extra keys are allowed.
Example:
actualMap := map[string]int{
"a": 1,
"b": 2,
}
Expect(actualMap).To(MatchAllKeys(Keys{
"a": Equal(1),
"b": Equal(2),
}))func MatchKeys(options Options, keys Keys) types.GomegaMatcherMatches map keys with flexible options. Can ignore extra keys and/or missing keys.
Example:
actualMap := map[string]int{
"a": 1,
"b": 2,
"c": 3,
}
// Ignore extra keys
Expect(actualMap).To(MatchKeys(IgnoreExtras, Keys{
"a": Equal(1),
"b": Equal(2),
}))
// Ignore missing keys
Expect(actualMap).To(MatchKeys(IgnoreMissing, Keys{
"a": Equal(1),
"b": Equal(2),
"c": Equal(3),
"d": Equal(4),
}))type Keys map[any]types.GomegaMatcherMaps map keys to matchers for their values. Keys can be of any type (matching the map's key type), and values are matchers that validate the corresponding map values.
func PointTo(matcher types.GomegaMatcher) types.GomegaMatcherDereferences a pointer and applies the given matcher to the pointed-to value. Fails if the pointer is nil.
Example:
actual := 5
Expect(&actual).To(PointTo(Equal(5)))
str := "hello"
Expect(&str).To(PointTo(ContainSubstring("ell")))
// Fails with nil pointer
var ptr *int
Expect(ptr).NotTo(PointTo(Equal(5)))func Ignore() types.GomegaMatcherMatcher that always succeeds, ignoring the actual value. Used to match fields/elements/keys without validating their values.
Example:
// Always succeeds
Expect(nil).To(Ignore())
Expect(true).To(Ignore())
Expect(42).To(Ignore())
// Useful for ignoring specific struct fields
Expect(actual).To(MatchAllFields(Fields{
"A": Equal(5),
"B": Ignore(), // Don't care about B's value
"C": Ignore(), // Don't care about C's value
}))func Reject() types.GomegaMatcherMatcher that always fails, ignoring the actual value. Can be used with IgnoreMissing to catch problematic elements, or to verify tests are running.
Example:
// Always fails
Expect(nil).NotTo(Reject())
Expect(true).NotTo(Reject())
Expect(42).NotTo(Reject())
// Can be used to ensure a field should never be present
Expect(actual).To(MatchFields(IgnoreMissing, Fields{
"A": Equal(5),
"BadField": Reject(), // Fail if this field exists
}))type Options intBitmask options for controlling matcher behavior. Multiple options can be combined using bitwise OR (|).
const IgnoreExtras OptionsTells the matcher to ignore extra fields/elements/keys not specified in the matcher, rather than triggering a failure.
const IgnoreMissing OptionsTells the matcher to ignore missing fields/elements/keys from the matcher, rather than triggering a failure.
const AllowDuplicates OptionsTells the matcher to permit multiple members of a slice to produce the same ID when processed by the identifier function. All members that map to a given key must still match successfully with the matcher provided for that key.
const IgnoreUnexportedExtras OptionsTells the matcher to ignore extra unexported struct fields, rather than triggering a failure. Since unexported fields cannot be checked for values, this option is only useful when you want to check every exported field but don't care about extra unexported fields.
Example:
// Single option
MatchFields(IgnoreExtras, Fields{...})
// Multiple options combined
MatchFields(IgnoreExtras|IgnoreMissing, Fields{...})
MatchElements(idFn, IgnoreExtras|AllowDuplicates, Elements{...})The errors sub-package (github.com/onsi/gomega/gstruct/errors) provides utilities for handling and nesting errors in structural matchers.
func Nest(path string, err error) errorWraps an error with a field path to provide better context in failure messages. This is used internally by structural matchers to indicate which field or element caused a match failure.
Parameters:
path - The field path or element identifier (e.g., "User.Name", "[2]")err - The error to wrapBehavior:
err is already a NestedError, prepends the path to iterr is an AggregateError, recursively nests each error in the aggregateNestedError with the given pathUsage Example:
import (
"errors"
"github.com/onsi/gomega/gstruct/errors"
)
// Wrap an error with field path context
err := errors.New("value mismatch")
nestedErr := gstructErrors.Nest("User.Email", err)
// Error message becomes: "User.Email: value mismatch"
// Nest multiple levels
err1 := errors.New("invalid format")
err2 := gstructErrors.Nest("Email", err1)
err3 := gstructErrors.Nest("User", err2)
// Error message becomes: "User.Email: invalid format"This function is typically used when implementing custom structural matchers to provide clear error messages that indicate exactly where in a nested structure a match failed.
package mytest
import (
"fmt"
"testing"
. "github.com/onsi/gomega"
. "github.com/onsi/gomega/gstruct"
)
type Person struct {
Name string
Age int
Email string
private string
}
func TestStructMatching(t *testing.T) {
RegisterTestingT(t)
person := Person{
Name: "Alice",
Age: 30,
Email: "alice@example.com",
private: "secret",
}
// Match all exported fields, ignore unexported extras
Expect(person).To(MatchFields(IgnoreUnexportedExtras, Fields{
"Name": Equal("Alice"),
"Age": BeNumerically(">", 18),
"Email": ContainSubstring("@example.com"),
}))
// Match with some fields ignored
Expect(person).To(MatchAllFields(Fields{
"Name": Equal("Alice"),
"Age": BeNumerically(">=", 30),
"Email": Ignore(),
"private": Ignore(),
}))
}
func TestSliceMatching(t *testing.T) {
RegisterTestingT(t)
people := []Person{
{Name: "Alice", Age: 30, Email: "alice@example.com"},
{Name: "Bob", Age: 25, Email: "bob@example.com"},
}
// Use name as identifier
byName := func(element any) string {
return element.(Person).Name
}
Expect(people).To(MatchAllElements(byName, Elements{
"Alice": MatchFields(IgnoreExtras, Fields{
"Age": Equal(30),
}),
"Bob": MatchFields(IgnoreExtras, Fields{
"Age": Equal(25),
}),
}))
// Use index as identifier
Expect(people).To(MatchAllElementsWithIndex(IndexIdentity, Elements{
"0": MatchFields(IgnoreExtras, Fields{"Name": Equal("Alice")}),
"1": MatchFields(IgnoreExtras, Fields{"Name": Equal("Bob")}),
}))
}
func TestMapMatching(t *testing.T) {
RegisterTestingT(t)
scores := map[string]int{
"Alice": 95,
"Bob": 87,
"Carol": 92,
}
// Match all keys
Expect(scores).To(MatchAllKeys(Keys{
"Alice": BeNumerically(">=", 90),
"Bob": BeNumerically(">", 80),
"Carol": BeNumerically(">=", 90),
}))
// Ignore extra keys
Expect(scores).To(MatchKeys(IgnoreExtras, Keys{
"Alice": BeNumerically(">=", 90),
"Bob": BeNumerically(">", 80),
}))
}
func TestPointerMatching(t *testing.T) {
RegisterTestingT(t)
value := 42
ptr := &value
Expect(ptr).To(PointTo(Equal(42)))
Expect(ptr).To(PointTo(BeNumerically(">", 40)))
// Nested pointer matching
personPtr := &Person{Name: "Alice", Age: 30}
Expect(personPtr).To(PointTo(MatchFields(IgnoreExtras, Fields{
"Name": Equal("Alice"),
"Age": Equal(30),
})))
}