Skip to content

Commit

Permalink
Merge pull request #3 from rgalanakis/matchptrfield
Browse files Browse the repository at this point in the history
Add MatchPtrField
  • Loading branch information
rgalanakis authored Sep 23, 2021
2 parents e786a63 + 7ab3920 commit e360084
Show file tree
Hide file tree
Showing 3 changed files with 121 additions and 1 deletion.
7 changes: 6 additions & 1 deletion golangal.go
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ func MatchLen(m interface{}) gomega.OmegaMatcher {
return &matchers.MatchLenMatcher{Matcher: internal.CoerceToMatcher(m)}
}

// MatchField matches the the value of the field named name on the actual struct.
// MatchField matches the value of the field named name on the actual struct.
// If m is a gomega matcher, it is matched against the field value.
// Otherwise, test against field equality.
//
Expand All @@ -131,6 +131,11 @@ func MatchField(name string, m interface{}) gomega.OmegaMatcher {
return &matchers.MatchFieldMatcher{Name: name, Matcher: internal.CoerceToMatcher(m)}
}

// MatchPtrField is same as MatchField, but the actual object should be a pointer, not a struct.
func MatchPtrField(name string, m interface{}) gomega.OmegaMatcher {
return &matchers.MatchPtrFieldMatcher{Name: name, Matcher: internal.CoerceToMatcher(m)}
}

// NotError is like gomega's Succeed matcher, except it handles functions which
// return multiple values. The docs say this:
//
Expand Down
46 changes: 46 additions & 0 deletions matchers/match_ptr_field.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package matchers

import (
"reflect"

"fmt"

"github.com/onsi/gomega/format"
"github.com/onsi/gomega/types"
)

type MatchPtrFieldMatcher struct {
Name string
Matcher types.GomegaMatcher
}

func (m *MatchPtrFieldMatcher) Match(actual interface{}) (success bool, err error) {
actualPtrVal := reflect.ValueOf(actual)
if actualPtrVal.Kind() != reflect.Ptr {
err := fmt.Errorf("MatchPtrField matcher requires an actual of kind ptr, not %s",
actualPtrVal.Kind().String())
return false, err
}
actualVal := actualPtrVal.Elem()
fieldVal := actualVal.FieldByName(m.Name)
if !fieldVal.IsValid() {
err := fmt.Errorf("field '%s' does not exist on type %s", m.Name, actualVal.Type().Name())
return false, err
}

return m.Matcher.Match(fieldVal.Interface())
}

func (m *MatchPtrFieldMatcher) FailureMessage(actual interface{}) (message string) {
return fmt.Sprintf("Field %s of\n%s\ndid not match. %s",
m.Name, format.Object(actual, 1), m.Matcher.FailureMessage(m.fieldValue(actual)))
}

func (m *MatchPtrFieldMatcher) NegatedFailureMessage(actual interface{}) (message string) {
return fmt.Sprintf("Field %s of\n%s\nmatched. %s",
m.Name, format.Object(actual, 1), m.Matcher.FailureMessage(m.fieldValue(actual)))
}

func (m *MatchPtrFieldMatcher) fieldValue(actual interface{}) interface{} {
return reflect.ValueOf(actual).Elem().FieldByName(m.Name).Interface()
}
69 changes: 69 additions & 0 deletions matchers/match_ptr_field_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
package matchers_test

import (
"fmt"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
. "github.com/rgalanakis/golangal"
)

var _ = Describe("MatchPtrField matcher", func() {
type T struct {
X int
}

It("matches if the value of the field equals the given value", func() {
Expect(&T{4}).To(MatchPtrField("X", 4))
Expect(&T{4}).ToNot(MatchPtrField("X", 5))
})

It("matches if the value of the field passes the given matcher", func() {
Expect(&T{4}).To(MatchPtrField("X", Equal(4)))
Expect(&T{4}).ToNot(MatchPtrField("X", Equal(5)))
})

It("fails if the value of the field does not match the given value", func() {
t := &T{5}
matcher := MatchPtrField("X", 3)
success, err := matcher.Match(t)
Expect(success).To(BeFalse())
Expect(err).ToNot(HaveOccurred())
msg := matcher.FailureMessage(t)
Expect(msg).To(HaveSuffix(fmt.Sprintf(`Field X of
<*matchers_test.T | %p>: {X: 5}
did not match. Expected
<int>: 5
to equal
<int>: 3`, t)))
})

It("fails if the value of the field does not match the given matcher", func() {
t := &T{5}
matcher := MatchPtrField("X", BeNil())
success, err := matcher.Match(t)
Expect(success).To(BeFalse())
Expect(err).ToNot(HaveOccurred())
msg := matcher.FailureMessage(t)
Expect(msg).To(HaveSuffix(fmt.Sprintf(`Field X of
<*matchers_test.T | %p>: {X: 5}
did not match. Expected
<int>: 5
to be nil`, t)))
})

It("errors if the actual type is not a pointer", func() {
success, err := MatchPtrField("X", Equal(1)).Match(123)
Expect(success).To(BeFalse())
Expect(err).To(MatchError("MatchPtrField matcher requires an actual of kind ptr, not int"))

success, err = MatchPtrField("X", Equal(1)).Match(T{})
Expect(success).To(BeFalse())
Expect(err).To(MatchError("MatchPtrField matcher requires an actual of kind ptr, not struct"))
})

It("errors if the field does not exist", func() {
success, err := MatchPtrField("Y", Equal(1)).Match(&T{})
Expect(success).To(BeFalse())
Expect(err).To(MatchError("field 'Y' does not exist on type T"))
})
})

0 comments on commit e360084

Please sign in to comment.