Repository: stretchr/testify
Pull request #1729 (2025-04-23 14:41:54)

fix Subset/NotSubset when calling with mixed input types
## Summary
`Subset`/`NotSubset` failed with "<subset> has unsupported type map" when called with a `list` of kind `array`/`slice` and `subset` of kind `map` which should work based on the documentation.

## Changes
I added support and test cases for these inputs.

## Motivation
Implementation should conform to documentation.

## Related issues



Patch:
diff --git a/assert/assertion_format.go b/assert/assertion_format.go
index ebcbae5..53c184c 100644
--- a/assert/assertion_format.go
+++ b/assert/assertion_format.go
@@ -117,8 +117,10 @@ func EqualValuesf(t TestingT, expected interface{}, actual interface{}, msg stri
 
 // Errorf asserts that a function returned an error (i.e. not `nil`).
 //
-//	actualObj, err := SomeFunction()
-//	assert.Errorf(t, err, "error message %s", "formatted")
+//	  actualObj, err := SomeFunction()
+//	  if assert.Errorf(t, err, "error message %s", "formatted") {
+//		   assert.Equal(t, expectedErrorf, err)
+//	  }
 func Errorf(t TestingT, err error, msg string, args ...interface{}) bool {
 	if h, ok := t.(tHelper); ok {
 		h.Helper()
@@ -691,12 +693,15 @@ func NotSamef(t TestingT, expected interface{}, actual interface{}, msg string,
 	return NotSame(t, expected, actual, append([]interface{}{msg}, args...)...)
 }
 
-// NotSubsetf asserts that the specified list(array, slice...) or map does NOT
-// contain all elements given in the specified subset list(array, slice...) or
-// map.
+// NotSubsetf asserts that the list (array, slice, or map) does NOT contain all
+// elements given in the subset (array, slice, or map).
+// Map elements are key-value pairs unless compared with an array or slice where
+// only the map key is evaluated.
 //
 //	assert.NotSubsetf(t, [1, 3, 4], [1, 2], "error message %s", "formatted")
 //	assert.NotSubsetf(t, {"x": 1, "y": 2}, {"z": 3}, "error message %s", "formatted")
+//	assert.NotSubsetf(t, [1, 3, 4], {1: "one", 2: "two"}, "error message %s", "formatted")
+//	assert.NotSubsetf(t, {"x": 1, "y": 2}, ["z"], "error message %s", "formatted")
 func NotSubsetf(t TestingT, list interface{}, subset interface{}, msg string, args ...interface{}) bool {
 	if h, ok := t.(tHelper); ok {
 		h.Helper()
@@ -780,11 +785,15 @@ func Samef(t TestingT, expected interface{}, actual interface{}, msg string, arg
 	return Same(t, expected, actual, append([]interface{}{msg}, args...)...)
 }
 
-// Subsetf asserts that the specified list(array, slice...) or map contains all
-// elements given in the specified subset list(array, slice...) or map.
+// Subsetf asserts that the list (array, slice, or map) contains all elements
+// given in the subset (array, slice, or map).
+// Map elements are key-value pairs unless compared with an array or slice where
+// only the map key is evaluated.
 //
 //	assert.Subsetf(t, [1, 2, 3], [1, 2], "error message %s", "formatted")
 //	assert.Subsetf(t, {"x": 1, "y": 2}, {"x": 1}, "error message %s", "formatted")
+//	assert.Subsetf(t, [1, 2, 3], {1: "one", 2: "two"}, "error message %s", "formatted")
+//	assert.Subsetf(t, {"x": 1, "y": 2}, ["x"], "error message %s", "formatted")
 func Subsetf(t TestingT, list interface{}, subset interface{}, msg string, args ...interface{}) bool {
 	if h, ok := t.(tHelper); ok {
 		h.Helper()
diff --git a/assert/assertion_forward.go b/assert/assertion_forward.go
index 6222d12..938bd24 100644
--- a/assert/assertion_forward.go
+++ b/assert/assertion_forward.go
@@ -224,8 +224,10 @@ func (a *Assertions) Equalf(expected interface{}, actual interface{}, msg string
 
 // Error asserts that a function returned an error (i.e. not `nil`).
 //
-//	actualObj, err := SomeFunction()
-//	a.Error(err)
+//	  actualObj, err := SomeFunction()
+//	  if a.Error(err) {
+//		   assert.Equal(t, expectedError, err)
+//	  }
 func (a *Assertions) Error(err error, msgAndArgs ...interface{}) bool {
 	if h, ok := a.t.(tHelper); ok {
 		h.Helper()
@@ -295,8 +297,10 @@ func (a *Assertions) ErrorIsf(err error, target error, msg string, args ...inter
 
 // Errorf asserts that a function returned an error (i.e. not `nil`).
 //
-//	actualObj, err := SomeFunction()
-//	a.Errorf(err, "error message %s", "formatted")
+//	  actualObj, err := SomeFunction()
+//	  if a.Errorf(err, "error message %s", "formatted") {
+//		   assert.Equal(t, expectedErrorf, err)
+//	  }
 func (a *Assertions) Errorf(err error, msg string, args ...interface{}) bool {
 	if h, ok := a.t.(tHelper); ok {
 		h.Helper()
@@ -1374,12 +1378,15 @@ func (a *Assertions) NotSamef(expected interface{}, actual interface{}, msg stri
 	return NotSamef(a.t, expected, actual, msg, args...)
 }
 
-// NotSubset asserts that the specified list(array, slice...) or map does NOT
-// contain all elements given in the specified subset list(array, slice...) or
-// map.
+// NotSubset asserts that the list (array, slice, or map) does NOT contain all
+// elements given in the subset (array, slice, or map).
+// Map elements are key-value pairs unless compared with an array or slice where
+// only the map key is evaluated.
 //
 //	a.NotSubset([1, 3, 4], [1, 2])
 //	a.NotSubset({"x": 1, "y": 2}, {"z": 3})
+//	a.NotSubset([1, 3, 4], {1: "one", 2: "two"})
+//	a.NotSubset({"x": 1, "y": 2}, ["z"])
 func (a *Assertions) NotSubset(list interface{}, subset interface{}, msgAndArgs ...interface{}) bool {
 	if h, ok := a.t.(tHelper); ok {
 		h.Helper()
@@ -1387,12 +1394,15 @@ func (a *Assertions) NotSubset(list interface{}, subset interface{}, msgAndArgs
 	return NotSubset(a.t, list, subset, msgAndArgs...)
 }
 
-// NotSubsetf asserts that the specified list(array, slice...) or map does NOT
-// contain all elements given in the specified subset list(array, slice...) or
-// map.
+// NotSubsetf asserts that the list (array, slice, or map) does NOT contain all
+// elements given in the subset (array, slice, or map).
+// Map elements are key-value pairs unless compared with an array or slice where
+// only the map key is evaluated.
 //
 //	a.NotSubsetf([1, 3, 4], [1, 2], "error message %s", "formatted")
 //	a.NotSubsetf({"x": 1, "y": 2}, {"z": 3}, "error message %s", "formatted")
+//	a.NotSubsetf([1, 3, 4], {1: "one", 2: "two"}, "error message %s", "formatted")
+//	a.NotSubsetf({"x": 1, "y": 2}, ["z"], "error message %s", "formatted")
 func (a *Assertions) NotSubsetf(list interface{}, subset interface{}, msg string, args ...interface{}) bool {
 	if h, ok := a.t.(tHelper); ok {
 		h.Helper()
@@ -1552,11 +1562,15 @@ func (a *Assertions) Samef(expected interface{}, actual interface{}, msg string,
 	return Samef(a.t, expected, actual, msg, args...)
 }
 
-// Subset asserts that the specified list(array, slice...) or map contains all
-// elements given in the specified subset list(array, slice...) or map.
+// Subset asserts that the list (array, slice, or map) contains all elements
+// given in the subset (array, slice, or map).
+// Map elements are key-value pairs unless compared with an array or slice where
+// only the map key is evaluated.
 //
 //	a.Subset([1, 2, 3], [1, 2])
 //	a.Subset({"x": 1, "y": 2}, {"x": 1})
+//	a.Subset([1, 2, 3], {1: "one", 2: "two"})
+//	a.Subset({"x": 1, "y": 2}, ["x"])
 func (a *Assertions) Subset(list interface{}, subset interface{}, msgAndArgs ...interface{}) bool {
 	if h, ok := a.t.(tHelper); ok {
 		h.Helper()
@@ -1564,11 +1578,15 @@ func (a *Assertions) Subset(list interface{}, subset interface{}, msgAndArgs ...
 	return Subset(a.t, list, subset, msgAndArgs...)
 }
 
-// Subsetf asserts that the specified list(array, slice...) or map contains all
-// elements given in the specified subset list(array, slice...) or map.
+// Subsetf asserts that the list (array, slice, or map) contains all elements
+// given in the subset (array, slice, or map).
+// Map elements are key-value pairs unless compared with an array or slice where
+// only the map key is evaluated.
 //
 //	a.Subsetf([1, 2, 3], [1, 2], "error message %s", "formatted")
 //	a.Subsetf({"x": 1, "y": 2}, {"x": 1}, "error message %s", "formatted")
+//	a.Subsetf([1, 2, 3], {1: "one", 2: "two"}, "error message %s", "formatted")
+//	a.Subsetf({"x": 1, "y": 2}, ["x"], "error message %s", "formatted")
 func (a *Assertions) Subsetf(list interface{}, subset interface{}, msg string, args ...interface{}) bool {
 	if h, ok := a.t.(tHelper); ok {
 		h.Helper()
diff --git a/assert/assertions.go b/assert/assertions.go
index 93da07e..3025206 100644
--- a/assert/assertions.go
+++ b/assert/assertions.go
@@ -982,11 +982,15 @@ func NotContains(t TestingT, s, contains interface{}, msgAndArgs ...interface{})
 
 }
 
-// Subset asserts that the specified list(array, slice...) or map contains all
-// elements given in the specified subset list(array, slice...) or map.
+// Subset asserts that the list (array, slice, or map) contains all elements
+// given in the subset (array, slice, or map).
+// Map elements are key-value pairs unless compared with an array or slice where
+// only the map key is evaluated.
 //
 //	assert.Subset(t, [1, 2, 3], [1, 2])
 //	assert.Subset(t, {"x": 1, "y": 2}, {"x": 1})
+//	assert.Subset(t, [1, 2, 3], {1: "one", 2: "two"})
+//	assert.Subset(t, {"x": 1, "y": 2}, ["x"])
 func Subset(t TestingT, list, subset interface{}, msgAndArgs ...interface{}) (ok bool) {
 	if h, ok := t.(tHelper); ok {
 		h.Helper()
@@ -1001,7 +1005,7 @@ func Subset(t TestingT, list, subset interface{}, msgAndArgs ...interface{}) (ok
 	}
 
 	subsetKind := reflect.TypeOf(subset).Kind()
-	if subsetKind != reflect.Array && subsetKind != reflect.Slice && listKind != reflect.Map {
+	if subsetKind != reflect.Array && subsetKind != reflect.Slice && subsetKind != reflect.Map {
 		return Fail(t, fmt.Sprintf("%q has an unsupported type %s", subset, subsetKind), msgAndArgs...)
 	}
 
@@ -1025,6 +1029,13 @@ func Subset(t TestingT, list, subset interface{}, msgAndArgs ...interface{}) (ok
 	}
 
 	subsetList := reflect.ValueOf(subset)
+	if subsetKind == reflect.Map {
+		keys := make([]interface{}, subsetList.Len())
+		for idx, key := range subsetList.MapKeys() {
+			keys[idx] = key.Interface()
+		}
+		subsetList = reflect.ValueOf(keys)
+	}
 	for i := 0; i < subsetList.Len(); i++ {
 		element := subsetList.Index(i).Interface()
 		ok, found := containsElement(list, element)
@@ -1039,12 +1050,15 @@ func Subset(t TestingT, list, subset interface{}, msgAndArgs ...interface{}) (ok
 	return true
 }
 
-// NotSubset asserts that the specified list(array, slice...) or map does NOT
-// contain all elements given in the specified subset list(array, slice...) or
-// map.
+// NotSubset asserts that the list (array, slice, or map) does NOT contain all
+// elements given in the subset (array, slice, or map).
+// Map elements are key-value pairs unless compared with an array or slice where
+// only the map key is evaluated.
 //
 //	assert.NotSubset(t, [1, 3, 4], [1, 2])
 //	assert.NotSubset(t, {"x": 1, "y": 2}, {"z": 3})
+//	assert.NotSubset(t, [1, 3, 4], {1: "one", 2: "two"})
+//	assert.NotSubset(t, {"x": 1, "y": 2}, ["z"])
 func NotSubset(t TestingT, list, subset interface{}, msgAndArgs ...interface{}) (ok bool) {
 	if h, ok := t.(tHelper); ok {
 		h.Helper()
@@ -1059,7 +1073,7 @@ func NotSubset(t TestingT, list, subset interface{}, msgAndArgs ...interface{})
 	}
 
 	subsetKind := reflect.TypeOf(subset).Kind()
-	if subsetKind != reflect.Array && subsetKind != reflect.Slice && listKind != reflect.Map {
+	if subsetKind != reflect.Array && subsetKind != reflect.Slice && subsetKind != reflect.Map {
 		return Fail(t, fmt.Sprintf("%q has an unsupported type %s", subset, subsetKind), msgAndArgs...)
 	}
 
@@ -1083,6 +1097,13 @@ func NotSubset(t TestingT, list, subset interface{}, msgAndArgs ...interface{})
 	}
 
 	subsetList := reflect.ValueOf(subset)
+	if subsetKind == reflect.Map {
+		keys := make([]interface{}, subsetList.Len())
+		for idx, key := range subsetList.MapKeys() {
+			keys[idx] = key.Interface()
+		}
+		subsetList = reflect.ValueOf(keys)
+	}
 	for i := 0; i < subsetList.Len(); i++ {
 		element := subsetList.Index(i).Interface()
 		ok, found := containsElement(list, element)
@@ -1609,8 +1630,10 @@ func NoError(t TestingT, err error, msgAndArgs ...interface{}) bool {
 
 // Error asserts that a function returned an error (i.e. not `nil`).
 //
-//	actualObj, err := SomeFunction()
-//	assert.Error(t, err)
+//	  actualObj, err := SomeFunction()
+//	  if assert.Error(t, err) {
+//		   assert.Equal(t, expectedError, err)
+//	  }
 func Error(t TestingT, err error, msgAndArgs ...interface{}) bool {
 	if err == nil {
 		if h, ok := t.(tHelper); ok {
@@ -1949,7 +1972,6 @@ func Eventually(t TestingT, condition func() bool, waitFor time.Duration, tick t
 	}
 
 	ch := make(chan bool, 1)
-	checkCond := func() { ch <- condition() }
 
 	timer := time.NewTimer(waitFor)
 	defer timer.Stop()
@@ -1957,23 +1979,18 @@ func Eventually(t TestingT, condition func() bool, waitFor time.Duration, tick t
 	ticker := time.NewTicker(tick)
 	defer ticker.Stop()
 
-	var tickC <-chan time.Time
-
-	// Check the condition once first on the initial call.
-	go checkCond()
-
-	for {
+	for tick := ticker.C; ; {
 		select {
 		case <-timer.C:
 			return Fail(t, "Condition never satisfied", msgAndArgs...)
-		case <-tickC:
-			tickC = nil
-			go checkCond()
+		case <-tick:
+			tick = nil
+			go func() { ch <- condition() }()
 		case v := <-ch:
 			if v {
 				return true
 			}
-			tickC = ticker.C
+			tick = ticker.C
 		}
 	}
 }
@@ -2043,42 +2060,35 @@ func EventuallyWithT(t TestingT, condition func(collect *CollectT), waitFor time
 	var lastFinishedTickErrs []error
 	ch := make(chan *CollectT, 1)
 
-	checkCond := func() {
-		collect := new(CollectT)
-		defer func() {
-			ch <- collect
-		}()
-		condition(collect)
-	}
-
 	timer := time.NewTimer(waitFor)
 	defer timer.Stop()
 
 	ticker := time.NewTicker(tick)
 	defer ticker.Stop()
 
-	var tickC <-chan time.Time
-
-	// Check the condition once first on the initial call.
-	go checkCond()
-
-	for {
+	for tick := ticker.C; ; {
 		select {
 		case <-timer.C:
 			for _, err := range lastFinishedTickErrs {
 				t.Errorf("%v", err)
 			}
 			return Fail(t, "Condition never satisfied", msgAndArgs...)
-		case <-tickC:
-			tickC = nil
-			go checkCond()
+		case <-tick:
+			tick = nil
+			go func() {
+				collect := new(CollectT)
+				defer func() {
+					ch <- collect
+				}()
+				condition(collect)
+			}()
 		case collect := <-ch:
 			if !collect.failed() {
 				return true
 			}
 			// Keep the errors from the last ended condition, so that they can be copied to t if timeout is reached.
 			lastFinishedTickErrs = collect.errors
-			tickC = ticker.C
+			tick = ticker.C
 		}
 	}
 }
@@ -2093,7 +2103,6 @@ func Never(t TestingT, condition func() bool, waitFor time.Duration, tick time.D
 	}
 
 	ch := make(chan bool, 1)
-	checkCond := func() { ch <- condition() }
 
 	timer := time.NewTimer(waitFor)
 	defer timer.Stop()
@@ -2101,23 +2110,18 @@ func Never(t TestingT, condition func() bool, waitFor time.Duration, tick time.D
 	ticker := time.NewTicker(tick)
 	defer ticker.Stop()
 
-	var tickC <-chan time.Time
-
-	// Check the condition once first on the initial call.
-	go checkCond()
-
-	for {
+	for tick := ticker.C; ; {
 		select {
 		case <-timer.C:
 			return true
-		case <-tickC:
-			tickC = nil
-			go checkCond()
+		case <-tick:
+			tick = nil
+			go func() { ch <- condition() }()
 		case v := <-ch:
 			if v {
 				return Fail(t, "Condition satisfied", msgAndArgs...)
 			}
-			tickC = ticker.C
+			tick = ticker.C
 		}
 	}
 }
@@ -2135,9 +2139,6 @@ func ErrorIs(t TestingT, err, target error, msgAndArgs ...interface{}) bool {
 	var expectedText string
 	if target != nil {
 		expectedText = target.Error()
-		if err == nil {
-			return Fail(t, fmt.Sprintf("Expected error with %q in chain but got nil.", expectedText), msgAndArgs...)
-		}
 	}
 
 	chain := buildErrorChainString(err, false)
diff --git a/assert/doc.go b/assert/doc.go
index a0b953a..4953981 100644
--- a/assert/doc.go
+++ b/assert/doc.go
@@ -1,9 +1,5 @@
 // Package assert provides a set of comprehensive testing tools for use with the normal Go testing system.
 //
-// # Note
-//
-// All functions in this package return a bool value indicating whether the assertion has passed.
-//
 // # Example Usage
 //
 // The following is a complete example using assert in a standard test function:
diff --git a/mock/mock.go b/mock/mock.go
index 73e020f..c81a0bd 100644
--- a/mock/mock.go
+++ b/mock/mock.go
@@ -948,8 +948,6 @@ func (args Arguments) Is(objects ...interface{}) bool {
 	return true
 }
 
-type outputRenderer func() string
-
 // Diff gets a string describing the differences between the arguments
 // and the specified objects.
 //
@@ -957,7 +955,7 @@ type outputRenderer func() string
 func (args Arguments) Diff(objects []interface{}) (string, int) {
 	// TODO: could return string as error and nil for No difference
 
-	var outputBuilder strings.Builder
+	output := "\n"
 	var differences int
 
 	maxArgCount := len(args)
@@ -965,35 +963,24 @@ func (args Arguments) Diff(objects []interface{}) (string, int) {
 		maxArgCount = len(objects)
 	}
 
-	outputRenderers := []outputRenderer{}
-
 	for i := 0; i < maxArgCount; i++ {
-		i := i
 		var actual, expected interface{}
-		var actualFmt, expectedFmt func() string
+		var actualFmt, expectedFmt string
 
 		if len(objects) <= i {
 			actual = "(Missing)"
-			actualFmt = func() string {
-				return "(Missing)"
-			}
+			actualFmt = "(Missing)"
 		} else {
 			actual = objects[i]
-			actualFmt = func() string {
-				return fmt.Sprintf("(%[1]T=%[1]v)", actual)
-			}
+			actualFmt = fmt.Sprintf("(%[1]T=%[1]v)", actual)
 		}
 
 		if len(args) <= i {
 			expected = "(Missing)"
-			expectedFmt = func() string {
-				return "(Missing)"
-			}
+			expectedFmt = "(Missing)"
 		} else {
 			expected = args[i]
-			expectedFmt = func() string {
-				return fmt.Sprintf("(%[1]T=%[1]v)", expected)
-			}
+			expectedFmt = fmt.Sprintf("(%[1]T=%[1]v)", expected)
 		}
 
 		if matcher, ok := expected.(argumentMatcher); ok {
@@ -1001,22 +988,16 @@ func (args Arguments) Diff(objects []interface{}) (string, int) {
 			func() {
 				defer func() {
 					if r := recover(); r != nil {
-						actualFmt = func() string {
-							return fmt.Sprintf("panic in argument matcher: %v", r)
-						}
+						actualFmt = fmt.Sprintf("panic in argument matcher: %v", r)
 					}
 				}()
 				matches = matcher.Matches(actual)
 			}()
 			if matches {
-				outputRenderers = append(outputRenderers, func() string {
-					return fmt.Sprintf("\t%d: PASS:  %s matched by %s\n", i, actualFmt(), matcher)
-				})
+				output = fmt.Sprintf("%s\t%d: PASS:  %s matched by %s\n", output, i, actualFmt, matcher)
 			} else {
 				differences++
-				outputRenderers = append(outputRenderers, func() string {
-					return fmt.Sprintf("\t%d: FAIL:  %s not matched by %s\n", i, actualFmt(), matcher)
-				})
+				output = fmt.Sprintf("%s\t%d: FAIL:  %s not matched by %s\n", output, i, actualFmt, matcher)
 			}
 		} else {
 			switch expected := expected.(type) {
@@ -1025,17 +1006,13 @@ func (args Arguments) Diff(objects []interface{}) (string, int) {
 				if reflect.TypeOf(actual).Name() != string(expected) && reflect.TypeOf(actual).String() != string(expected) {
 					// not match
 					differences++
-					outputRenderers = append(outputRenderers, func() string {
-						return fmt.Sprintf("\t%d: FAIL:  type %s != type %s - %s\n", i, expected, reflect.TypeOf(actual).Name(), actualFmt())
-					})
+					output = fmt.Sprintf("%s\t%d: FAIL:  type %s != type %s - %s\n", output, i, expected, reflect.TypeOf(actual).Name(), actualFmt)
 				}
 			case *IsTypeArgument:
 				actualT := reflect.TypeOf(actual)
 				if actualT != expected.t {
 					differences++
-					outputRenderers = append(outputRenderers, func() string {
-						return fmt.Sprintf("\t%d: FAIL:  type %s != type %s - %s\n", i, expected.t.Name(), actualT.Name(), actualFmt())
-					})
+					output = fmt.Sprintf("%s\t%d: FAIL:  type %s != type %s - %s\n", output, i, expected.t.Name(), actualT.Name(), actualFmt)
 				}
 			case *FunctionalOptionsArgument:
 				var name string
@@ -1046,36 +1023,26 @@ func (args Arguments) Diff(objects []interface{}) (string, int) {
 				const tName = "[]interface{}"
 				if name != reflect.TypeOf(actual).String() && len(expected.values) != 0 {
 					differences++
-					outputRenderers = append(outputRenderers, func() string {
-						return fmt.Sprintf("\t%d: FAIL:  type %s != type %s - %s\n", i, tName, reflect.TypeOf(actual).Name(), actualFmt())
-					})
+					output = fmt.Sprintf("%s\t%d: FAIL:  type %s != type %s - %s\n", output, i, tName, reflect.TypeOf(actual).Name(), actualFmt)
 				} else {
 					if ef, af := assertOpts(expected.values, actual); ef == "" && af == "" {
 						// match
-						outputRenderers = append(outputRenderers, func() string {
-							return fmt.Sprintf("\t%d: PASS:  %s == %s\n", i, tName, tName)
-						})
+						output = fmt.Sprintf("%s\t%d: PASS:  %s == %s\n", output, i, tName, tName)
 					} else {
 						// not match
 						differences++
-						outputRenderers = append(outputRenderers, func() string {
-							return fmt.Sprintf("\t%d: FAIL:  %s != %s\n", i, af, ef)
-						})
+						output = fmt.Sprintf("%s\t%d: FAIL:  %s != %s\n", output, i, af, ef)
 					}
 				}
 
 			default:
 				if assert.ObjectsAreEqual(expected, Anything) || assert.ObjectsAreEqual(actual, Anything) || assert.ObjectsAreEqual(actual, expected) {
 					// match
-					outputRenderers = append(outputRenderers, func() string {
-						return fmt.Sprintf("\t%d: PASS:  %s == %s\n", i, actualFmt(), expectedFmt())
-					})
+					output = fmt.Sprintf("%s\t%d: PASS:  %s == %s\n", output, i, actualFmt, expectedFmt)
 				} else {
 					// not match
 					differences++
-					outputRenderers = append(outputRenderers, func() string {
-						return fmt.Sprintf("\t%d: FAIL:  %s != %s\n", i, actualFmt(), expectedFmt())
-					})
+					output = fmt.Sprintf("%s\t%d: FAIL:  %s != %s\n", output, i, actualFmt, expectedFmt)
 				}
 			}
 		}
@@ -1086,12 +1053,7 @@ func (args Arguments) Diff(objects []interface{}) (string, int) {
 		return "No differences.", differences
 	}
 
-	outputBuilder.WriteString("\n")
-	for _, r := range outputRenderers {
-		outputBuilder.WriteString(r())
-	}
-
-	return outputBuilder.String(), differences
+	return output, differences
 }
 
 // Assert compares the arguments with the specified objects and fails if
diff --git a/require/require.go b/require/require.go
index 5cc14fa..b78c5cf 100644
--- a/require/require.go
+++ b/require/require.go
@@ -279,8 +279,10 @@ func Equalf(t TestingT, expected interface{}, actual interface{}, msg string, ar
 
 // Error asserts that a function returned an error (i.e. not `nil`).
 //
-//	actualObj, err := SomeFunction()
-//	require.Error(t, err)
+//	  actualObj, err := SomeFunction()
+//	  if require.Error(t, err) {
+//		   require.Equal(t, expectedError, err)
+//	  }
 func Error(t TestingT, err error, msgAndArgs ...interface{}) {
 	if h, ok := t.(tHelper); ok {
 		h.Helper()
@@ -371,8 +373,10 @@ func ErrorIsf(t TestingT, err error, target error, msg string, args ...interface
 
 // Errorf asserts that a function returned an error (i.e. not `nil`).
 //
-//	actualObj, err := SomeFunction()
-//	require.Errorf(t, err, "error message %s", "formatted")
+//	  actualObj, err := SomeFunction()
+//	  if require.Errorf(t, err, "error message %s", "formatted") {
+//		   require.Equal(t, expectedErrorf, err)
+//	  }
 func Errorf(t TestingT, err error, msg string, args ...interface{}) {
 	if h, ok := t.(tHelper); ok {
 		h.Helper()
@@ -1741,12 +1745,15 @@ func NotSamef(t TestingT, expected interface{}, actual interface{}, msg string,
 	t.FailNow()
 }
 
-// NotSubset asserts that the specified list(array, slice...) or map does NOT
-// contain all elements given in the specified subset list(array, slice...) or
-// map.
+// NotSubset asserts that the list (array, slice, or map) does NOT contain all
+// elements given in the subset (array, slice, or map).
+// Map elements are key-value pairs unless compared with an array or slice where
+// only the map key is evaluated.
 //
 //	require.NotSubset(t, [1, 3, 4], [1, 2])
 //	require.NotSubset(t, {"x": 1, "y": 2}, {"z": 3})
+//	require.NotSubset(t, [1, 3, 4], {1: "one", 2: "two"})
+//	require.NotSubset(t, {"x": 1, "y": 2}, ["z"])
 func NotSubset(t TestingT, list interface{}, subset interface{}, msgAndArgs ...interface{}) {
 	if h, ok := t.(tHelper); ok {
 		h.Helper()
@@ -1757,12 +1764,15 @@ func NotSubset(t TestingT, list interface{}, subset interface{}, msgAndArgs ...i
 	t.FailNow()
 }
 
-// NotSubsetf asserts that the specified list(array, slice...) or map does NOT
-// contain all elements given in the specified subset list(array, slice...) or
-// map.
+// NotSubsetf asserts that the list (array, slice, or map) does NOT contain all
+// elements given in the subset (array, slice, or map).
+// Map elements are key-value pairs unless compared with an array or slice where
+// only the map key is evaluated.
 //
 //	require.NotSubsetf(t, [1, 3, 4], [1, 2], "error message %s", "formatted")
 //	require.NotSubsetf(t, {"x": 1, "y": 2}, {"z": 3}, "error message %s", "formatted")
+//	require.NotSubsetf(t, [1, 3, 4], {1: "one", 2: "two"}, "error message %s", "formatted")
+//	require.NotSubsetf(t, {"x": 1, "y": 2}, ["z"], "error message %s", "formatted")
 func NotSubsetf(t TestingT, list interface{}, subset interface{}, msg string, args ...interface{}) {
 	if h, ok := t.(tHelper); ok {
 		h.Helper()
@@ -1967,11 +1977,15 @@ func Samef(t TestingT, expected interface{}, actual interface{}, msg string, arg
 	t.FailNow()
 }
 
-// Subset asserts that the specified list(array, slice...) or map contains all
-// elements given in the specified subset list(array, slice...) or map.
+// Subset asserts that the list (array, slice, or map) contains all elements
+// given in the subset (array, slice, or map).
+// Map elements are key-value pairs unless compared with an array or slice where
+// only the map key is evaluated.
 //
 //	require.Subset(t, [1, 2, 3], [1, 2])
 //	require.Subset(t, {"x": 1, "y": 2}, {"x": 1})
+//	require.Subset(t, [1, 2, 3], {1: "one", 2: "two"})
+//	require.Subset(t, {"x": 1, "y": 2}, ["x"])
 func Subset(t TestingT, list interface{}, subset interface{}, msgAndArgs ...interface{}) {
 	if h, ok := t.(tHelper); ok {
 		h.Helper()
@@ -1982,11 +1996,15 @@ func Subset(t TestingT, list interface{}, subset interface{}, msgAndArgs ...inte
 	t.FailNow()
 }
 
-// Subsetf asserts that the specified list(array, slice...) or map contains all
-// elements given in the specified subset list(array, slice...) or map.
+// Subsetf asserts that the list (array, slice, or map) contains all elements
+// given in the subset (array, slice, or map).
+// Map elements are key-value pairs unless compared with an array or slice where
+// only the map key is evaluated.
 //
 //	require.Subsetf(t, [1, 2, 3], [1, 2], "error message %s", "formatted")
 //	require.Subsetf(t, {"x": 1, "y": 2}, {"x": 1}, "error message %s", "formatted")
+//	require.Subsetf(t, [1, 2, 3], {1: "one", 2: "two"}, "error message %s", "formatted")
+//	require.Subsetf(t, {"x": 1, "y": 2}, ["x"], "error message %s", "formatted")
 func Subsetf(t TestingT, list interface{}, subset interface{}, msg string, args ...interface{}) {
 	if h, ok := t.(tHelper); ok {
 		h.Helper()
diff --git a/require/require_forward.go b/require/require_forward.go
index 096904b..b74150f 100644
--- a/require/require_forward.go
+++ b/require/require_forward.go
@@ -225,8 +225,10 @@ func (a *Assertions) Equalf(expected interface{}, actual interface{}, msg string
 
 // Error asserts that a function returned an error (i.e. not `nil`).
 //
-//	actualObj, err := SomeFunction()
-//	a.Error(err)
+//	  actualObj, err := SomeFunction()
+//	  if a.Error(err) {
+//		   assert.Equal(t, expectedError, err)
+//	  }
 func (a *Assertions) Error(err error, msgAndArgs ...interface{}) {
 	if h, ok := a.t.(tHelper); ok {
 		h.Helper()
@@ -296,8 +298,10 @@ func (a *Assertions) ErrorIsf(err error, target error, msg string, args ...inter
 
 // Errorf asserts that a function returned an error (i.e. not `nil`).
 //
-//	actualObj, err := SomeFunction()
-//	a.Errorf(err, "error message %s", "formatted")
+//	  actualObj, err := SomeFunction()
+//	  if a.Errorf(err, "error message %s", "formatted") {
+//		   assert.Equal(t, expectedErrorf, err)
+//	  }
 func (a *Assertions) Errorf(err error, msg string, args ...interface{}) {
 	if h, ok := a.t.(tHelper); ok {
 		h.Helper()
@@ -1375,12 +1379,15 @@ func (a *Assertions) NotSamef(expected interface{}, actual interface{}, msg stri
 	NotSamef(a.t, expected, actual, msg, args...)
 }
 
-// NotSubset asserts that the specified list(array, slice...) or map does NOT
-// contain all elements given in the specified subset list(array, slice...) or
-// map.
+// NotSubset asserts that the list (array, slice, or map) does NOT contain all
+// elements given in the subset (array, slice, or map).
+// Map elements are key-value pairs unless compared with an array or slice where
+// only the map key is evaluated.
 //
 //	a.NotSubset([1, 3, 4], [1, 2])
 //	a.NotSubset({"x": 1, "y": 2}, {"z": 3})
+//	a.NotSubset([1, 3, 4], {1: "one", 2: "two"})
+//	a.NotSubset({"x": 1, "y": 2}, ["z"])
 func (a *Assertions) NotSubset(list interface{}, subset interface{}, msgAndArgs ...interface{}) {
 	if h, ok := a.t.(tHelper); ok {
 		h.Helper()
@@ -1388,12 +1395,15 @@ func (a *Assertions) NotSubset(list interface{}, subset interface{}, msgAndArgs
 	NotSubset(a.t, list, subset, msgAndArgs...)
 }
 
-// NotSubsetf asserts that the specified list(array, slice...) or map does NOT
-// contain all elements given in the specified subset list(array, slice...) or
-// map.
+// NotSubsetf asserts that the list (array, slice, or map) does NOT contain all
+// elements given in the subset (array, slice, or map).
+// Map elements are key-value pairs unless compared with an array or slice where
+// only the map key is evaluated.
 //
 //	a.NotSubsetf([1, 3, 4], [1, 2], "error message %s", "formatted")
 //	a.NotSubsetf({"x": 1, "y": 2}, {"z": 3}, "error message %s", "formatted")
+//	a.NotSubsetf([1, 3, 4], {1: "one", 2: "two"}, "error message %s", "formatted")
+//	a.NotSubsetf({"x": 1, "y": 2}, ["z"], "error message %s", "formatted")
 func (a *Assertions) NotSubsetf(list interface{}, subset interface{}, msg string, args ...interface{}) {
 	if h, ok := a.t.(tHelper); ok {
 		h.Helper()
@@ -1553,11 +1563,15 @@ func (a *Assertions) Samef(expected interface{}, actual interface{}, msg string,
 	Samef(a.t, expected, actual, msg, args...)
 }
 
-// Subset asserts that the specified list(array, slice...) or map contains all
-// elements given in the specified subset list(array, slice...) or map.
+// Subset asserts that the list (array, slice, or map) contains all elements
+// given in the subset (array, slice, or map).
+// Map elements are key-value pairs unless compared with an array or slice where
+// only the map key is evaluated.
 //
 //	a.Subset([1, 2, 3], [1, 2])
 //	a.Subset({"x": 1, "y": 2}, {"x": 1})
+//	a.Subset([1, 2, 3], {1: "one", 2: "two"})
+//	a.Subset({"x": 1, "y": 2}, ["x"])
 func (a *Assertions) Subset(list interface{}, subset interface{}, msgAndArgs ...interface{}) {
 	if h, ok := a.t.(tHelper); ok {
 		h.Helper()
@@ -1565,11 +1579,15 @@ func (a *Assertions) Subset(list interface{}, subset interface{}, msgAndArgs ...
 	Subset(a.t, list, subset, msgAndArgs...)
 }
 
-// Subsetf asserts that the specified list(array, slice...) or map contains all
-// elements given in the specified subset list(array, slice...) or map.
+// Subsetf asserts that the list (array, slice, or map) contains all elements
+// given in the subset (array, slice, or map).
+// Map elements are key-value pairs unless compared with an array or slice where
+// only the map key is evaluated.
 //
 //	a.Subsetf([1, 2, 3], [1, 2], "error message %s", "formatted")
 //	a.Subsetf({"x": 1, "y": 2}, {"x": 1}, "error message %s", "formatted")
+//	a.Subsetf([1, 2, 3], {1: "one", 2: "two"}, "error message %s", "formatted")
+//	a.Subsetf({"x": 1, "y": 2}, ["x"], "error message %s", "formatted")
 func (a *Assertions) Subsetf(list interface{}, subset interface{}, msg string, args ...interface{}) {
 	if h, ok := a.t.(tHelper); ok {
 		h.Helper()


Test patch:
diff --git a/assert/assertions_test.go b/assert/assertions_test.go
index 0efdd53..ec807ca 100644
--- a/assert/assertions_test.go
+++ b/assert/assertions_test.go
@@ -1142,6 +1142,7 @@ func TestSubsetNotSubset(t *testing.T) {
 			"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"]`},
 
 		// cases that are expected not to contain
 		{[]string{"hello", "world"}, []string{"hello", "testify"}, false, `[]string{"hello", "world"} does not contain "testify"`},
@@ -1163,6 +1164,7 @@ func TestSubsetNotSubset(t *testing.T) {
 			"b": "y",
 			"c": "z",
 		}, false, `map[string]string{"a":"x", "b":"y"} does not contain map[string]string{"a":"x", "b":"y", "c":"z"}`},
+		{[]string{"a", "b", "c"}, map[string]int{"c": 3, "d": 4}, false, `[]string{"a", "b", "c"} does not contain "d"`},
 	}
 
 	for _, c := range cases {
@@ -3058,49 +3060,6 @@ func TestEventuallyWithTFailNow(t *testing.T) {
 	Len(t, mockT.errors, 1)
 }
 
-// Check that a long running condition doesn't block Eventually.
-// See issue 805 (and its long tail of following issues)
-func TestEventuallyTimeout(t *testing.T) {
-	mockT := new(testing.T)
-
-	NotPanics(t, func() {
-		done, done2 := make(chan struct{}), make(chan struct{})
-
-		// A condition function that returns after the Eventually timeout
-		condition := func() bool {
-			// Wait until Eventually times out and terminates
-			<-done
-			close(done2)
-			return true
-		}
-
-		False(t, Eventually(mockT, condition, time.Millisecond, time.Microsecond))
-
-		close(done)
-		<-done2
-	})
-}
-
-func TestEventuallySucceedQuickly(t *testing.T) {
-	mockT := new(testing.T)
-
-	condition := func() bool { return true }
-
-	// By making the tick longer than the total duration, we expect that this test would fail if
-	// we didn't check the condition before the first tick elapses.
-	True(t, Eventually(mockT, condition, 100*time.Millisecond, time.Second))
-}
-
-func TestEventuallyWithTSucceedQuickly(t *testing.T) {
-	mockT := new(testing.T)
-
-	condition := func(t *CollectT) {}
-
-	// By making the tick longer than the total duration, we expect that this test would fail if
-	// we didn't check the condition before the first tick elapses.
-	True(t, EventuallyWithT(mockT, condition, 100*time.Millisecond, time.Second))
-}
-
 func TestNeverFalse(t *testing.T) {
 	condition := func() bool {
 		return false
@@ -3128,13 +3087,27 @@ func TestNeverTrue(t *testing.T) {
 	False(t, Never(mockT, condition, 100*time.Millisecond, 20*time.Millisecond))
 }
 
-func TestNeverFailQuickly(t *testing.T) {
+// Check that a long running condition doesn't block Eventually.
+// See issue 805 (and its long tail of following issues)
+func TestEventuallyTimeout(t *testing.T) {
 	mockT := new(testing.T)
 
-	// By making the tick longer than the total duration, we expect that this test would fail if
-	// we didn't check the condition before the first tick elapses.
-	condition := func() bool { return true }
-	False(t, Never(mockT, condition, 100*time.Millisecond, time.Second))
+	NotPanics(t, func() {
+		done, done2 := make(chan struct{}), make(chan struct{})
+
+		// A condition function that returns after the Eventually timeout
+		condition := func() bool {
+			// Wait until Eventually times out and terminates
+			<-done
+			close(done2)
+			return true
+		}
+
+		False(t, Eventually(mockT, condition, time.Millisecond, time.Microsecond))
+
+		close(done)
+		<-done2
+	})
 }
 
 func Test_validateEqualArgs(t *testing.T) {
@@ -3272,10 +3245,13 @@ func TestErrorIs(t *testing.T) {
 				"in chain: \"EOF\"\n",
 		},
 		{
-			err:          nil,
-			target:       io.EOF,
-			result:       false,
-			resultErrMsg: "Expected error with \"EOF\" in chain but got nil.\n",
+			err:    nil,
+			target: io.EOF,
+			result: false,
+			resultErrMsg: "" +
+				"Target error should be in err chain:\n" +
+				"expected: \"EOF\"\n" +
+				"in chain: \n",
 		},
 		{
 			err:    io.EOF,