Skip to content

Commit c5fc9d6

Browse files
mchlpanthony-chang
andauthored
Compare public elements of struct (#1309)
* Implement checking only exported fields Co-authored-by: Anthony Chang <[email protected]> * Update comment * Run go generate * Make compatiable with Go 1.16.5 * Fix go generate files * Fix white space changes * Fix whitespace changes * Fix whitespace changes in gogenerate files --------- Co-authored-by: Anthony Chang <[email protected]>
1 parent f36bfe3 commit c5fc9d6

6 files changed

+253
-0
lines changed

assert/assertion_format.go

+17
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,23 @@ func EqualErrorf(t TestingT, theError error, errString string, msg string, args
9090
return EqualError(t, theError, errString, append([]interface{}{msg}, args...)...)
9191
}
9292

93+
// EqualExportedValuesf asserts that the types of two objects are equal and their public
94+
// fields are also equal. This is useful for comparing structs that have private fields
95+
// that could potentially differ.
96+
//
97+
// type S struct {
98+
// Exported int
99+
// notExported int
100+
// }
101+
// assert.EqualExportedValuesf(t, S{1, 2}, S{1, 3}, "error message %s", "formatted") => true
102+
// assert.EqualExportedValuesf(t, S{1, 2}, S{2, 3}, "error message %s", "formatted") => false
103+
func EqualExportedValuesf(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) bool {
104+
if h, ok := t.(tHelper); ok {
105+
h.Helper()
106+
}
107+
return EqualExportedValues(t, expected, actual, append([]interface{}{msg}, args...)...)
108+
}
109+
93110
// EqualValuesf asserts that two objects are equal or convertable to the same types
94111
// and equal.
95112
//

assert/assertion_forward.go

+34
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,40 @@ func (a *Assertions) EqualErrorf(theError error, errString string, msg string, a
155155
return EqualErrorf(a.t, theError, errString, msg, args...)
156156
}
157157

158+
// EqualExportedValues asserts that the types of two objects are equal and their public
159+
// fields are also equal. This is useful for comparing structs that have private fields
160+
// that could potentially differ.
161+
//
162+
// type S struct {
163+
// Exported int
164+
// notExported int
165+
// }
166+
// a.EqualExportedValues(S{1, 2}, S{1, 3}) => true
167+
// a.EqualExportedValues(S{1, 2}, S{2, 3}) => false
168+
func (a *Assertions) EqualExportedValues(expected interface{}, actual interface{}, msgAndArgs ...interface{}) bool {
169+
if h, ok := a.t.(tHelper); ok {
170+
h.Helper()
171+
}
172+
return EqualExportedValues(a.t, expected, actual, msgAndArgs...)
173+
}
174+
175+
// EqualExportedValuesf asserts that the types of two objects are equal and their public
176+
// fields are also equal. This is useful for comparing structs that have private fields
177+
// that could potentially differ.
178+
//
179+
// type S struct {
180+
// Exported int
181+
// notExported int
182+
// }
183+
// a.EqualExportedValuesf(S{1, 2}, S{1, 3}, "error message %s", "formatted") => true
184+
// a.EqualExportedValuesf(S{1, 2}, S{2, 3}, "error message %s", "formatted") => false
185+
func (a *Assertions) EqualExportedValuesf(expected interface{}, actual interface{}, msg string, args ...interface{}) bool {
186+
if h, ok := a.t.(tHelper); ok {
187+
h.Helper()
188+
}
189+
return EqualExportedValuesf(a.t, expected, actual, msg, args...)
190+
}
191+
158192
// EqualValues asserts that two objects are equal or convertable to the same types
159193
// and equal.
160194
//

assert/assertions.go

+83
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,48 @@ func ObjectsAreEqual(expected, actual interface{}) bool {
7575
return bytes.Equal(exp, act)
7676
}
7777

78+
// ObjectsExportedFieldsAreEqual determines if the exported (public) fields of two structs are considered equal.
79+
// If the two objects are not of the same type, or if either of them are not a struct, they are not considered equal.
80+
//
81+
// This function does no assertion of any kind.
82+
func ObjectsExportedFieldsAreEqual(expected, actual interface{}) bool {
83+
if expected == nil || actual == nil {
84+
return expected == actual
85+
}
86+
87+
expectedType := reflect.TypeOf(expected)
88+
actualType := reflect.TypeOf(actual)
89+
90+
if expectedType != actualType {
91+
return false
92+
}
93+
94+
if expectedType.Kind() != reflect.Struct || actualType.Kind() != reflect.Struct {
95+
return false
96+
}
97+
98+
expectedValue := reflect.ValueOf(expected)
99+
actualValue := reflect.ValueOf(actual)
100+
101+
for i := 0; i < expectedType.NumField(); i++ {
102+
field := expectedType.Field(i)
103+
isExported := field.PkgPath == "" // should use field.IsExported() but it's not available in Go 1.16.5
104+
if isExported {
105+
var equal bool
106+
if field.Type.Kind() == reflect.Struct {
107+
equal = ObjectsExportedFieldsAreEqual(expectedValue.Field(i).Interface(), actualValue.Field(i).Interface())
108+
} else {
109+
equal = ObjectsAreEqualValues(expectedValue.Field(i).Interface(), actualValue.Field(i).Interface())
110+
}
111+
112+
if !equal {
113+
return false
114+
}
115+
}
116+
}
117+
return true
118+
}
119+
78120
// ObjectsAreEqualValues gets whether two objects are equal, or if their
79121
// values are equal.
80122
func ObjectsAreEqualValues(expected, actual interface{}) bool {
@@ -473,6 +515,47 @@ func EqualValues(t TestingT, expected, actual interface{}, msgAndArgs ...interfa
473515

474516
}
475517

518+
// EqualExportedValues asserts that the types of two objects are equal and their public
519+
// fields are also equal. This is useful for comparing structs that have private fields
520+
// that could potentially differ.
521+
//
522+
// type S struct {
523+
// Exported int
524+
// notExported int
525+
// }
526+
// assert.EqualExportedValues(t, S{1, 2}, S{1, 3}) => true
527+
// assert.EqualExportedValues(t, S{1, 2}, S{2, 3}) => false
528+
func EqualExportedValues(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}) bool {
529+
if h, ok := t.(tHelper); ok {
530+
h.Helper()
531+
}
532+
533+
aType := reflect.TypeOf(expected)
534+
bType := reflect.TypeOf(actual)
535+
536+
if aType != bType {
537+
return Fail(t, fmt.Sprintf("Types expected to match exactly\n\t%v != %v", aType, bType), msgAndArgs...)
538+
}
539+
540+
if aType.Kind() != reflect.Struct {
541+
return Fail(t, fmt.Sprintf("Types expected to both be struct \n\t%v != %v", aType.Kind(), reflect.Struct), msgAndArgs...)
542+
}
543+
544+
if bType.Kind() != reflect.Struct {
545+
return Fail(t, fmt.Sprintf("Types expected to both be struct \n\t%v != %v", bType.Kind(), reflect.Struct), msgAndArgs...)
546+
}
547+
548+
if !ObjectsExportedFieldsAreEqual(expected, actual) {
549+
diff := diff(expected, actual)
550+
expected, actual = formatUnequalValues(expected, actual)
551+
return Fail(t, fmt.Sprintf("Not equal (comparing only exported fields): \n"+
552+
"expected: %s\n"+
553+
"actual : %s%s", expected, actual, diff), msgAndArgs...)
554+
}
555+
556+
return true
557+
}
558+
476559
// Exactly asserts that two objects are equal in value and type.
477560
//
478561
// assert.Exactly(t, int32(123), int64(123))

assert/assertions_test.go

+45
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,51 @@ func TestObjectsAreEqual(t *testing.T) {
149149

150150
}
151151

152+
func TestObjectsExportedFieldsAreEqual(t *testing.T) {
153+
type Nested struct {
154+
Exported interface{}
155+
notExported interface{}
156+
}
157+
158+
type S struct {
159+
Exported1 interface{}
160+
Exported2 Nested
161+
notExported1 interface{}
162+
notExported2 Nested
163+
}
164+
165+
type S2 struct {
166+
foo interface{}
167+
}
168+
169+
cases := []struct {
170+
expected interface{}
171+
actual interface{}
172+
result bool
173+
}{
174+
{S{1, Nested{2, 3}, 4, Nested{5, 6}}, S{1, Nested{2, 3}, 4, Nested{5, 6}}, true},
175+
{S{1, Nested{2, 3}, 4, Nested{5, 6}}, S{1, Nested{2, 3}, "a", Nested{5, 6}}, true},
176+
{S{1, Nested{2, 3}, 4, Nested{5, 6}}, S{1, Nested{2, 3}, 4, Nested{5, "a"}}, true},
177+
{S{1, Nested{2, 3}, 4, Nested{5, 6}}, S{1, Nested{2, 3}, 4, Nested{"a", "a"}}, true},
178+
{S{1, Nested{2, 3}, 4, Nested{5, 6}}, S{1, Nested{2, "a"}, 4, Nested{5, 6}}, true},
179+
{S{1, Nested{2, 3}, 4, Nested{5, 6}}, S{"a", Nested{2, 3}, 4, Nested{5, 6}}, false},
180+
{S{1, Nested{2, 3}, 4, Nested{5, 6}}, S{1, Nested{"a", 3}, 4, Nested{5, 6}}, false},
181+
{S{1, Nested{2, 3}, 4, Nested{5, 6}}, S2{1}, false},
182+
{1, S{1, Nested{2, 3}, 4, Nested{5, 6}}, false},
183+
}
184+
185+
for _, c := range cases {
186+
t.Run(fmt.Sprintf("ObjectsExportedFieldsAreEqual(%#v, %#v)", c.expected, c.actual), func(t *testing.T) {
187+
res := ObjectsExportedFieldsAreEqual(c.expected, c.actual)
188+
189+
if res != c.result {
190+
t.Errorf("ObjectsExportedFieldsAreEqual(%#v, %#v) should return %#v", c.expected, c.actual, c.result)
191+
}
192+
193+
})
194+
}
195+
}
196+
152197
func TestImplements(t *testing.T) {
153198

154199
mockT := new(testing.T)

require/require.go

+40
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,46 @@ func EqualErrorf(t TestingT, theError error, errString string, msg string, args
195195
t.FailNow()
196196
}
197197

198+
// EqualExportedValues asserts that the types of two objects are equal and their public
199+
// fields are also equal. This is useful for comparing structs that have private fields
200+
// that could potentially differ.
201+
//
202+
// type S struct {
203+
// Exported int
204+
// notExported int
205+
// }
206+
// assert.EqualExportedValues(t, S{1, 2}, S{1, 3}) => true
207+
// assert.EqualExportedValues(t, S{1, 2}, S{2, 3}) => false
208+
func EqualExportedValues(t TestingT, expected interface{}, actual interface{}, msgAndArgs ...interface{}) {
209+
if h, ok := t.(tHelper); ok {
210+
h.Helper()
211+
}
212+
if assert.EqualExportedValues(t, expected, actual, msgAndArgs...) {
213+
return
214+
}
215+
t.FailNow()
216+
}
217+
218+
// EqualExportedValuesf asserts that the types of two objects are equal and their public
219+
// fields are also equal. This is useful for comparing structs that have private fields
220+
// that could potentially differ.
221+
//
222+
// type S struct {
223+
// Exported int
224+
// notExported int
225+
// }
226+
// assert.EqualExportedValuesf(t, S{1, 2}, S{1, 3}, "error message %s", "formatted") => true
227+
// assert.EqualExportedValuesf(t, S{1, 2}, S{2, 3}, "error message %s", "formatted") => false
228+
func EqualExportedValuesf(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) {
229+
if h, ok := t.(tHelper); ok {
230+
h.Helper()
231+
}
232+
if assert.EqualExportedValuesf(t, expected, actual, msg, args...) {
233+
return
234+
}
235+
t.FailNow()
236+
}
237+
198238
// EqualValues asserts that two objects are equal or convertable to the same types
199239
// and equal.
200240
//

require/require_forward.go

+34
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,40 @@ func (a *Assertions) EqualErrorf(theError error, errString string, msg string, a
156156
EqualErrorf(a.t, theError, errString, msg, args...)
157157
}
158158

159+
// EqualExportedValues asserts that the types of two objects are equal and their public
160+
// fields are also equal. This is useful for comparing structs that have private fields
161+
// that could potentially differ.
162+
//
163+
// type S struct {
164+
// Exported int
165+
// notExported int
166+
// }
167+
// a.EqualExportedValues(S{1, 2}, S{1, 3}) => true
168+
// a.EqualExportedValues(S{1, 2}, S{2, 3}) => false
169+
func (a *Assertions) EqualExportedValues(expected interface{}, actual interface{}, msgAndArgs ...interface{}) {
170+
if h, ok := a.t.(tHelper); ok {
171+
h.Helper()
172+
}
173+
EqualExportedValues(a.t, expected, actual, msgAndArgs...)
174+
}
175+
176+
// EqualExportedValuesf asserts that the types of two objects are equal and their public
177+
// fields are also equal. This is useful for comparing structs that have private fields
178+
// that could potentially differ.
179+
//
180+
// type S struct {
181+
// Exported int
182+
// notExported int
183+
// }
184+
// a.EqualExportedValuesf(S{1, 2}, S{1, 3}, "error message %s", "formatted") => true
185+
// a.EqualExportedValuesf(S{1, 2}, S{2, 3}, "error message %s", "formatted") => false
186+
func (a *Assertions) EqualExportedValuesf(expected interface{}, actual interface{}, msg string, args ...interface{}) {
187+
if h, ok := a.t.(tHelper); ok {
188+
h.Helper()
189+
}
190+
EqualExportedValuesf(a.t, expected, actual, msg, args...)
191+
}
192+
159193
// EqualValues asserts that two objects are equal or convertable to the same types
160194
// and equal.
161195
//

0 commit comments

Comments
 (0)