From 1c8eb0f5c956efafe7a2e3c96058baa12aa74e6d Mon Sep 17 00:00:00 2001 From: Varun Chawla Date: Tue, 10 Feb 2026 00:14:27 -0800 Subject: [PATCH 1/7] fix: use %#v instead of %q in Subset/NotSubset error messages The %q format verb is intended for quoted strings and treats byte/int slice elements as rune literals (e.g., '\x01' instead of 1). For types that don't implement the fmt.Stringer interface with %q, it produces broken output like %!q(bool=true). This changes NotSubset (and the unsupported-type errors in Subset) to use %#v, which produces proper Go-syntax representation for all types. This is consistent with how Subset already formats its "does not contain" failure messages. Before: [%!q(bool=true)] is a subset of [%!q(bool=true)] After: []bool{true} is a subset of []bool{true} Fixes #1800 Co-Authored-By: Claude Opus 4.6 --- assert/assertions.go | 14 +++++++------- assert/assertions_test.go | 20 ++++++++++---------- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/assert/assertions.go b/assert/assertions.go index 6950636d3..8e2cac4d3 100644 --- a/assert/assertions.go +++ b/assert/assertions.go @@ -1015,12 +1015,12 @@ func Subset(t TestingT, list, subset interface{}, msgAndArgs ...interface{}) (ok listKind := reflect.TypeOf(list).Kind() if listKind != reflect.Array && listKind != reflect.Slice && listKind != reflect.Map { - return Fail(t, fmt.Sprintf("%q has an unsupported type %s", list, listKind), msgAndArgs...) + return Fail(t, fmt.Sprintf("%#v has an unsupported type %s", list, listKind), msgAndArgs...) } subsetKind := reflect.TypeOf(subset).Kind() if subsetKind != reflect.Array && subsetKind != reflect.Slice && subsetKind != reflect.Map { - return Fail(t, fmt.Sprintf("%q has an unsupported type %s", subset, subsetKind), msgAndArgs...) + return Fail(t, fmt.Sprintf("%#v has an unsupported type %s", subset, subsetKind), msgAndArgs...) } if subsetKind == reflect.Map && listKind == reflect.Map { @@ -1083,12 +1083,12 @@ func NotSubset(t TestingT, list, subset interface{}, msgAndArgs ...interface{}) listKind := reflect.TypeOf(list).Kind() if listKind != reflect.Array && listKind != reflect.Slice && listKind != reflect.Map { - return Fail(t, fmt.Sprintf("%q has an unsupported type %s", list, listKind), msgAndArgs...) + return Fail(t, fmt.Sprintf("%#v has an unsupported type %s", list, listKind), msgAndArgs...) } subsetKind := reflect.TypeOf(subset).Kind() if subsetKind != reflect.Array && subsetKind != reflect.Slice && subsetKind != reflect.Map { - return Fail(t, fmt.Sprintf("%q has an unsupported type %s", subset, subsetKind), msgAndArgs...) + return Fail(t, fmt.Sprintf("%#v has an unsupported type %s", subset, subsetKind), msgAndArgs...) } if subsetKind == reflect.Map && listKind == reflect.Map { @@ -1107,7 +1107,7 @@ func NotSubset(t TestingT, list, subset interface{}, msgAndArgs ...interface{}) } } - return Fail(t, fmt.Sprintf("%s is a subset of %s", truncatingFormat("%q", subset), truncatingFormat("%q", list)), msgAndArgs...) + return Fail(t, fmt.Sprintf("%s is a subset of %s", truncatingFormat("%#v", subset), truncatingFormat("%#v", list)), msgAndArgs...) } subsetList := reflect.ValueOf(subset) @@ -1122,14 +1122,14 @@ func NotSubset(t TestingT, list, subset interface{}, msgAndArgs ...interface{}) element := subsetList.Index(i).Interface() ok, found := containsElement(list, element) if !ok { - return Fail(t, fmt.Sprintf("%q could not be applied builtin len()", list), msgAndArgs...) + return Fail(t, fmt.Sprintf("%#v could not be applied builtin len()", list), msgAndArgs...) } if !found { return true } } - return Fail(t, fmt.Sprintf("%s is a subset of %s", truncatingFormat("%q", subset), truncatingFormat("%q", list)), msgAndArgs...) + return Fail(t, fmt.Sprintf("%s is a subset of %s", truncatingFormat("%#v", subset), truncatingFormat("%#v", list)), msgAndArgs...) } // ElementsMatch asserts that the specified listA(array, slice...) is equal to specified diff --git a/assert/assertions_test.go b/assert/assertions_test.go index 4975f5e41..d6795928c 100644 --- a/assert/assertions_test.go +++ b/assert/assertions_test.go @@ -1202,10 +1202,10 @@ func TestSubsetNotSubset(t *testing.T) { }{ // cases that are expected to contain {[]int{1, 2, 3}, nil, true, `nil is the empty set which is a subset of every set`}, - {[]int{1, 2, 3}, []int{}, true, `[] is a subset of ['\x01' '\x02' '\x03']`}, - {[]int{1, 2, 3}, []int{1, 2}, true, `['\x01' '\x02'] is a subset of ['\x01' '\x02' '\x03']`}, - {[]int{1, 2, 3}, []int{1, 2, 3}, true, `['\x01' '\x02' '\x03'] is a subset of ['\x01' '\x02' '\x03']`}, - {[]string{"hello", "world"}, []string{"hello"}, true, `["hello"] is a subset of ["hello" "world"]`}, + {[]int{1, 2, 3}, []int{}, true, `[]int{} is a subset of []int{1, 2, 3}`}, + {[]int{1, 2, 3}, []int{1, 2}, true, `[]int{1, 2} is a subset of []int{1, 2, 3}`}, + {[]int{1, 2, 3}, []int{1, 2, 3}, true, `[]int{1, 2, 3} is a subset of []int{1, 2, 3}`}, + {[]string{"hello", "world"}, []string{"hello"}, true, `[]string{"hello"} is a subset of []string{"hello", "world"}`}, {map[string]string{ "a": "x", "c": "z", @@ -1213,8 +1213,8 @@ func TestSubsetNotSubset(t *testing.T) { }, map[string]string{ "a": "x", "b": "y", - }, true, `map["a":"x" "b":"y"] is a subset of map["a":"x" "b":"y" "c":"z"]`}, - {[]string{"a", "b", "c"}, map[string]int{"a": 1, "c": 3}, true, `map["a":'\x01' "c":'\x03'] is a subset of ["a" "b" "c"]`}, + }, true, `map[string]string{"a":"x", "b":"y"} is a subset of map[string]string{"a":"x", "b":"y", "c":"z"}`}, + {[]string{"a", "b", "c"}, map[string]int{"a": 1, "c": 3}, true, `map[string]int{"a":1, "c":3} is a subset of []string{"a", "b", "c"}`}, // cases that are expected not to contain {[]string{"hello", "world"}, []string{"hello", "testify"}, false, `[]string{"hello", "world"} does not contain "testify"`}, @@ -4032,8 +4032,8 @@ func TestNotSubsetWithSliceTooLongToPrint(t *testing.T) { NotSubset(mockT, longSlice, longSlice) Contains(t, mockT.errorString(), ` Error Trace: - Error: ['\x00' '\x00' '\x00'`) - Contains(t, mockT.errorString(), `<... truncated> is a subset of ['\x00' '\x00' '\x00'`) + Error: []int{0, 0, 0`) + Contains(t, mockT.errorString(), `<... truncated> is a subset of []int{0, 0, 0`) } func TestNotSubsetWithMapTooLongToPrint(t *testing.T) { @@ -4043,8 +4043,8 @@ func TestNotSubsetWithMapTooLongToPrint(t *testing.T) { NotSubset(mockT, map[int][]int{1: longSlice}, map[int][]int{1: longSlice}) Contains(t, mockT.errorString(), ` Error Trace: - Error: map['\x01':['\x00' '\x00' '\x00'`) - Contains(t, mockT.errorString(), `<... truncated> is a subset of map['\x01':['\x00' '\x00' '\x00'`) + Error: map[int][]int{1:[]int{0, 0, 0`) + Contains(t, mockT.errorString(), `<... truncated> is a subset of map[int][]int{1:[]int{0, 0, 0`) } func TestSameWithSliceTooLongToPrint(t *testing.T) { From e88a6d7c7ccd61be156bed38f1261a8b69b444e4 Mon Sep 17 00:00:00 2001 From: Varun Chawla Date: Thu, 19 Feb 2026 21:58:50 -0800 Subject: [PATCH 2/7] fix: narrow %#v change to only NotSubset failure messages Revert %q -> %#v in "unsupported type" error paths for both Subset and NotSubset, keeping the fix only for the "is a subset of" and "could not be applied builtin len()" messages that were actually producing confusing output. Co-Authored-By: Claude Opus 4.6 --- assert/assertions.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/assert/assertions.go b/assert/assertions.go index 8e2cac4d3..4cd2c5776 100644 --- a/assert/assertions.go +++ b/assert/assertions.go @@ -1015,12 +1015,12 @@ func Subset(t TestingT, list, subset interface{}, msgAndArgs ...interface{}) (ok listKind := reflect.TypeOf(list).Kind() if listKind != reflect.Array && listKind != reflect.Slice && listKind != reflect.Map { - return Fail(t, fmt.Sprintf("%#v has an unsupported type %s", list, listKind), msgAndArgs...) + return Fail(t, fmt.Sprintf("%q has an unsupported type %s", list, listKind), msgAndArgs...) } subsetKind := reflect.TypeOf(subset).Kind() if subsetKind != reflect.Array && subsetKind != reflect.Slice && subsetKind != reflect.Map { - return Fail(t, fmt.Sprintf("%#v has an unsupported type %s", subset, subsetKind), msgAndArgs...) + return Fail(t, fmt.Sprintf("%q has an unsupported type %s", subset, subsetKind), msgAndArgs...) } if subsetKind == reflect.Map && listKind == reflect.Map { @@ -1083,12 +1083,12 @@ func NotSubset(t TestingT, list, subset interface{}, msgAndArgs ...interface{}) listKind := reflect.TypeOf(list).Kind() if listKind != reflect.Array && listKind != reflect.Slice && listKind != reflect.Map { - return Fail(t, fmt.Sprintf("%#v has an unsupported type %s", list, listKind), msgAndArgs...) + return Fail(t, fmt.Sprintf("%q has an unsupported type %s", list, listKind), msgAndArgs...) } subsetKind := reflect.TypeOf(subset).Kind() if subsetKind != reflect.Array && subsetKind != reflect.Slice && subsetKind != reflect.Map { - return Fail(t, fmt.Sprintf("%#v has an unsupported type %s", subset, subsetKind), msgAndArgs...) + return Fail(t, fmt.Sprintf("%q has an unsupported type %s", subset, subsetKind), msgAndArgs...) } if subsetKind == reflect.Map && listKind == reflect.Map { From 26028e95f4ce5644c8e50fbaaa681e0d7300e62e Mon Sep 17 00:00:00 2001 From: Varun Chawla Date: Sun, 22 Feb 2026 18:50:10 -0800 Subject: [PATCH 3/7] test: add unit tests for Subset/NotSubset error message formatting Add TestSubsetNotSubsetErrorMessages to verify that error messages use %#v (Go-syntax) formatting across various data types: bool, float64, uint, byte, struct slices and maps. Each subtest also asserts that no broken %q artifacts (%!q) appear in the output. --- assert/assertions_test.go | 95 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 95 insertions(+) diff --git a/assert/assertions_test.go b/assert/assertions_test.go index d6795928c..e30202ea1 100644 --- a/assert/assertions_test.go +++ b/assert/assertions_test.go @@ -1288,6 +1288,101 @@ func TestNotSubsetNil(t *testing.T) { } } +// TestSubsetNotSubsetErrorMessages verifies that Subset and NotSubset produce +// readable error messages using Go-syntax formatting (%#v) for various data types. +// This catches regressions where %q was used, which produces broken output like +// %!q(bool=true) for non-string types. +func TestSubsetNotSubsetErrorMessages(t *testing.T) { + t.Parallel() + + t.Run("NotSubset with bool slices", func(t *testing.T) { + mockT := new(mockTestingT) + NotSubset(mockT, []bool{true, false}, []bool{true}) + msg := mockT.errorString() + Contains(t, msg, "[]bool{true} is a subset of []bool{true, false}") + // Ensure no broken %q formatting like %!q(bool=true) + NotContains(t, msg, "%!q") + }) + + t.Run("NotSubset with float64 slices", func(t *testing.T) { + mockT := new(mockTestingT) + NotSubset(mockT, []float64{1.1, 2.2, 3.3}, []float64{1.1, 2.2}) + msg := mockT.errorString() + Contains(t, msg, "[]float64{1.1, 2.2} is a subset of []float64{1.1, 2.2, 3.3}") + NotContains(t, msg, "%!q") + }) + + t.Run("NotSubset with uint slices", func(t *testing.T) { + mockT := new(mockTestingT) + NotSubset(mockT, []uint{10, 20, 30}, []uint{10, 20}) + msg := mockT.errorString() + Contains(t, msg, "[]uint{0xa, 0x14} is a subset of []uint{0xa, 0x14, 0x1e}") + NotContains(t, msg, "%!q") + }) + + t.Run("NotSubset with map of int to bool", func(t *testing.T) { + mockT := new(mockTestingT) + NotSubset(mockT, + map[int]bool{1: true, 2: false, 3: true}, + map[int]bool{1: true, 2: false}, + ) + msg := mockT.errorString() + Contains(t, msg, "is a subset of") + // Verify %#v formatting is used (type info present, no %!q artifacts) + Contains(t, msg, "map[int]bool{") + NotContains(t, msg, "%!q") + }) + + t.Run("Subset failure with bool slices", func(t *testing.T) { + mockT := new(mockTestingT) + Subset(mockT, []bool{true}, []bool{true, false}) + msg := mockT.errorString() + Contains(t, msg, "[]bool{true} does not contain false") + NotContains(t, msg, "%!q") + }) + + t.Run("Subset failure with float64 slices", func(t *testing.T) { + mockT := new(mockTestingT) + Subset(mockT, []float64{1.1, 2.2}, []float64{1.1, 3.3}) + msg := mockT.errorString() + Contains(t, msg, "does not contain 3.3") + NotContains(t, msg, "%!q") + }) + + t.Run("NotSubset with byte slices", func(t *testing.T) { + mockT := new(mockTestingT) + NotSubset(mockT, []byte{0x01, 0x02, 0x03}, []byte{0x01, 0x02}) + msg := mockT.errorString() + Contains(t, msg, "is a subset of") + // %#v for byte slices uses hex notation like []byte{0x1, 0x2} + Contains(t, msg, "[]byte{") + NotContains(t, msg, "%!q") + }) + + t.Run("NotSubset with struct slices", func(t *testing.T) { + type item struct { + Name string + Val int + } + mockT := new(mockTestingT) + list := []item{{"a", 1}, {"b", 2}, {"c", 3}} + subset := []item{{"a", 1}, {"b", 2}} + NotSubset(mockT, list, subset) + msg := mockT.errorString() + Contains(t, msg, "is a subset of") + Contains(t, msg, "assert.item{") + NotContains(t, msg, "%!q") + }) + + t.Run("NotSubset with empty bool slice", func(t *testing.T) { + mockT := new(mockTestingT) + NotSubset(mockT, []bool{true, false}, []bool{}) + msg := mockT.errorString() + Contains(t, msg, "[]bool{} is a subset of []bool{true, false}") + }) +} + + func Test_containsElement(t *testing.T) { t.Parallel() From c469490919807c479f934cb42a060e36228be0bf Mon Sep 17 00:00:00 2001 From: Varun Chawla Date: Sun, 22 Feb 2026 20:41:11 -0800 Subject: [PATCH 4/7] fix: run gofmt on assertions_test.go --- assert/assertions_test.go | 1 - 1 file changed, 1 deletion(-) diff --git a/assert/assertions_test.go b/assert/assertions_test.go index e30202ea1..0b9c2ffb9 100644 --- a/assert/assertions_test.go +++ b/assert/assertions_test.go @@ -1382,7 +1382,6 @@ func TestSubsetNotSubsetErrorMessages(t *testing.T) { }) } - func Test_containsElement(t *testing.T) { t.Parallel() From 1081b06cfe761ebe118d482c2dda415639a44e9a Mon Sep 17 00:00:00 2001 From: Varun Chawla Date: Mon, 23 Feb 2026 23:33:03 -0800 Subject: [PATCH 5/7] test: expand Subset/NotSubset error message coverage for more types Add tests for string slices (verifying %#v still readable), map-to-map with int keys, Subset failure with map of int keys, Subset failure with uint slices, and NotSubset with int32 slices. These cover additional code paths and type combinations to catch formatting regressions. --- assert/assertions_test.go | 45 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/assert/assertions_test.go b/assert/assertions_test.go index 0b9c2ffb9..e0073d98f 100644 --- a/assert/assertions_test.go +++ b/assert/assertions_test.go @@ -1380,6 +1380,51 @@ func TestSubsetNotSubsetErrorMessages(t *testing.T) { msg := mockT.errorString() Contains(t, msg, "[]bool{} is a subset of []bool{true, false}") }) + + t.Run("NotSubset with string slices still readable", func(t *testing.T) { + // Strings were the one type %q handled well; verify %#v still produces + // clear output (quoted strings with type info). + mockT := new(mockTestingT) + NotSubset(mockT, []string{"hello", "world"}, []string{"hello"}) + msg := mockT.errorString() + Contains(t, msg, `[]string{"hello"} is a subset of []string{"hello", "world"}`) + NotContains(t, msg, "%!q") + }) + + t.Run("NotSubset map-to-map with int keys", func(t *testing.T) { + mockT := new(mockTestingT) + NotSubset(mockT, map[int]string{1: "a", 2: "b"}, map[int]string{1: "a"}) + msg := mockT.errorString() + Contains(t, msg, "is a subset of") + Contains(t, msg, "map[int]string{") + NotContains(t, msg, "%!q") + }) + + t.Run("Subset failure with map of int keys", func(t *testing.T) { + mockT := new(mockTestingT) + Subset(mockT, map[int]string{1: "a"}, map[int]string{1: "a", 2: "b"}) + msg := mockT.errorString() + Contains(t, msg, "does not contain") + Contains(t, msg, "map[int]string{") + NotContains(t, msg, "%!q") + }) + + t.Run("Subset failure with uint slices", func(t *testing.T) { + mockT := new(mockTestingT) + Subset(mockT, []uint{10, 20}, []uint{10, 30}) + msg := mockT.errorString() + Contains(t, msg, "does not contain") + NotContains(t, msg, "%!q") + }) + + t.Run("NotSubset with int32 slices", func(t *testing.T) { + mockT := new(mockTestingT) + NotSubset(mockT, []int32{1, 2, 3}, []int32{1, 2}) + msg := mockT.errorString() + Contains(t, msg, "is a subset of") + Contains(t, msg, "[]int32{") + NotContains(t, msg, "%!q") + }) } func Test_containsElement(t *testing.T) { From 2c1a2b045b6f399b873b9bd9bf7fb8f9b8f833ec Mon Sep 17 00:00:00 2001 From: Varun Chawla Date: Fri, 27 Feb 2026 22:12:30 -0800 Subject: [PATCH 6/7] fix: use %#v in remaining unsupported type error messages and add tests Address Copilot review feedback: change the remaining %q format verbs to %#v in the "unsupported type" error paths of Subset, NotSubset, and isList. This prevents broken output like %!q(bool=true) when non-string types are passed where a slice/map is expected. Add TestSubsetNotSubsetUnsupportedTypes with 5 subtests covering each unsupported-type error path in Subset, NotSubset, and ElementsMatch. --- assert/assertions.go | 10 ++++---- assert/assertions_test.go | 53 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 58 insertions(+), 5 deletions(-) diff --git a/assert/assertions.go b/assert/assertions.go index 4cd2c5776..9cc1363e1 100644 --- a/assert/assertions.go +++ b/assert/assertions.go @@ -1015,12 +1015,12 @@ func Subset(t TestingT, list, subset interface{}, msgAndArgs ...interface{}) (ok listKind := reflect.TypeOf(list).Kind() if listKind != reflect.Array && listKind != reflect.Slice && listKind != reflect.Map { - return Fail(t, fmt.Sprintf("%q has an unsupported type %s", list, listKind), msgAndArgs...) + return Fail(t, fmt.Sprintf("%#v has an unsupported type %s", list, listKind), msgAndArgs...) } subsetKind := reflect.TypeOf(subset).Kind() if subsetKind != reflect.Array && subsetKind != reflect.Slice && subsetKind != reflect.Map { - return Fail(t, fmt.Sprintf("%q has an unsupported type %s", subset, subsetKind), msgAndArgs...) + return Fail(t, fmt.Sprintf("%#v has an unsupported type %s", subset, subsetKind), msgAndArgs...) } if subsetKind == reflect.Map && listKind == reflect.Map { @@ -1083,12 +1083,12 @@ func NotSubset(t TestingT, list, subset interface{}, msgAndArgs ...interface{}) listKind := reflect.TypeOf(list).Kind() if listKind != reflect.Array && listKind != reflect.Slice && listKind != reflect.Map { - return Fail(t, fmt.Sprintf("%q has an unsupported type %s", list, listKind), msgAndArgs...) + return Fail(t, fmt.Sprintf("%#v has an unsupported type %s", list, listKind), msgAndArgs...) } subsetKind := reflect.TypeOf(subset).Kind() if subsetKind != reflect.Array && subsetKind != reflect.Slice && subsetKind != reflect.Map { - return Fail(t, fmt.Sprintf("%q has an unsupported type %s", subset, subsetKind), msgAndArgs...) + return Fail(t, fmt.Sprintf("%#v has an unsupported type %s", subset, subsetKind), msgAndArgs...) } if subsetKind == reflect.Map && listKind == reflect.Map { @@ -1162,7 +1162,7 @@ func ElementsMatch(t TestingT, listA, listB interface{}, msgAndArgs ...interface func isList(t TestingT, list interface{}, msgAndArgs ...interface{}) (ok bool) { kind := reflect.TypeOf(list).Kind() if kind != reflect.Array && kind != reflect.Slice { - return Fail(t, fmt.Sprintf("%q has an unsupported type %s, expecting array or slice", list, kind), + return Fail(t, fmt.Sprintf("%#v has an unsupported type %s, expecting array or slice", list, kind), msgAndArgs...) } return true diff --git a/assert/assertions_test.go b/assert/assertions_test.go index e0073d98f..63ad6eb43 100644 --- a/assert/assertions_test.go +++ b/assert/assertions_test.go @@ -1427,6 +1427,59 @@ func TestSubsetNotSubsetErrorMessages(t *testing.T) { }) } +// TestSubsetNotSubsetUnsupportedTypes verifies that Subset and NotSubset +// produce readable error messages when called with unsupported types +// (not array, slice, or map). This covers the "unsupported type" error +// paths that previously used %q formatting. +func TestSubsetNotSubsetUnsupportedTypes(t *testing.T) { + t.Parallel() + + t.Run("Subset with unsupported list type", func(t *testing.T) { + mockT := new(mockTestingT) + Subset(mockT, 42, []int{1}) + msg := mockT.errorString() + Contains(t, msg, "has an unsupported type") + Contains(t, msg, "int") + NotContains(t, msg, "%!q") + }) + + t.Run("Subset with unsupported subset type", func(t *testing.T) { + mockT := new(mockTestingT) + Subset(mockT, []int{1, 2}, "not a slice") + msg := mockT.errorString() + Contains(t, msg, "has an unsupported type") + Contains(t, msg, "string") + NotContains(t, msg, "%!q") + }) + + t.Run("NotSubset with unsupported list type", func(t *testing.T) { + mockT := new(mockTestingT) + NotSubset(mockT, true, []bool{true}) + msg := mockT.errorString() + Contains(t, msg, "has an unsupported type") + Contains(t, msg, "bool") + NotContains(t, msg, "%!q") + }) + + t.Run("NotSubset with unsupported subset type", func(t *testing.T) { + mockT := new(mockTestingT) + NotSubset(mockT, []int{1, 2}, 42) + msg := mockT.errorString() + Contains(t, msg, "has an unsupported type") + Contains(t, msg, "int") + NotContains(t, msg, "%!q") + }) + + t.Run("ElementsMatch with unsupported type", func(t *testing.T) { + mockT := new(mockTestingT) + ElementsMatch(mockT, "not a slice", []int{1}) + msg := mockT.errorString() + Contains(t, msg, "has an unsupported type") + Contains(t, msg, "expecting array or slice") + NotContains(t, msg, "%!q") + }) +} + func Test_containsElement(t *testing.T) { t.Parallel() From 841c7047b86c3a88ba6a8b528c662cb84d1f3d08 Mon Sep 17 00:00:00 2001 From: Varun Chawla Date: Tue, 10 Mar 2026 06:25:49 -0700 Subject: [PATCH 7/7] address review feedback: simplify assertions, add issue references - Remove NotContains %!q checks per reviewer suggestion, Contains is sufficient - Add related issue link (#1800) to test function comments --- assert/assertions_test.go | 22 ++-------------------- 1 file changed, 2 insertions(+), 20 deletions(-) diff --git a/assert/assertions_test.go b/assert/assertions_test.go index 63ad6eb43..cd5b4ddd7 100644 --- a/assert/assertions_test.go +++ b/assert/assertions_test.go @@ -1292,6 +1292,7 @@ func TestNotSubsetNil(t *testing.T) { // readable error messages using Go-syntax formatting (%#v) for various data types. // This catches regressions where %q was used, which produces broken output like // %!q(bool=true) for non-string types. +// Related: https://github.com/stretchr/testify/issues/1800 func TestSubsetNotSubsetErrorMessages(t *testing.T) { t.Parallel() @@ -1300,8 +1301,6 @@ func TestSubsetNotSubsetErrorMessages(t *testing.T) { NotSubset(mockT, []bool{true, false}, []bool{true}) msg := mockT.errorString() Contains(t, msg, "[]bool{true} is a subset of []bool{true, false}") - // Ensure no broken %q formatting like %!q(bool=true) - NotContains(t, msg, "%!q") }) t.Run("NotSubset with float64 slices", func(t *testing.T) { @@ -1309,7 +1308,6 @@ func TestSubsetNotSubsetErrorMessages(t *testing.T) { NotSubset(mockT, []float64{1.1, 2.2, 3.3}, []float64{1.1, 2.2}) msg := mockT.errorString() Contains(t, msg, "[]float64{1.1, 2.2} is a subset of []float64{1.1, 2.2, 3.3}") - NotContains(t, msg, "%!q") }) t.Run("NotSubset with uint slices", func(t *testing.T) { @@ -1317,7 +1315,6 @@ func TestSubsetNotSubsetErrorMessages(t *testing.T) { NotSubset(mockT, []uint{10, 20, 30}, []uint{10, 20}) msg := mockT.errorString() Contains(t, msg, "[]uint{0xa, 0x14} is a subset of []uint{0xa, 0x14, 0x1e}") - NotContains(t, msg, "%!q") }) t.Run("NotSubset with map of int to bool", func(t *testing.T) { @@ -1328,9 +1325,7 @@ func TestSubsetNotSubsetErrorMessages(t *testing.T) { ) msg := mockT.errorString() Contains(t, msg, "is a subset of") - // Verify %#v formatting is used (type info present, no %!q artifacts) Contains(t, msg, "map[int]bool{") - NotContains(t, msg, "%!q") }) t.Run("Subset failure with bool slices", func(t *testing.T) { @@ -1338,7 +1333,6 @@ func TestSubsetNotSubsetErrorMessages(t *testing.T) { Subset(mockT, []bool{true}, []bool{true, false}) msg := mockT.errorString() Contains(t, msg, "[]bool{true} does not contain false") - NotContains(t, msg, "%!q") }) t.Run("Subset failure with float64 slices", func(t *testing.T) { @@ -1346,7 +1340,6 @@ func TestSubsetNotSubsetErrorMessages(t *testing.T) { Subset(mockT, []float64{1.1, 2.2}, []float64{1.1, 3.3}) msg := mockT.errorString() Contains(t, msg, "does not contain 3.3") - NotContains(t, msg, "%!q") }) t.Run("NotSubset with byte slices", func(t *testing.T) { @@ -1356,7 +1349,6 @@ func TestSubsetNotSubsetErrorMessages(t *testing.T) { Contains(t, msg, "is a subset of") // %#v for byte slices uses hex notation like []byte{0x1, 0x2} Contains(t, msg, "[]byte{") - NotContains(t, msg, "%!q") }) t.Run("NotSubset with struct slices", func(t *testing.T) { @@ -1371,7 +1363,6 @@ func TestSubsetNotSubsetErrorMessages(t *testing.T) { msg := mockT.errorString() Contains(t, msg, "is a subset of") Contains(t, msg, "assert.item{") - NotContains(t, msg, "%!q") }) t.Run("NotSubset with empty bool slice", func(t *testing.T) { @@ -1388,7 +1379,6 @@ func TestSubsetNotSubsetErrorMessages(t *testing.T) { NotSubset(mockT, []string{"hello", "world"}, []string{"hello"}) msg := mockT.errorString() Contains(t, msg, `[]string{"hello"} is a subset of []string{"hello", "world"}`) - NotContains(t, msg, "%!q") }) t.Run("NotSubset map-to-map with int keys", func(t *testing.T) { @@ -1397,7 +1387,6 @@ func TestSubsetNotSubsetErrorMessages(t *testing.T) { msg := mockT.errorString() Contains(t, msg, "is a subset of") Contains(t, msg, "map[int]string{") - NotContains(t, msg, "%!q") }) t.Run("Subset failure with map of int keys", func(t *testing.T) { @@ -1406,7 +1395,6 @@ func TestSubsetNotSubsetErrorMessages(t *testing.T) { msg := mockT.errorString() Contains(t, msg, "does not contain") Contains(t, msg, "map[int]string{") - NotContains(t, msg, "%!q") }) t.Run("Subset failure with uint slices", func(t *testing.T) { @@ -1414,7 +1402,6 @@ func TestSubsetNotSubsetErrorMessages(t *testing.T) { Subset(mockT, []uint{10, 20}, []uint{10, 30}) msg := mockT.errorString() Contains(t, msg, "does not contain") - NotContains(t, msg, "%!q") }) t.Run("NotSubset with int32 slices", func(t *testing.T) { @@ -1423,7 +1410,6 @@ func TestSubsetNotSubsetErrorMessages(t *testing.T) { msg := mockT.errorString() Contains(t, msg, "is a subset of") Contains(t, msg, "[]int32{") - NotContains(t, msg, "%!q") }) } @@ -1431,6 +1417,7 @@ func TestSubsetNotSubsetErrorMessages(t *testing.T) { // produce readable error messages when called with unsupported types // (not array, slice, or map). This covers the "unsupported type" error // paths that previously used %q formatting. +// Related: https://github.com/stretchr/testify/issues/1800 func TestSubsetNotSubsetUnsupportedTypes(t *testing.T) { t.Parallel() @@ -1440,7 +1427,6 @@ func TestSubsetNotSubsetUnsupportedTypes(t *testing.T) { msg := mockT.errorString() Contains(t, msg, "has an unsupported type") Contains(t, msg, "int") - NotContains(t, msg, "%!q") }) t.Run("Subset with unsupported subset type", func(t *testing.T) { @@ -1449,7 +1435,6 @@ func TestSubsetNotSubsetUnsupportedTypes(t *testing.T) { msg := mockT.errorString() Contains(t, msg, "has an unsupported type") Contains(t, msg, "string") - NotContains(t, msg, "%!q") }) t.Run("NotSubset with unsupported list type", func(t *testing.T) { @@ -1458,7 +1443,6 @@ func TestSubsetNotSubsetUnsupportedTypes(t *testing.T) { msg := mockT.errorString() Contains(t, msg, "has an unsupported type") Contains(t, msg, "bool") - NotContains(t, msg, "%!q") }) t.Run("NotSubset with unsupported subset type", func(t *testing.T) { @@ -1467,7 +1451,6 @@ func TestSubsetNotSubsetUnsupportedTypes(t *testing.T) { msg := mockT.errorString() Contains(t, msg, "has an unsupported type") Contains(t, msg, "int") - NotContains(t, msg, "%!q") }) t.Run("ElementsMatch with unsupported type", func(t *testing.T) { @@ -1476,7 +1459,6 @@ func TestSubsetNotSubsetUnsupportedTypes(t *testing.T) { msg := mockT.errorString() Contains(t, msg, "has an unsupported type") Contains(t, msg, "expecting array or slice") - NotContains(t, msg, "%!q") }) }