@@ -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.
80122func 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))
0 commit comments