Repository: stretchr/testify
Pull request #1760 (2025-06-02 16:47:33)

doc: improve godoc of top level 'testify' package
## Summary

Improve package doc of [`github.com/stretchr/testify`](https://pkg.go.dev/github.com/stretchr/testify)

## Motivation

Add godoc links to other packages `assert`, `require`, `mock`, `suite`.



Patch:
diff --git a/assert/assertion_format.go b/assert/assertion_format.go
index 7144041..c592f6a 100644
--- a/assert/assertion_format.go
+++ b/assert/assertion_format.go
@@ -50,10 +50,19 @@ func ElementsMatchf(t TestingT, listA interface{}, listB interface{}, msg string
 	return ElementsMatch(t, listA, listB, append([]interface{}{msg}, args...)...)
 }
 
-// Emptyf asserts that the specified object is empty.  I.e. nil, "", false, 0 or either
-// a slice or a channel with len == 0.
+// Emptyf asserts that the given value is "empty".
+//
+// [Zero values] are "empty".
+//
+// Arrays are "empty" if every element is the zero value of the type (stricter than "empty").
+//
+// Slices, maps and channels with zero length are "empty".
+//
+// Pointer values are "empty" if the pointer is nil or if the pointed value is "empty".
 //
 //	assert.Emptyf(t, obj, "error message %s", "formatted")
+//
+// [Zero values]: https://go.dev/ref/spec#The_zero_value
 func Emptyf(t TestingT, object interface{}, msg string, args ...interface{}) bool {
 	if h, ok := t.(tHelper); ok {
 		h.Helper()
@@ -595,8 +604,7 @@ func NotElementsMatchf(t TestingT, listA interface{}, listB interface{}, msg str
 	return NotElementsMatch(t, listA, listB, append([]interface{}{msg}, args...)...)
 }
 
-// NotEmptyf asserts that the specified object is NOT empty.  I.e. not nil, "", false, 0 or either
-// a slice or a channel with len == 0.
+// NotEmptyf asserts that the specified object is NOT [Empty].
 //
 //	if assert.NotEmptyf(t, obj, "error message %s", "formatted") {
 //	  assert.Equal(t, "two", obj[1])
diff --git a/assert/assertion_forward.go b/assert/assertion_forward.go
index 38e253e..58db928 100644
--- a/assert/assertion_forward.go
+++ b/assert/assertion_forward.go
@@ -92,10 +92,19 @@ func (a *Assertions) ElementsMatchf(listA interface{}, listB interface{}, msg st
 	return ElementsMatchf(a.t, listA, listB, msg, args...)
 }
 
-// Empty asserts that the specified object is empty.  I.e. nil, "", false, 0 or either
-// a slice or a channel with len == 0.
+// Empty asserts that the given value is "empty".
+//
+// [Zero values] are "empty".
+//
+// Arrays are "empty" if every element is the zero value of the type (stricter than "empty").
+//
+// Slices, maps and channels with zero length are "empty".
+//
+// Pointer values are "empty" if the pointer is nil or if the pointed value is "empty".
 //
 //	a.Empty(obj)
+//
+// [Zero values]: https://go.dev/ref/spec#The_zero_value
 func (a *Assertions) Empty(object interface{}, msgAndArgs ...interface{}) bool {
 	if h, ok := a.t.(tHelper); ok {
 		h.Helper()
@@ -103,10 +112,19 @@ func (a *Assertions) Empty(object interface{}, msgAndArgs ...interface{}) bool {
 	return Empty(a.t, object, msgAndArgs...)
 }
 
-// Emptyf asserts that the specified object is empty.  I.e. nil, "", false, 0 or either
-// a slice or a channel with len == 0.
+// Emptyf asserts that the given value is "empty".
+//
+// [Zero values] are "empty".
+//
+// Arrays are "empty" if every element is the zero value of the type (stricter than "empty").
+//
+// Slices, maps and channels with zero length are "empty".
+//
+// Pointer values are "empty" if the pointer is nil or if the pointed value is "empty".
 //
 //	a.Emptyf(obj, "error message %s", "formatted")
+//
+// [Zero values]: https://go.dev/ref/spec#The_zero_value
 func (a *Assertions) Emptyf(object interface{}, msg string, args ...interface{}) bool {
 	if h, ok := a.t.(tHelper); ok {
 		h.Helper()
@@ -1182,8 +1200,7 @@ func (a *Assertions) NotElementsMatchf(listA interface{}, listB interface{}, msg
 	return NotElementsMatchf(a.t, listA, listB, msg, args...)
 }
 
-// NotEmpty asserts that the specified object is NOT empty.  I.e. not nil, "", false, 0 or either
-// a slice or a channel with len == 0.
+// NotEmpty asserts that the specified object is NOT [Empty].
 //
 //	if a.NotEmpty(obj) {
 //	  assert.Equal(t, "two", obj[1])
@@ -1195,8 +1212,7 @@ func (a *Assertions) NotEmpty(object interface{}, msgAndArgs ...interface{}) boo
 	return NotEmpty(a.t, object, msgAndArgs...)
 }
 
-// NotEmptyf asserts that the specified object is NOT empty.  I.e. not nil, "", false, 0 or either
-// a slice or a channel with len == 0.
+// NotEmptyf asserts that the specified object is NOT [Empty].
 //
 //	if a.NotEmptyf(obj, "error message %s", "formatted") {
 //	  assert.Equal(t, "two", obj[1])
diff --git a/assert/assertions.go b/assert/assertions.go
index 9913ac2..4658206 100644
--- a/assert/assertions.go
+++ b/assert/assertions.go
@@ -773,10 +773,19 @@ func isEmpty(object interface{}) bool {
 	}
 }
 
-// Empty asserts that the specified object is empty.  I.e. nil, "", false, 0 or either
-// a slice or a channel with len == 0.
+// Empty asserts that the given value is "empty".
+//
+// [Zero values] are "empty".
+//
+// Arrays are "empty" if every element is the zero value of the type (stricter than "empty").
+//
+// Slices, maps and channels with zero length are "empty".
+//
+// Pointer values are "empty" if the pointer is nil or if the pointed value is "empty".
 //
 //	assert.Empty(t, obj)
+//
+// [Zero values]: https://go.dev/ref/spec#The_zero_value
 func Empty(t TestingT, object interface{}, msgAndArgs ...interface{}) bool {
 	pass := isEmpty(object)
 	if !pass {
@@ -789,8 +798,7 @@ func Empty(t TestingT, object interface{}, msgAndArgs ...interface{}) bool {
 	return pass
 }
 
-// NotEmpty asserts that the specified object is NOT empty.  I.e. not nil, "", false, 0 or either
-// a slice or a channel with len == 0.
+// NotEmpty asserts that the specified object is NOT [Empty].
 //
 //	if assert.NotEmpty(t, obj) {
 //	  assert.Equal(t, "two", obj[1])
diff --git a/doc.go b/doc.go
index babc122..2076714 100644
--- a/doc.go
+++ b/doc.go
@@ -1,15 +1,17 @@
-// Package testify is a set of packages that provide many tools for testifying that your code will behave as you intend.
+// Module testify is a set of packages that provide many tools for testifying that your code will behave as you intend.
 //
-// testify contains the following packages:
+// Testify contains the following packages:
 //
-// The assert package provides a comprehensive set of assertion functions that tie in to the Go testing system.
+// The [github.com/stretchr/testify/assert] package provides a comprehensive set of assertion functions that tie in to [the Go testing system].
+// The [github.com/stretchr/testify/require] package provides the same assertions but as fatal checks.
 //
-// The mock package provides a system by which it is possible to mock your objects and verify calls are happening as expected.
+// The [github.com/stretchr/testify/mock] package provides a system by which it is possible to mock your objects and verify calls are happening as expected.
 //
-// The suite package provides a basic structure for using structs as testing suites, and methods on those structs as tests.  It includes setup/teardown functionality in the way of interfaces.
+// The [github.com/stretchr/testify/suite] package provides a basic structure for using structs as testing suites, and methods on those structs as tests.  It includes setup/teardown functionality in the way of interfaces.
 //
 // A [golangci-lint] compatible linter for testify is available called [testifylint].
 //
+// [the Go testing system]: https://go.dev/doc/code#Testing
 // [golangci-lint]: https://golangci-lint.run/
 // [testifylint]: https://github.com/Antonboom/testifylint
 package testify
diff --git a/require/require.go b/require/require.go
index 6cd1334..2d02f9b 100644
--- a/require/require.go
+++ b/require/require.go
@@ -117,10 +117,19 @@ func ElementsMatchf(t TestingT, listA interface{}, listB interface{}, msg string
 	t.FailNow()
 }
 
-// Empty asserts that the specified object is empty.  I.e. nil, "", false, 0 or either
-// a slice or a channel with len == 0.
+// Empty asserts that the given value is "empty".
+//
+// [Zero values] are "empty".
+//
+// Arrays are "empty" if every element is the zero value of the type (stricter than "empty").
+//
+// Slices, maps and channels with zero length are "empty".
+//
+// Pointer values are "empty" if the pointer is nil or if the pointed value is "empty".
 //
 //	require.Empty(t, obj)
+//
+// [Zero values]: https://go.dev/ref/spec#The_zero_value
 func Empty(t TestingT, object interface{}, msgAndArgs ...interface{}) {
 	if h, ok := t.(tHelper); ok {
 		h.Helper()
@@ -131,10 +140,19 @@ func Empty(t TestingT, object interface{}, msgAndArgs ...interface{}) {
 	t.FailNow()
 }
 
-// Emptyf asserts that the specified object is empty.  I.e. nil, "", false, 0 or either
-// a slice or a channel with len == 0.
+// Emptyf asserts that the given value is "empty".
+//
+// [Zero values] are "empty".
+//
+// Arrays are "empty" if every element is the zero value of the type (stricter than "empty").
+//
+// Slices, maps and channels with zero length are "empty".
+//
+// Pointer values are "empty" if the pointer is nil or if the pointed value is "empty".
 //
 //	require.Emptyf(t, obj, "error message %s", "formatted")
+//
+// [Zero values]: https://go.dev/ref/spec#The_zero_value
 func Emptyf(t TestingT, object interface{}, msg string, args ...interface{}) {
 	if h, ok := t.(tHelper); ok {
 		h.Helper()
@@ -1495,8 +1513,7 @@ func NotElementsMatchf(t TestingT, listA interface{}, listB interface{}, msg str
 	t.FailNow()
 }
 
-// NotEmpty asserts that the specified object is NOT empty.  I.e. not nil, "", false, 0 or either
-// a slice or a channel with len == 0.
+// NotEmpty asserts that the specified object is NOT [Empty].
 //
 //	if require.NotEmpty(t, obj) {
 //	  require.Equal(t, "two", obj[1])
@@ -1511,8 +1528,7 @@ func NotEmpty(t TestingT, object interface{}, msgAndArgs ...interface{}) {
 	t.FailNow()
 }
 
-// NotEmptyf asserts that the specified object is NOT empty.  I.e. not nil, "", false, 0 or either
-// a slice or a channel with len == 0.
+// NotEmptyf asserts that the specified object is NOT [Empty].
 //
 //	if require.NotEmptyf(t, obj, "error message %s", "formatted") {
 //	  require.Equal(t, "two", obj[1])
diff --git a/require/require_forward.go b/require/require_forward.go
index f6192df..e6f7e94 100644
--- a/require/require_forward.go
+++ b/require/require_forward.go
@@ -93,10 +93,19 @@ func (a *Assertions) ElementsMatchf(listA interface{}, listB interface{}, msg st
 	ElementsMatchf(a.t, listA, listB, msg, args...)
 }
 
-// Empty asserts that the specified object is empty.  I.e. nil, "", false, 0 or either
-// a slice or a channel with len == 0.
+// Empty asserts that the given value is "empty".
+//
+// [Zero values] are "empty".
+//
+// Arrays are "empty" if every element is the zero value of the type (stricter than "empty").
+//
+// Slices, maps and channels with zero length are "empty".
+//
+// Pointer values are "empty" if the pointer is nil or if the pointed value is "empty".
 //
 //	a.Empty(obj)
+//
+// [Zero values]: https://go.dev/ref/spec#The_zero_value
 func (a *Assertions) Empty(object interface{}, msgAndArgs ...interface{}) {
 	if h, ok := a.t.(tHelper); ok {
 		h.Helper()
@@ -104,10 +113,19 @@ func (a *Assertions) Empty(object interface{}, msgAndArgs ...interface{}) {
 	Empty(a.t, object, msgAndArgs...)
 }
 
-// Emptyf asserts that the specified object is empty.  I.e. nil, "", false, 0 or either
-// a slice or a channel with len == 0.
+// Emptyf asserts that the given value is "empty".
+//
+// [Zero values] are "empty".
+//
+// Arrays are "empty" if every element is the zero value of the type (stricter than "empty").
+//
+// Slices, maps and channels with zero length are "empty".
+//
+// Pointer values are "empty" if the pointer is nil or if the pointed value is "empty".
 //
 //	a.Emptyf(obj, "error message %s", "formatted")
+//
+// [Zero values]: https://go.dev/ref/spec#The_zero_value
 func (a *Assertions) Emptyf(object interface{}, msg string, args ...interface{}) {
 	if h, ok := a.t.(tHelper); ok {
 		h.Helper()
@@ -1183,8 +1201,7 @@ func (a *Assertions) NotElementsMatchf(listA interface{}, listB interface{}, msg
 	NotElementsMatchf(a.t, listA, listB, msg, args...)
 }
 
-// NotEmpty asserts that the specified object is NOT empty.  I.e. not nil, "", false, 0 or either
-// a slice or a channel with len == 0.
+// NotEmpty asserts that the specified object is NOT [Empty].
 //
 //	if a.NotEmpty(obj) {
 //	  assert.Equal(t, "two", obj[1])
@@ -1196,8 +1213,7 @@ func (a *Assertions) NotEmpty(object interface{}, msgAndArgs ...interface{}) {
 	NotEmpty(a.t, object, msgAndArgs...)
 }
 
-// NotEmptyf asserts that the specified object is NOT empty.  I.e. not nil, "", false, 0 or either
-// a slice or a channel with len == 0.
+// NotEmptyf asserts that the specified object is NOT [Empty].
 //
 //	if a.NotEmptyf(obj, "error message %s", "formatted") {
 //	  assert.Equal(t, "two", obj[1])


Test patch:
diff --git a/assert/assertions_test.go b/assert/assertions_test.go
index 955188d..d30eef6 100644
--- a/assert/assertions_test.go
+++ b/assert/assertions_test.go
@@ -1728,24 +1728,119 @@ func Test_isEmpty(t *testing.T) {
 
 	True(t, isEmpty(""))
 	True(t, isEmpty(nil))
+	True(t, isEmpty(error(nil)))
+	True(t, isEmpty((*int)(nil)))
+	True(t, isEmpty((*string)(nil)))
+	True(t, isEmpty(new(string)))
 	True(t, isEmpty([]string{}))
+	True(t, isEmpty([]string(nil)))
+	True(t, isEmpty([]byte(nil)))
+	True(t, isEmpty([]byte{}))
+	True(t, isEmpty([]byte("")))
+	True(t, isEmpty([]bool(nil)))
+	True(t, isEmpty([]bool{}))
+	True(t, isEmpty([]interface{}(nil)))
+	True(t, isEmpty([]interface{}{}))
+	True(t, isEmpty(struct{}{}))
+	True(t, isEmpty(&struct{}{}))
+	True(t, isEmpty(struct{ A int }{A: 0}))
+	True(t, isEmpty(struct{ a int }{a: 0}))
+	True(t, isEmpty(struct {
+		a int
+		B int
+	}{a: 0, B: 0}))
 	True(t, isEmpty(0))
+	True(t, isEmpty(int(0)))
+	True(t, isEmpty(int8(0)))
+	True(t, isEmpty(int16(0)))
+	True(t, isEmpty(uint16(0)))
 	True(t, isEmpty(int32(0)))
+	True(t, isEmpty(uint32(0)))
 	True(t, isEmpty(int64(0)))
+	True(t, isEmpty(uint64(0)))
+	True(t, isEmpty('\u0000')) // rune => int32
+	True(t, isEmpty(float32(0)))
+	True(t, isEmpty(float64(0)))
+	True(t, isEmpty(0i))   // complex
+	True(t, isEmpty(0.0i)) // complex
 	True(t, isEmpty(false))
+	True(t, isEmpty(new(bool)))
 	True(t, isEmpty(map[string]string{}))
+	True(t, isEmpty(map[string]string(nil)))
 	True(t, isEmpty(new(time.Time)))
 	True(t, isEmpty(time.Time{}))
 	True(t, isEmpty(make(chan struct{})))
-	True(t, isEmpty([1]int{}))
+	True(t, isEmpty(chan struct{}(nil)))
+	True(t, isEmpty(chan<- struct{}(nil)))
+	True(t, isEmpty(make(chan struct{})))
+	True(t, isEmpty(make(chan<- struct{})))
+	True(t, isEmpty(make(chan struct{}, 1)))
+	True(t, isEmpty(make(chan<- struct{}, 1)))
+	True(t, isEmpty([1]int{0}))
+	True(t, isEmpty([2]int{0, 0}))
+	True(t, isEmpty([8]int{}))
+	True(t, isEmpty([...]int{7: 0}))
+	True(t, isEmpty([...]bool{false, false}))
+	True(t, isEmpty(errors.New(""))) // BEWARE
+	True(t, isEmpty([]error{}))
+	True(t, isEmpty([]error(nil)))
+	True(t, isEmpty(&[1]int{0}))
+	True(t, isEmpty(&[2]int{0, 0}))
 	False(t, isEmpty("something"))
 	False(t, isEmpty(errors.New("something")))
 	False(t, isEmpty([]string{"something"}))
 	False(t, isEmpty(1))
+	False(t, isEmpty(int(1)))
+	False(t, isEmpty(uint(1)))
+	False(t, isEmpty(byte(1)))
+	False(t, isEmpty(int8(1)))
+	False(t, isEmpty(uint8(1)))
+	False(t, isEmpty(int16(1)))
+	False(t, isEmpty(uint16(1)))
+	False(t, isEmpty(int32(1)))
+	False(t, isEmpty(uint32(1)))
+	False(t, isEmpty(int64(1)))
+	False(t, isEmpty(uint64(1)))
+	False(t, isEmpty('A')) // rune => int32
 	False(t, isEmpty(true))
+	False(t, isEmpty(1.0))
+	False(t, isEmpty(1i))            // complex
+	False(t, isEmpty([]byte{0}))     // elements values are ignored for slices
+	False(t, isEmpty([]byte{0, 0}))  // elements values are ignored for slices
+	False(t, isEmpty([]string{""}))  // elements values are ignored for slices
+	False(t, isEmpty([]string{"a"})) // elements values are ignored for slices
+	False(t, isEmpty([]bool{false})) // elements values are ignored for slices
+	False(t, isEmpty([]bool{true}))  // elements values are ignored for slices
+	False(t, isEmpty([]error{errors.New("xxx")}))
+	False(t, isEmpty([]error{nil}))            // BEWARE
+	False(t, isEmpty([]error{errors.New("")})) // BEWARE
 	False(t, isEmpty(map[string]string{"Hello": "World"}))
+	False(t, isEmpty(map[string]string{"": ""}))
+	False(t, isEmpty(map[string]string{"foo": ""}))
+	False(t, isEmpty(map[string]string{"": "foo"}))
 	False(t, isEmpty(chWithValue))
+	False(t, isEmpty([1]bool{true}))
+	False(t, isEmpty([2]bool{false, true}))
+	False(t, isEmpty([...]bool{10: true}))
+	False(t, isEmpty([]int{0}))
+	False(t, isEmpty([]int{42}))
 	False(t, isEmpty([1]int{42}))
+	False(t, isEmpty([2]int{0, 42}))
+	False(t, isEmpty(&[1]int{42}))
+	False(t, isEmpty(&[2]int{0, 42}))
+	False(t, isEmpty([1]*int{new(int)})) // array elements must be the zero value, not any Empty value
+	False(t, isEmpty(struct{ A int }{A: 42}))
+	False(t, isEmpty(struct{ a int }{a: 42}))
+	False(t, isEmpty(struct{ a *int }{a: new(int)})) // fields must be the zero value, not any Empty value
+	False(t, isEmpty(struct{ a []int }{a: []int{}})) // fields must be the zero value, not any Empty value
+	False(t, isEmpty(struct {
+		a int
+		B int
+	}{a: 0, B: 42}))
+	False(t, isEmpty(struct {
+		a int
+		B int
+	}{a: 42, B: 0}))
 }
 
 func TestEmpty(t *testing.T) {