feat(assert): use String() for Stringer types in diff output

Values whose type, or slice or array element type, implements
fmt.Stringer are now rendered through their String method in the diff
section of a failed assertion. Previously a named string such as
peer.ID was dumped as raw escaped bytes, which is unreadable.

This affects diff presentation only. Equality is still decided by
ObjectsAreEqual and is unaffected. The default spew config keeps
DisableMethods for every other type, matching the existing time.Time
handling in diff.

Refs #1215
This commit is contained in:
dualfroz committed 2026-09-02 09:43:14 +02:00
1 parent 08fef859fb
commit 9337ead2bc
2 files changed
+50 -2

No files matched your search

+26 -2
View File
@@ -1955,8 +1955,13 @@ func diff(expected interface{}, actual interface{}) string {
e = spewConfigStringerEnabled.Sdump(expected)
a = spewConfigStringerEnabled.Sdump(actual)
default:
e = spewConfig.Sdump(expected)
a = spewConfig.Sdump(actual)
if hasStringer(et) {
e = spewConfigStringerEnabled.Sdump(expected)
a = spewConfigStringerEnabled.Sdump(actual)
} else {
e = spewConfig.Sdump(expected)
a = spewConfig.Sdump(actual)
}
}
diff, _ := difflib.GetUnifiedDiffString(difflib.UnifiedDiff{
@@ -1972,6 +1977,25 @@ func diff(expected interface{}, actual interface{}) string {
return "\n\nDiff:\n" + diff
}
var stringerType = reflect.TypeOf((*fmt.Stringer)(nil)).Elem()
// hasStringer reports whether t, or the element type of a slice or array,
// implements fmt.Stringer. It lets diff render such values through the
// Stringer-enabled spew config, so a named string like peer.ID is shown by its
// String output instead of its raw bytes. This influences diff presentation
// only: equality is decided by ObjectsAreEqual and is unaffected, and the
// default spew config keeps DisableMethods for every other type.
func hasStringer(t reflect.Type) bool {
if t.Implements(stringerType) {
return true
}
switch t.Kind() {
case reflect.Slice, reflect.Array:
return t.Elem().Implements(stringerType)
}
return false
}
func isFunction(arg interface{}) bool {
if arg == nil {
return false
+24
View File
@@ -2902,6 +2902,12 @@ func (d *diffTestingStruct) String() string {
return d.A
}
type diffTestingStringer string
func (id diffTestingStringer) String() string {
return "stringer:" + string(id)
}
func TestDiff(t *testing.T) {
t.Parallel()
@@ -3036,6 +3042,24 @@ Diff:
time.Date(2020, 9, 25, 0, 0, 0, 0, time.UTC),
)
Equal(t, expected, actual)
expected = `
Diff:
--- Expected
+++ Actual
@@ -2,3 +2,3 @@
(assert.diffTestingStringer) (len=3) stringer:one,
- (assert.diffTestingStringer) (len=3) stringer:two
+ (assert.diffTestingStringer) (len=5) stringer:three
}
`
actual = diff(
[]diffTestingStringer{"one", "two"},
[]diffTestingStringer{"one", "three"},
)
Equal(t, expected, actual)
}
func TestTimeEqualityErrorFormatting(t *testing.T) {