diff --git a/assert/assert_assertions.go b/assert/assert_assertions.go index bd58c8569..496049885 100644 --- a/assert/assert_assertions.go +++ b/assert/assert_assertions.go @@ -1739,6 +1739,30 @@ func MapContainsT[Map ~map[K]V, K comparable, V any](t T, m Map, key K, msgAndAr return assertions.MapContainsT[Map, K, V](t, m, key, msgAndArgs...) } +// MapEqualT asserts that 2 maps of comparable elements are equal, +// that is have the same length and contain the keys with the same elements. +// +// See also [maps.Equal]. +// +// # Usage +// +// assertions.MapEqualT(t, map[string]string{"1": "Hello", "2": "World"}, map[string]string{"1": "Hello", "2": "World"}) +// +// # Examples +// +// success: map[string]string{"1": "Hello", "2": "World"}, map[string]string{"1": "Hello", "2": "World"} +// failure: map[string]string{"2": "Hello", "1": "World"}, map[string]string{"1": "Hello", "2": "World"} +// +// Upon failure, the test [T] is marked as failed and continues execution. +// +// [comparable-types]: https://go.dev/blog/comparable +func MapEqualT[K, V comparable](t T, listA map[K]V, listB map[K]V, msgAndArgs ...any) (ok bool) { + if h, ok := t.(H); ok { + h.Helper() + } + return assertions.MapEqualT[K, V](t, listA, listB, msgAndArgs...) +} + // MapNotContainsT asserts that the specified map does not contain a key. // // # Usage @@ -1758,6 +1782,29 @@ func MapNotContainsT[Map ~map[K]V, K comparable, V any](t T, m Map, key K, msgAn return assertions.MapNotContainsT[Map, K, V](t, m, key, msgAndArgs...) } +// MapNotEqualT asserts that 2 maps of comparable elements are not equal. +// +// See also [MapEqualT]. +// +// # Usage +// +// assertions.MapNotEqualT(t, map[string]string{"2": "Hello", "1": "World"}, map[string]string{"1": "Hello", "2": "World"}) +// +// # Examples +// +// success: map[string]string{"2": "Hello", "1": "World"}, map[string]string{"1": "Hello", "2": "World"} +// failure: map[string]string{"1": "Hello", "2": "World"}, map[string]string{"1": "Hello", "2": "World"} +// +// Upon failure, the test [T] is marked as failed and continues execution. +// +// [comparable-types]: https://go.dev/blog/comparable +func MapNotEqualT[K, V comparable](t T, listA map[K]V, listB map[K]V, msgAndArgs ...any) (ok bool) { + if h, ok := t.(H); ok { + h.Helper() + } + return assertions.MapNotEqualT[K, V](t, listA, listB, msgAndArgs...) +} + // Negative asserts that the specified element is strictly negative. // // # Usage @@ -2648,6 +2695,30 @@ func SliceContainsT[Slice ~[]E, E comparable](t T, s Slice, element E, msgAndArg return assertions.SliceContainsT[Slice, E](t, s, element, msgAndArgs...) } +// SliceEqualT asserts that 2 slices of comparable elements are equal, +// that is have the same length and contain the same elements in the same order. +// +// See also [slices.Equal]. +// +// # Usage +// +// assertions.SliceEqualT(t, []string{"Hello","World"}, []string{"Hello","World"}) +// +// # Examples +// +// success: []string{"Hello","World"}, []string{"Hello","World"} +// failure: []string{"Hello","World"}, []string{"Hello"} +// +// Upon failure, the test [T] is marked as failed and continues execution. +// +// [comparable-types]: https://go.dev/blog/comparable +func SliceEqualT[E comparable](t T, listA []E, listB []E, msgAndArgs ...any) (ok bool) { + if h, ok := t.(H); ok { + h.Helper() + } + return assertions.SliceEqualT[E](t, listA, listB, msgAndArgs...) +} + // SliceNotContainsT asserts that the specified slice does not contain a comparable element. // // See [SliceContainsT]. @@ -2669,6 +2740,29 @@ func SliceNotContainsT[Slice ~[]E, E comparable](t T, s Slice, element E, msgAnd return assertions.SliceNotContainsT[Slice, E](t, s, element, msgAndArgs...) } +// SliceNotEqualT asserts that 2 slices of comparable elements are not equal. +// +// See also [SliceEqualT]. +// +// # Usage +// +// assertions.SliceNotEqualT(t, []string{"Hello","World"}, []string{"Hello"}) +// +// # Examples +// +// success: []string{"Hello","World"}, []string{"Hello"} +// failure: []string{"Hello","World"}, []string{"Hello","World"} +// +// Upon failure, the test [T] is marked as failed and continues execution. +// +// [comparable-types]: https://go.dev/blog/comparable +func SliceNotEqualT[E comparable](t T, listA []E, listB []E, msgAndArgs ...any) (ok bool) { + if h, ok := t.(H); ok { + h.Helper() + } + return assertions.SliceNotEqualT[E](t, listA, listB, msgAndArgs...) +} + // SliceNotSubsetT asserts that a slice of comparable elements does not contain all the elements given in the subset. // // # Usage diff --git a/assert/assert_assertions_test.go b/assert/assert_assertions_test.go index dc47056c6..e7ba42dc1 100644 --- a/assert/assert_assertions_test.go +++ b/assert/assert_assertions_test.go @@ -1887,6 +1887,33 @@ func TestMapContainsT(t *testing.T) { }) } +func TestMapEqualT(t *testing.T) { + t.Parallel() + + t.Run("success", func(t *testing.T) { + t.Parallel() + + mock := new(mockT) + result := MapEqualT(mock, map[string]string{"1": "Hello", "2": "World"}, map[string]string{"1": "Hello", "2": "World"}) + if !result { + t.Error("MapEqualT should return true on success") + } + }) + + t.Run("failure", func(t *testing.T) { + t.Parallel() + + mock := new(mockT) + result := MapEqualT(mock, map[string]string{"2": "Hello", "1": "World"}, map[string]string{"1": "Hello", "2": "World"}) + if result { + t.Error("MapEqualT should return false on failure") + } + if !mock.failed { + t.Error("MapEqualT should mark test as failed") + } + }) +} + func TestMapNotContainsT(t *testing.T) { t.Parallel() @@ -1914,6 +1941,33 @@ func TestMapNotContainsT(t *testing.T) { }) } +func TestMapNotEqualT(t *testing.T) { + t.Parallel() + + t.Run("success", func(t *testing.T) { + t.Parallel() + + mock := new(mockT) + result := MapNotEqualT(mock, map[string]string{"2": "Hello", "1": "World"}, map[string]string{"1": "Hello", "2": "World"}) + if !result { + t.Error("MapNotEqualT should return true on success") + } + }) + + t.Run("failure", func(t *testing.T) { + t.Parallel() + + mock := new(mockT) + result := MapNotEqualT(mock, map[string]string{"1": "Hello", "2": "World"}, map[string]string{"1": "Hello", "2": "World"}) + if result { + t.Error("MapNotEqualT should return false on failure") + } + if !mock.failed { + t.Error("MapNotEqualT should mark test as failed") + } + }) +} + func TestNegative(t *testing.T) { t.Parallel() @@ -2941,6 +2995,33 @@ func TestSliceContainsT(t *testing.T) { }) } +func TestSliceEqualT(t *testing.T) { + t.Parallel() + + t.Run("success", func(t *testing.T) { + t.Parallel() + + mock := new(mockT) + result := SliceEqualT(mock, []string{"Hello", "World"}, []string{"Hello", "World"}) + if !result { + t.Error("SliceEqualT should return true on success") + } + }) + + t.Run("failure", func(t *testing.T) { + t.Parallel() + + mock := new(mockT) + result := SliceEqualT(mock, []string{"Hello", "World"}, []string{"Hello"}) + if result { + t.Error("SliceEqualT should return false on failure") + } + if !mock.failed { + t.Error("SliceEqualT should mark test as failed") + } + }) +} + func TestSliceNotContainsT(t *testing.T) { t.Parallel() @@ -2968,6 +3049,33 @@ func TestSliceNotContainsT(t *testing.T) { }) } +func TestSliceNotEqualT(t *testing.T) { + t.Parallel() + + t.Run("success", func(t *testing.T) { + t.Parallel() + + mock := new(mockT) + result := SliceNotEqualT(mock, []string{"Hello", "World"}, []string{"Hello"}) + if !result { + t.Error("SliceNotEqualT should return true on success") + } + }) + + t.Run("failure", func(t *testing.T) { + t.Parallel() + + mock := new(mockT) + result := SliceNotEqualT(mock, []string{"Hello", "World"}, []string{"Hello", "World"}) + if result { + t.Error("SliceNotEqualT should return false on failure") + } + if !mock.failed { + t.Error("SliceNotEqualT should mark test as failed") + } + }) +} + func TestSliceNotSubsetT(t *testing.T) { t.Parallel() diff --git a/assert/assert_examples_test.go b/assert/assert_examples_test.go index 83d8f0a2f..9c6941b32 100644 --- a/assert/assert_examples_test.go +++ b/assert/assert_examples_test.go @@ -580,6 +580,14 @@ func ExampleMapContainsT() { // Output: success: true } +func ExampleMapEqualT() { + t := new(testing.T) // should come from testing, e.g. func TestMapEqualT(t *testing.T) + success := assert.MapEqualT(t, map[string]string{"1": "Hello", "2": "World"}, map[string]string{"1": "Hello", "2": "World"}) + fmt.Printf("success: %t\n", success) + + // Output: success: true +} + func ExampleMapNotContainsT() { t := new(testing.T) // should come from testing, e.g. func TestMapNotContainsT(t *testing.T) success := assert.MapNotContainsT(t, map[string]string{"A": "B"}, "C") @@ -588,6 +596,14 @@ func ExampleMapNotContainsT() { // Output: success: true } +func ExampleMapNotEqualT() { + t := new(testing.T) // should come from testing, e.g. func TestMapNotEqualT(t *testing.T) + success := assert.MapNotEqualT(t, map[string]string{"2": "Hello", "1": "World"}, map[string]string{"1": "Hello", "2": "World"}) + fmt.Printf("success: %t\n", success) + + // Output: success: true +} + func ExampleNegative() { t := new(testing.T) // should come from testing, e.g. func TestNegative(t *testing.T) success := assert.Negative(t, -1) @@ -919,6 +935,14 @@ func ExampleSliceContainsT() { // Output: success: true } +func ExampleSliceEqualT() { + t := new(testing.T) // should come from testing, e.g. func TestSliceEqualT(t *testing.T) + success := assert.SliceEqualT(t, []string{"Hello", "World"}, []string{"Hello", "World"}) + fmt.Printf("success: %t\n", success) + + // Output: success: true +} + func ExampleSliceNotContainsT() { t := new(testing.T) // should come from testing, e.g. func TestSliceNotContainsT(t *testing.T) success := assert.SliceNotContainsT(t, []string{"A", "B"}, "C") @@ -927,6 +951,14 @@ func ExampleSliceNotContainsT() { // Output: success: true } +func ExampleSliceNotEqualT() { + t := new(testing.T) // should come from testing, e.g. func TestSliceNotEqualT(t *testing.T) + success := assert.SliceNotEqualT(t, []string{"Hello", "World"}, []string{"Hello"}) + fmt.Printf("success: %t\n", success) + + // Output: success: true +} + func ExampleSliceNotSubsetT() { t := new(testing.T) // should come from testing, e.g. func TestSliceNotSubsetT(t *testing.T) success := assert.SliceNotSubsetT(t, []int{1, 2, 3}, []int{4, 5}) diff --git a/assert/assert_format.go b/assert/assert_format.go index 7104d2205..ada936e51 100644 --- a/assert/assert_format.go +++ b/assert/assert_format.go @@ -715,6 +715,16 @@ func MapContainsTf[Map ~map[K]V, K comparable, V any](t T, m Map, key K, msg str return assertions.MapContainsT[Map, K, V](t, m, key, forwardArgs(msg, args)) } +// MapEqualTf is the same as [MapEqualT], but it accepts a format string to format arguments like [fmt.Printf]. +// +// Upon failure, the test [T] is marked as failed and continues execution. +func MapEqualTf[K, V comparable](t T, listA map[K]V, listB map[K]V, msg string, args ...any) (ok bool) { + if h, ok := t.(H); ok { + h.Helper() + } + return assertions.MapEqualT[K, V](t, listA, listB, forwardArgs(msg, args)) +} + // MapNotContainsTf is the same as [MapNotContainsT], but it accepts a format string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. @@ -725,6 +735,16 @@ func MapNotContainsTf[Map ~map[K]V, K comparable, V any](t T, m Map, key K, msg return assertions.MapNotContainsT[Map, K, V](t, m, key, forwardArgs(msg, args)) } +// MapNotEqualTf is the same as [MapNotEqualT], but it accepts a format string to format arguments like [fmt.Printf]. +// +// Upon failure, the test [T] is marked as failed and continues execution. +func MapNotEqualTf[K, V comparable](t T, listA map[K]V, listB map[K]V, msg string, args ...any) (ok bool) { + if h, ok := t.(H); ok { + h.Helper() + } + return assertions.MapNotEqualT[K, V](t, listA, listB, forwardArgs(msg, args)) +} + // Negativef is the same as [Negative], but it accepts a format string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. @@ -1115,6 +1135,16 @@ func SliceContainsTf[Slice ~[]E, E comparable](t T, s Slice, element E, msg stri return assertions.SliceContainsT[Slice, E](t, s, element, forwardArgs(msg, args)) } +// SliceEqualTf is the same as [SliceEqualT], but it accepts a format string to format arguments like [fmt.Printf]. +// +// Upon failure, the test [T] is marked as failed and continues execution. +func SliceEqualTf[E comparable](t T, listA []E, listB []E, msg string, args ...any) (ok bool) { + if h, ok := t.(H); ok { + h.Helper() + } + return assertions.SliceEqualT[E](t, listA, listB, forwardArgs(msg, args)) +} + // SliceNotContainsTf is the same as [SliceNotContainsT], but it accepts a format string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. @@ -1125,6 +1155,16 @@ func SliceNotContainsTf[Slice ~[]E, E comparable](t T, s Slice, element E, msg s return assertions.SliceNotContainsT[Slice, E](t, s, element, forwardArgs(msg, args)) } +// SliceNotEqualTf is the same as [SliceNotEqualT], but it accepts a format string to format arguments like [fmt.Printf]. +// +// Upon failure, the test [T] is marked as failed and continues execution. +func SliceNotEqualTf[E comparable](t T, listA []E, listB []E, msg string, args ...any) (ok bool) { + if h, ok := t.(H); ok { + h.Helper() + } + return assertions.SliceNotEqualT[E](t, listA, listB, forwardArgs(msg, args)) +} + // SliceNotSubsetTf is the same as [SliceNotSubsetT], but it accepts a format string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. diff --git a/assert/assert_format_test.go b/assert/assert_format_test.go index c5a1cecff..20519ed4d 100644 --- a/assert/assert_format_test.go +++ b/assert/assert_format_test.go @@ -1887,6 +1887,33 @@ func TestMapContainsTf(t *testing.T) { }) } +func TestMapEqualTf(t *testing.T) { + t.Parallel() + + t.Run("success", func(t *testing.T) { + t.Parallel() + + mock := new(mockT) + result := MapEqualTf(mock, map[string]string{"1": "Hello", "2": "World"}, map[string]string{"1": "Hello", "2": "World"}, "test message") + if !result { + t.Error("MapEqualTf should return true on success") + } + }) + + t.Run("failure", func(t *testing.T) { + t.Parallel() + + mock := new(mockT) + result := MapEqualTf(mock, map[string]string{"2": "Hello", "1": "World"}, map[string]string{"1": "Hello", "2": "World"}, "test message") + if result { + t.Error("MapEqualTf should return false on failure") + } + if !mock.failed { + t.Error("MapEqualTf should mark test as failed") + } + }) +} + func TestMapNotContainsTf(t *testing.T) { t.Parallel() @@ -1914,6 +1941,33 @@ func TestMapNotContainsTf(t *testing.T) { }) } +func TestMapNotEqualTf(t *testing.T) { + t.Parallel() + + t.Run("success", func(t *testing.T) { + t.Parallel() + + mock := new(mockT) + result := MapNotEqualTf(mock, map[string]string{"2": "Hello", "1": "World"}, map[string]string{"1": "Hello", "2": "World"}, "test message") + if !result { + t.Error("MapNotEqualTf should return true on success") + } + }) + + t.Run("failure", func(t *testing.T) { + t.Parallel() + + mock := new(mockT) + result := MapNotEqualTf(mock, map[string]string{"1": "Hello", "2": "World"}, map[string]string{"1": "Hello", "2": "World"}, "test message") + if result { + t.Error("MapNotEqualTf should return false on failure") + } + if !mock.failed { + t.Error("MapNotEqualTf should mark test as failed") + } + }) +} + func TestNegativef(t *testing.T) { t.Parallel() @@ -2941,6 +2995,33 @@ func TestSliceContainsTf(t *testing.T) { }) } +func TestSliceEqualTf(t *testing.T) { + t.Parallel() + + t.Run("success", func(t *testing.T) { + t.Parallel() + + mock := new(mockT) + result := SliceEqualTf(mock, []string{"Hello", "World"}, []string{"Hello", "World"}, "test message") + if !result { + t.Error("SliceEqualTf should return true on success") + } + }) + + t.Run("failure", func(t *testing.T) { + t.Parallel() + + mock := new(mockT) + result := SliceEqualTf(mock, []string{"Hello", "World"}, []string{"Hello"}, "test message") + if result { + t.Error("SliceEqualTf should return false on failure") + } + if !mock.failed { + t.Error("SliceEqualTf should mark test as failed") + } + }) +} + func TestSliceNotContainsTf(t *testing.T) { t.Parallel() @@ -2968,6 +3049,33 @@ func TestSliceNotContainsTf(t *testing.T) { }) } +func TestSliceNotEqualTf(t *testing.T) { + t.Parallel() + + t.Run("success", func(t *testing.T) { + t.Parallel() + + mock := new(mockT) + result := SliceNotEqualTf(mock, []string{"Hello", "World"}, []string{"Hello"}, "test message") + if !result { + t.Error("SliceNotEqualTf should return true on success") + } + }) + + t.Run("failure", func(t *testing.T) { + t.Parallel() + + mock := new(mockT) + result := SliceNotEqualTf(mock, []string{"Hello", "World"}, []string{"Hello", "World"}, "test message") + if result { + t.Error("SliceNotEqualTf should return false on failure") + } + if !mock.failed { + t.Error("SliceNotEqualTf should mark test as failed") + } + }) +} + func TestSliceNotSubsetTf(t *testing.T) { t.Parallel() diff --git a/docs/doc-site/api/_index.md b/docs/doc-site/api/_index.md index e2b6de88e..488ebca1c 100644 --- a/docs/doc-site/api/_index.md +++ b/docs/doc-site/api/_index.md @@ -32,7 +32,7 @@ Each domain contains assertions regrouped by their use case (e.g. http, json, er --- - [Boolean](./boolean.md) - Asserting Boolean Values (4) -- [Collection](./collection.md) - Asserting Slices And Maps (19) +- [Collection](./collection.md) - Asserting Slices And Maps (23) - [Comparison](./comparison.md) - Comparing Ordered Values (12) - [Condition](./condition.md) - Expressing Assertions Using Conditions (5) - [Equality](./equality.md) - Asserting Two Things Are Equal (16) diff --git a/docs/doc-site/api/collection.md b/docs/doc-site/api/collection.md index 31e6d0768..c701f0ed6 100644 --- a/docs/doc-site/api/collection.md +++ b/docs/doc-site/api/collection.md @@ -15,8 +15,12 @@ keywords: - "Lenf" - "MapContainsT" - "MapContainsTf" + - "MapEqualT" + - "MapEqualTf" - "MapNotContainsT" - "MapNotContainsTf" + - "MapNotEqualT" + - "MapNotEqualTf" - "NotContains" - "NotContainsf" - "NotElementsMatch" @@ -31,8 +35,12 @@ keywords: - "SeqNotContainsTf" - "SliceContainsT" - "SliceContainsTf" + - "SliceEqualT" + - "SliceEqualTf" - "SliceNotContainsT" - "SliceNotContainsTf" + - "SliceNotEqualT" + - "SliceNotEqualTf" - "SliceNotSubsetT" - "SliceNotSubsetTf" - "SliceSubsetT" @@ -54,7 +62,7 @@ Asserting Slices And Maps _All links point to _ -This domain exposes 19 functionalities. +This domain exposes 23 functionalities. Generic assertions are marked with a {{% icon icon="star" color=orange %}}. ```tree @@ -63,7 +71,9 @@ Generic assertions are marked with a {{% icon icon="star" color=orange %}}. - [ElementsMatchT[E comparable]](#elementsmatchte-comparable) | star | orange - [Len](#len) | angles-right - [MapContainsT[Map ~map[K]V, K comparable, V any]](#mapcontainstmap-mapkv-k-comparable-v-any) | star | orange +- [MapEqualT[K, V comparable]](#mapequaltk-v-comparable) | star | orange - [MapNotContainsT[Map ~map[K]V, K comparable, V any]](#mapnotcontainstmap-mapkv-k-comparable-v-any) | star | orange +- [MapNotEqualT[K, V comparable]](#mapnotequaltk-v-comparable) | star | orange - [NotContains](#notcontains) | angles-right - [NotElementsMatch](#notelementsmatch) | angles-right - [NotElementsMatchT[E comparable]](#notelementsmatchte-comparable) | star | orange @@ -71,7 +81,9 @@ Generic assertions are marked with a {{% icon icon="star" color=orange %}}. - [SeqContainsT[E comparable]](#seqcontainste-comparable) | star | orange - [SeqNotContainsT[E comparable]](#seqnotcontainste-comparable) | star | orange - [SliceContainsT[Slice ~[]E, E comparable]](#slicecontainstslice-e-e-comparable) | star | orange +- [SliceEqualT[E comparable]](#sliceequalte-comparable) | star | orange - [SliceNotContainsT[Slice ~[]E, E comparable]](#slicenotcontainstslice-e-e-comparable) | star | orange +- [SliceNotEqualT[E comparable]](#slicenotequalte-comparable) | star | orange - [SliceNotSubsetT[Slice ~[]E, E comparable]](#slicenotsubsettslice-e-e-comparable) | star | orange - [SliceSubsetT[Slice ~[]E, E comparable]](#slicesubsettslice-e-e-comparable) | star | orange - [StringContainsT[ADoc, EDoc Text]](#stringcontainstadoc-edoc-text) | star | orange @@ -189,7 +201,7 @@ func main() { |--|--| | [`assertions.Contains(t T, s any, contains any, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#Contains) | internal implementation | -**Source:** [github.com/go-openapi/testify/v2/internal/assertions#Contains](https://github.com/go-openapi/testify/blob/master/internal/assertions/collection.go#L63) +**Source:** [github.com/go-openapi/testify/v2/internal/assertions#Contains](https://github.com/go-openapi/testify/blob/master/internal/assertions/collection.go#L64) {{% /tab %}} {{< /tabs >}} @@ -302,7 +314,7 @@ func main() { |--|--| | [`assertions.ElementsMatch(t T, listA any, listB any, msgAndArgs ...any) (ok bool)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#ElementsMatch) | internal implementation | -**Source:** [github.com/go-openapi/testify/v2/internal/assertions#ElementsMatch](https://github.com/go-openapi/testify/blob/master/internal/assertions/collection.go#L538) +**Source:** [github.com/go-openapi/testify/v2/internal/assertions#ElementsMatch](https://github.com/go-openapi/testify/blob/master/internal/assertions/collection.go#L539) {{% /tab %}} {{< /tabs >}} @@ -411,7 +423,7 @@ func main() { |--|--| | [`assertions.ElementsMatchT[E comparable](t T, listA []E, listB []E, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#ElementsMatchT) | internal implementation | -**Source:** [github.com/go-openapi/testify/v2/internal/assertions#ElementsMatchT](https://github.com/go-openapi/testify/blob/master/internal/assertions/collection.go#L611) +**Source:** [github.com/go-openapi/testify/v2/internal/assertions#ElementsMatchT](https://github.com/go-openapi/testify/blob/master/internal/assertions/collection.go#L612) {{% /tab %}} {{< /tabs >}} @@ -528,7 +540,7 @@ func main() { |--|--| | [`assertions.Len(t T, object any, length int, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#Len) | internal implementation | -**Source:** [github.com/go-openapi/testify/v2/internal/assertions#Len](https://github.com/go-openapi/testify/blob/master/internal/assertions/collection.go#L31) +**Source:** [github.com/go-openapi/testify/v2/internal/assertions#Len](https://github.com/go-openapi/testify/blob/master/internal/assertions/collection.go#L32) > **Note** > @@ -642,7 +654,119 @@ func main() { |--|--| | [`assertions.MapContainsT[Map ~map[K]V, K comparable, V any](t T, m Map, key K, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#MapContainsT) | internal implementation | -**Source:** [github.com/go-openapi/testify/v2/internal/assertions#MapContainsT](https://github.com/go-openapi/testify/blob/master/internal/assertions/collection.go#L177) +**Source:** [github.com/go-openapi/testify/v2/internal/assertions#MapContainsT](https://github.com/go-openapi/testify/blob/master/internal/assertions/collection.go#L178) +{{% /tab %}} +{{< /tabs >}} + +### MapEqualT[K, V comparable] {{% icon icon="star" color=orange %}}{#mapequaltk-v-comparable} +MapEqualT asserts that 2 maps of comparable elements are equal, +that is have the same length and contain the keys with the same elements. + +See also [maps.Equal](https://pkg.go.dev/maps#Equal). + + +[comparable-types]: https://go.dev/blog/comparable +{{% expand title="Examples" %}} +{{< tabs >}} +{{% tab title="Usage" %}} +```go + assertions.MapEqualT(t, map[string](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#string)string{"1": "Hello", "2": "World"}, map[string](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#string)string{"1": "Hello", "2": "World"}) + success: map[string](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#string)string{"1": "Hello", "2": "World"}, map[string](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#string)string{"1": "Hello", "2": "World"} + failure: map[string](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#string)string{"2": "Hello", "1": "World"}, map[string](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#string)string{"1": "Hello", "2": "World"} +``` +{{< /tab >}} +{{% tab title="Testable Examples (assert)" %}} +{{% cards %}} +{{% card %}} + + +*[Copy and click to open Go Playground](https://go.dev/play/)* + + +```go +// real-world test would inject *testing.T from TestMapEqualT(t *testing.T) +package main + +import ( + "fmt" + "testing" + + "github.com/go-openapi/testify/v2/assert" +) + +func main() { + t := new(testing.T) // should come from testing, e.g. func TestMapEqualT(t *testing.T) + success := assert.MapEqualT(t, map[string]string{"1": "Hello", "2": "World"}, map[string]string{"1": "Hello", "2": "World"}) + fmt.Printf("success: %t\n", success) + +} + +``` +{{% /card %}} + + +{{% /cards %}} +{{< /tab >}} + + +{{% tab title="Testable Examples (require)" %}} +{{% cards %}} +{{% card %}} + + +*[Copy and click to open Go Playground](https://go.dev/play/)* + + +```go +// real-world test would inject *testing.T from TestMapEqualT(t *testing.T) +package main + +import ( + "fmt" + "testing" + + "github.com/go-openapi/testify/v2/require" +) + +func main() { + t := new(testing.T) // should come from testing, e.g. func TestMapEqualT(t *testing.T) + require.MapEqualT(t, map[string]string{"1": "Hello", "2": "World"}, map[string]string{"1": "Hello", "2": "World"}) + fmt.Println("passed") + +} + +``` +{{% /card %}} + + +{{% /cards %}} +{{< /tab >}} + + +{{< /tabs >}} +{{% /expand %}} + +{{< tabs >}} + +{{% tab title="assert" style="secondary" %}} +| Signature | Usage | +|--|--| +| [`assert.MapEqualT[K, V comparable](t T, listA map[K]V, listB map[K]V, msgAndArgs ...any) (ok bool)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#MapEqualT) | package-level function | +| [`assert.MapEqualTf[K, V comparable](t T, listA map[K]V, listB map[K]V, msg string, args ...any) (ok bool)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#MapEqualTf) | formatted variant | +{{% /tab %}} +{{% tab title="require" style="secondary" %}} +| Signature | Usage | +|--|--| +| [`require.MapEqualT[K, V comparable](t T, listA map[K]V, listB map[K]V, msgAndArgs ...any) (ok bool)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#MapEqualT) | package-level function | +| [`require.MapEqualTf[K, V comparable](t T, listA map[K]V, listB map[K]V, msg string, args ...any) (ok bool)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#MapEqualTf) | formatted variant | +{{% /tab %}} + +{{% tab title="internal" style="accent" icon="wrench" %}} +| Signature | Usage | +|--|--| +| [`assertions.MapEqualT[K, V comparable](t T, listA map[K]V, listB map[K]V, msgAndArgs ...any) (ok bool)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#MapEqualT) | internal implementation | + +**Source:** [github.com/go-openapi/testify/v2/internal/assertions#MapEqualT](https://github.com/go-openapi/testify/blob/master/internal/assertions/collection.go#L733) {{% /tab %}} {{< /tabs >}} @@ -749,7 +873,118 @@ func main() { |--|--| | [`assertions.MapNotContainsT[Map ~map[K]V, K comparable, V any](t T, m Map, key K, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#MapNotContainsT) | internal implementation | -**Source:** [github.com/go-openapi/testify/v2/internal/assertions#MapNotContainsT](https://github.com/go-openapi/testify/blob/master/internal/assertions/collection.go#L308) +**Source:** [github.com/go-openapi/testify/v2/internal/assertions#MapNotContainsT](https://github.com/go-openapi/testify/blob/master/internal/assertions/collection.go#L309) +{{% /tab %}} +{{< /tabs >}} + +### MapNotEqualT[K, V comparable] {{% icon icon="star" color=orange %}}{#mapnotequaltk-v-comparable} +MapNotEqualT asserts that 2 maps of comparable elements are not equal. + +See also [MapEqualT](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#MapEqualT). + + +[comparable-types]: https://go.dev/blog/comparable +{{% expand title="Examples" %}} +{{< tabs >}} +{{% tab title="Usage" %}} +```go + assertions.MapNotEqualT(t, map[string](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#string)string{"2": "Hello", "1": "World"}, map[string](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#string)string{"1": "Hello", "2": "World"}) + success: map[string](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#string)string{"2": "Hello", "1": "World"}, map[string](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#string)string{"1": "Hello", "2": "World"} + failure: map[string](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#string)string{"1": "Hello", "2": "World"}, map[string](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#string)string{"1": "Hello", "2": "World"} +``` +{{< /tab >}} +{{% tab title="Testable Examples (assert)" %}} +{{% cards %}} +{{% card %}} + + +*[Copy and click to open Go Playground](https://go.dev/play/)* + + +```go +// real-world test would inject *testing.T from TestMapNotEqualT(t *testing.T) +package main + +import ( + "fmt" + "testing" + + "github.com/go-openapi/testify/v2/assert" +) + +func main() { + t := new(testing.T) // should come from testing, e.g. func TestMapNotEqualT(t *testing.T) + success := assert.MapNotEqualT(t, map[string]string{"2": "Hello", "1": "World"}, map[string]string{"1": "Hello", "2": "World"}) + fmt.Printf("success: %t\n", success) + +} + +``` +{{% /card %}} + + +{{% /cards %}} +{{< /tab >}} + + +{{% tab title="Testable Examples (require)" %}} +{{% cards %}} +{{% card %}} + + +*[Copy and click to open Go Playground](https://go.dev/play/)* + + +```go +// real-world test would inject *testing.T from TestMapNotEqualT(t *testing.T) +package main + +import ( + "fmt" + "testing" + + "github.com/go-openapi/testify/v2/require" +) + +func main() { + t := new(testing.T) // should come from testing, e.g. func TestMapNotEqualT(t *testing.T) + require.MapNotEqualT(t, map[string]string{"2": "Hello", "1": "World"}, map[string]string{"1": "Hello", "2": "World"}) + fmt.Println("passed") + +} + +``` +{{% /card %}} + + +{{% /cards %}} +{{< /tab >}} + + +{{< /tabs >}} +{{% /expand %}} + +{{< tabs >}} + +{{% tab title="assert" style="secondary" %}} +| Signature | Usage | +|--|--| +| [`assert.MapNotEqualT[K, V comparable](t T, listA map[K]V, listB map[K]V, msgAndArgs ...any) (ok bool)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#MapNotEqualT) | package-level function | +| [`assert.MapNotEqualTf[K, V comparable](t T, listA map[K]V, listB map[K]V, msg string, args ...any) (ok bool)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#MapNotEqualTf) | formatted variant | +{{% /tab %}} +{{% tab title="require" style="secondary" %}} +| Signature | Usage | +|--|--| +| [`require.MapNotEqualT[K, V comparable](t T, listA map[K]V, listB map[K]V, msgAndArgs ...any) (ok bool)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#MapNotEqualT) | package-level function | +| [`require.MapNotEqualTf[K, V comparable](t T, listA map[K]V, listB map[K]V, msg string, args ...any) (ok bool)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#MapNotEqualTf) | formatted variant | +{{% /tab %}} + +{{% tab title="internal" style="accent" icon="wrench" %}} +| Signature | Usage | +|--|--| +| [`assertions.MapNotEqualT[K, V comparable](t T, listA map[K]V, listB map[K]V, msgAndArgs ...any) (ok bool)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#MapNotEqualT) | internal implementation | + +**Source:** [github.com/go-openapi/testify/v2/internal/assertions#MapNotEqualT](https://github.com/go-openapi/testify/blob/master/internal/assertions/collection.go#L757) {{% /tab %}} {{< /tabs >}} @@ -863,7 +1098,7 @@ func main() { |--|--| | [`assertions.NotContains(t T, s any, contains any, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#NotContains) | internal implementation | -**Source:** [github.com/go-openapi/testify/v2/internal/assertions#NotContains](https://github.com/go-openapi/testify/blob/master/internal/assertions/collection.go#L204) +**Source:** [github.com/go-openapi/testify/v2/internal/assertions#NotContains](https://github.com/go-openapi/testify/blob/master/internal/assertions/collection.go#L205) {{% /tab %}} {{< /tabs >}} @@ -979,7 +1214,7 @@ func main() { |--|--| | [`assertions.NotElementsMatch(t T, listA any, listB any, msgAndArgs ...any) (ok bool)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#NotElementsMatch) | internal implementation | -**Source:** [github.com/go-openapi/testify/v2/internal/assertions#NotElementsMatch](https://github.com/go-openapi/testify/blob/master/internal/assertions/collection.go#L575) +**Source:** [github.com/go-openapi/testify/v2/internal/assertions#NotElementsMatch](https://github.com/go-openapi/testify/blob/master/internal/assertions/collection.go#L576) {{% /tab %}} {{< /tabs >}} @@ -1091,7 +1326,7 @@ func main() { |--|--| | [`assertions.NotElementsMatchT[E comparable](t T, listA []E, listB []E, msgAndArgs ...any) (ok bool)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#NotElementsMatchT) | internal implementation | -**Source:** [github.com/go-openapi/testify/v2/internal/assertions#NotElementsMatchT](https://github.com/go-openapi/testify/blob/master/internal/assertions/collection.go#L647) +**Source:** [github.com/go-openapi/testify/v2/internal/assertions#NotElementsMatchT](https://github.com/go-openapi/testify/blob/master/internal/assertions/collection.go#L648) {{% /tab %}} {{< /tabs >}} @@ -1208,7 +1443,7 @@ func main() { |--|--| | [`assertions.NotSubset(t T, list any, subset any, msgAndArgs ...any) (ok bool)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#NotSubset) | internal implementation | -**Source:** [github.com/go-openapi/testify/v2/internal/assertions#NotSubset](https://github.com/go-openapi/testify/blob/master/internal/assertions/collection.go#L444) +**Source:** [github.com/go-openapi/testify/v2/internal/assertions#NotSubset](https://github.com/go-openapi/testify/blob/master/internal/assertions/collection.go#L445) {{% /tab %}} {{< /tabs >}} @@ -1321,7 +1556,7 @@ func main() { |--|--| | [`assertions.SeqContainsT[E comparable](t T, iter iter.Seq[E], element E, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#SeqContainsT) | internal implementation | -**Source:** [github.com/go-openapi/testify/v2/internal/assertions#SeqContainsT](https://github.com/go-openapi/testify/blob/master/internal/assertions/collection.go#L148) +**Source:** [github.com/go-openapi/testify/v2/internal/assertions#SeqContainsT](https://github.com/go-openapi/testify/blob/master/internal/assertions/collection.go#L149) {{% /tab %}} {{< /tabs >}} @@ -1432,7 +1667,7 @@ func main() { |--|--| | [`assertions.SeqNotContainsT[E comparable](t T, iter iter.Seq[E], element E, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#SeqNotContainsT) | internal implementation | -**Source:** [github.com/go-openapi/testify/v2/internal/assertions#SeqNotContainsT](https://github.com/go-openapi/testify/blob/master/internal/assertions/collection.go#L283) +**Source:** [github.com/go-openapi/testify/v2/internal/assertions#SeqNotContainsT](https://github.com/go-openapi/testify/blob/master/internal/assertions/collection.go#L284) {{% /tab %}} {{< /tabs >}} @@ -1541,7 +1776,119 @@ func main() { |--|--| | [`assertions.SliceContainsT[Slice ~[]E, E comparable](t T, s Slice, element E, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#SliceContainsT) | internal implementation | -**Source:** [github.com/go-openapi/testify/v2/internal/assertions#SliceContainsT](https://github.com/go-openapi/testify/blob/master/internal/assertions/collection.go#L119) +**Source:** [github.com/go-openapi/testify/v2/internal/assertions#SliceContainsT](https://github.com/go-openapi/testify/blob/master/internal/assertions/collection.go#L120) +{{% /tab %}} +{{< /tabs >}} + +### SliceEqualT[E comparable] {{% icon icon="star" color=orange %}}{#sliceequalte-comparable} +SliceEqualT asserts that 2 slices of comparable elements are equal, +that is have the same length and contain the same elements in the same order. + +See also [slices.Equal](https://pkg.go.dev/slices#Equal). + + +[comparable-types]: https://go.dev/blog/comparable +{{% expand title="Examples" %}} +{{< tabs >}} +{{% tab title="Usage" %}} +```go + assertions.SliceEqualT(t, []string{"Hello","World"}, []string{"Hello","World"}) + success: []string{"Hello","World"}, []string{"Hello","World"} + failure: []string{"Hello","World"}, []string{"Hello"} +``` +{{< /tab >}} +{{% tab title="Testable Examples (assert)" %}} +{{% cards %}} +{{% card %}} + + +*[Copy and click to open Go Playground](https://go.dev/play/)* + + +```go +// real-world test would inject *testing.T from TestSliceEqualT(t *testing.T) +package main + +import ( + "fmt" + "testing" + + "github.com/go-openapi/testify/v2/assert" +) + +func main() { + t := new(testing.T) // should come from testing, e.g. func TestSliceEqualT(t *testing.T) + success := assert.SliceEqualT(t, []string{"Hello", "World"}, []string{"Hello", "World"}) + fmt.Printf("success: %t\n", success) + +} + +``` +{{% /card %}} + + +{{% /cards %}} +{{< /tab >}} + + +{{% tab title="Testable Examples (require)" %}} +{{% cards %}} +{{% card %}} + + +*[Copy and click to open Go Playground](https://go.dev/play/)* + + +```go +// real-world test would inject *testing.T from TestSliceEqualT(t *testing.T) +package main + +import ( + "fmt" + "testing" + + "github.com/go-openapi/testify/v2/require" +) + +func main() { + t := new(testing.T) // should come from testing, e.g. func TestSliceEqualT(t *testing.T) + require.SliceEqualT(t, []string{"Hello", "World"}, []string{"Hello", "World"}) + fmt.Println("passed") + +} + +``` +{{% /card %}} + + +{{% /cards %}} +{{< /tab >}} + + +{{< /tabs >}} +{{% /expand %}} + +{{< tabs >}} + +{{% tab title="assert" style="secondary" %}} +| Signature | Usage | +|--|--| +| [`assert.SliceEqualT[E comparable](t T, listA []E, listB []E, msgAndArgs ...any) (ok bool)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#SliceEqualT) | package-level function | +| [`assert.SliceEqualTf[E comparable](t T, listA []E, listB []E, msg string, args ...any) (ok bool)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#SliceEqualTf) | formatted variant | +{{% /tab %}} +{{% tab title="require" style="secondary" %}} +| Signature | Usage | +|--|--| +| [`require.SliceEqualT[E comparable](t T, listA []E, listB []E, msgAndArgs ...any) (ok bool)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#SliceEqualT) | package-level function | +| [`require.SliceEqualTf[E comparable](t T, listA []E, listB []E, msg string, args ...any) (ok bool)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#SliceEqualTf) | formatted variant | +{{% /tab %}} + +{{% tab title="internal" style="accent" icon="wrench" %}} +| Signature | Usage | +|--|--| +| [`assertions.SliceEqualT[E comparable](t T, listA []E, listB []E, msgAndArgs ...any) (ok bool)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#SliceEqualT) | internal implementation | + +**Source:** [github.com/go-openapi/testify/v2/internal/assertions#SliceEqualT](https://github.com/go-openapi/testify/blob/master/internal/assertions/collection.go#L684) {{% /tab %}} {{< /tabs >}} @@ -1650,7 +1997,118 @@ func main() { |--|--| | [`assertions.SliceNotContainsT[Slice ~[]E, E comparable](t T, s Slice, element E, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#SliceNotContainsT) | internal implementation | -**Source:** [github.com/go-openapi/testify/v2/internal/assertions#SliceNotContainsT](https://github.com/go-openapi/testify/blob/master/internal/assertions/collection.go#L258) +**Source:** [github.com/go-openapi/testify/v2/internal/assertions#SliceNotContainsT](https://github.com/go-openapi/testify/blob/master/internal/assertions/collection.go#L259) +{{% /tab %}} +{{< /tabs >}} + +### SliceNotEqualT[E comparable] {{% icon icon="star" color=orange %}}{#slicenotequalte-comparable} +SliceNotEqualT asserts that 2 slices of comparable elements are not equal. + +See also [SliceEqualT](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#SliceEqualT). + + +[comparable-types]: https://go.dev/blog/comparable +{{% expand title="Examples" %}} +{{< tabs >}} +{{% tab title="Usage" %}} +```go + assertions.SliceNotEqualT(t, []string{"Hello","World"}, []string{"Hello"}) + success: []string{"Hello","World"}, []string{"Hello"} + failure: []string{"Hello","World"}, []string{"Hello","World"} +``` +{{< /tab >}} +{{% tab title="Testable Examples (assert)" %}} +{{% cards %}} +{{% card %}} + + +*[Copy and click to open Go Playground](https://go.dev/play/)* + + +```go +// real-world test would inject *testing.T from TestSliceNotEqualT(t *testing.T) +package main + +import ( + "fmt" + "testing" + + "github.com/go-openapi/testify/v2/assert" +) + +func main() { + t := new(testing.T) // should come from testing, e.g. func TestSliceNotEqualT(t *testing.T) + success := assert.SliceNotEqualT(t, []string{"Hello", "World"}, []string{"Hello"}) + fmt.Printf("success: %t\n", success) + +} + +``` +{{% /card %}} + + +{{% /cards %}} +{{< /tab >}} + + +{{% tab title="Testable Examples (require)" %}} +{{% cards %}} +{{% card %}} + + +*[Copy and click to open Go Playground](https://go.dev/play/)* + + +```go +// real-world test would inject *testing.T from TestSliceNotEqualT(t *testing.T) +package main + +import ( + "fmt" + "testing" + + "github.com/go-openapi/testify/v2/require" +) + +func main() { + t := new(testing.T) // should come from testing, e.g. func TestSliceNotEqualT(t *testing.T) + require.SliceNotEqualT(t, []string{"Hello", "World"}, []string{"Hello"}) + fmt.Println("passed") + +} + +``` +{{% /card %}} + + +{{% /cards %}} +{{< /tab >}} + + +{{< /tabs >}} +{{% /expand %}} + +{{< tabs >}} + +{{% tab title="assert" style="secondary" %}} +| Signature | Usage | +|--|--| +| [`assert.SliceNotEqualT[E comparable](t T, listA []E, listB []E, msgAndArgs ...any) (ok bool)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#SliceNotEqualT) | package-level function | +| [`assert.SliceNotEqualTf[E comparable](t T, listA []E, listB []E, msg string, args ...any) (ok bool)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#SliceNotEqualTf) | formatted variant | +{{% /tab %}} +{{% tab title="require" style="secondary" %}} +| Signature | Usage | +|--|--| +| [`require.SliceNotEqualT[E comparable](t T, listA []E, listB []E, msgAndArgs ...any) (ok bool)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#SliceNotEqualT) | package-level function | +| [`require.SliceNotEqualTf[E comparable](t T, listA []E, listB []E, msg string, args ...any) (ok bool)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#SliceNotEqualTf) | formatted variant | +{{% /tab %}} + +{{% tab title="internal" style="accent" icon="wrench" %}} +| Signature | Usage | +|--|--| +| [`assertions.SliceNotEqualT[E comparable](t T, listA []E, listB []E, msgAndArgs ...any) (ok bool)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#SliceNotEqualT) | internal implementation | + +**Source:** [github.com/go-openapi/testify/v2/internal/assertions#SliceNotEqualT](https://github.com/go-openapi/testify/blob/master/internal/assertions/collection.go#L708) {{% /tab %}} {{< /tabs >}} @@ -1757,7 +2215,7 @@ func main() { |--|--| | [`assertions.SliceNotSubsetT[Slice ~[]E, E comparable](t T, list Slice, subset Slice, msgAndArgs ...any) (ok bool)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#SliceNotSubsetT) | internal implementation | -**Source:** [github.com/go-openapi/testify/v2/internal/assertions#SliceNotSubsetT](https://github.com/go-openapi/testify/blob/master/internal/assertions/collection.go#L511) +**Source:** [github.com/go-openapi/testify/v2/internal/assertions#SliceNotSubsetT](https://github.com/go-openapi/testify/blob/master/internal/assertions/collection.go#L512) {{% /tab %}} {{< /tabs >}} @@ -1864,7 +2322,7 @@ func main() { |--|--| | [`assertions.SliceSubsetT[Slice ~[]E, E comparable](t T, list Slice, subset Slice, msgAndArgs ...any) (ok bool)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#SliceSubsetT) | internal implementation | -**Source:** [github.com/go-openapi/testify/v2/internal/assertions#SliceSubsetT](https://github.com/go-openapi/testify/blob/master/internal/assertions/collection.go#L413) +**Source:** [github.com/go-openapi/testify/v2/internal/assertions#SliceSubsetT](https://github.com/go-openapi/testify/blob/master/internal/assertions/collection.go#L414) {{% /tab %}} {{< /tabs >}} @@ -1973,7 +2431,7 @@ func main() { |--|--| | [`assertions.StringContainsT[ADoc, EDoc Text](t T, str ADoc, substring EDoc, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#StringContainsT) | internal implementation | -**Source:** [github.com/go-openapi/testify/v2/internal/assertions#StringContainsT](https://github.com/go-openapi/testify/blob/master/internal/assertions/collection.go#L92) +**Source:** [github.com/go-openapi/testify/v2/internal/assertions#StringContainsT](https://github.com/go-openapi/testify/blob/master/internal/assertions/collection.go#L93) {{% /tab %}} {{< /tabs >}} @@ -2082,7 +2540,7 @@ func main() { |--|--| | [`assertions.StringNotContainsT[ADoc, EDoc Text](t T, str ADoc, substring EDoc, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#StringNotContainsT) | internal implementation | -**Source:** [github.com/go-openapi/testify/v2/internal/assertions#StringNotContainsT](https://github.com/go-openapi/testify/blob/master/internal/assertions/collection.go#L233) +**Source:** [github.com/go-openapi/testify/v2/internal/assertions#StringNotContainsT](https://github.com/go-openapi/testify/blob/master/internal/assertions/collection.go#L234) {{% /tab %}} {{< /tabs >}} @@ -2202,7 +2660,7 @@ func main() { |--|--| | [`assertions.Subset(t T, list any, subset any, msgAndArgs ...any) (ok bool)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#Subset) | internal implementation | -**Source:** [github.com/go-openapi/testify/v2/internal/assertions#Subset](https://github.com/go-openapi/testify/blob/master/internal/assertions/collection.go#L343) +**Source:** [github.com/go-openapi/testify/v2/internal/assertions#Subset](https://github.com/go-openapi/testify/blob/master/internal/assertions/collection.go#L344) {{% /tab %}} {{< /tabs >}} diff --git a/docs/doc-site/project/maintainers/ARCHITECTURE.md b/docs/doc-site/project/maintainers/ARCHITECTURE.md index d19212fb9..ea77cf8fc 100644 --- a/docs/doc-site/project/maintainers/ARCHITECTURE.md +++ b/docs/doc-site/project/maintainers/ARCHITECTURE.md @@ -57,7 +57,7 @@ We have adopted code and documentation generation as a mean to mitigate this iss #### Current (v2.3.0-unreleased) - 1. Generic assertions (with type parameters): 42 functions + 1. Generic assertions (with type parameters): 46 functions 2. Non-generic assertions (with t T parameter, no type parameters): 82 functions 3. Helper functions (no t T parameter): 4 functions diff --git a/docs/doc-site/project/maintainers/BENCHMARKS.md b/docs/doc-site/project/maintainers/BENCHMARKS.md index 16c4e4d35..6d834a37c 100644 --- a/docs/doc-site/project/maintainers/BENCHMARKS.md +++ b/docs/doc-site/project/maintainers/BENCHMARKS.md @@ -158,9 +158,9 @@ go test -run=^$ -bench=. -benchmem ./internal/assertions ## Benchmark Coverage -**38/42 generic functions benchmarked across 10 domains:** +**38/46 generic functions benchmarked across 10 domains:** - Boolean (2): TrueT, FalseT -- Collection (12): StringContainsT, SliceContainsT, MapContainsT, SeqContainsT, ElementsMatchT, SliceSubsetT, and negative variants +- Collection (16): StringContainsT, SliceContainsT, MapContainsT, SeqContainsT, ElementsMatchT, SliceSubsetT, SliceEqualT, MapEqualT, and negative variants - Comparison (6): GreaterT, LessT, GreaterOrEqualT, LessOrEqualT, PositiveT, NegativeT - Equality (4): EqualT, NotEqualT, SameT, NotSameT - JSON (1): JSONEqT diff --git a/docs/doc-site/usage/GENERICS.md b/docs/doc-site/usage/GENERICS.md index 446f9e696..cba989095 100644 --- a/docs/doc-site/usage/GENERICS.md +++ b/docs/doc-site/usage/GENERICS.md @@ -4,7 +4,7 @@ description: Using generic assertions. weight: 10 --- -Testify v2 provides **42 generic assertion functions** that offer compile-time type safety alongside +Testify v2 provides **46 generic assertion functions** that offer compile-time type safety alongside the traditional reflection-based assertions. Generic variants are identified by the `T` suffix (e.g., `EqualT`, `GreaterT`, `ElementsMatchT`). @@ -201,14 +201,16 @@ Testify v2 provides generic variants across all major domains: - `PositiveT[V SignedNumeric]` - Assert value > 0 - `NegativeT[V SignedNumeric]` - Assert value < 0 -### Collection (12 functions) +### Collection (16 functions) - `StringContainsT[S Text]` - String/byte slice contains substring - `SliceContainsT[E comparable]` - Slice contains element - `MapContainsT[K comparable, V any]` - Map contains key - `SeqContainsT[E comparable]` - Iterator contains element (Go 1.23+) - `ElementsMatchT[E comparable]` - Slices have same elements (any order) - `SliceSubsetT[E comparable]` - Slice is subset of another -- Plus negative variants: `*NotContainsT`, `NotElementsMatchT`, `SliceNotSubsetT` +- `SliceEqualT[E comparable]` - Slices are equal (same order) +- `MapEqualT[K, V comparable]` - Maps are equal (same keys and values) +- Plus negative variants: `*NotContainsT`, `NotElementsMatchT`, `SliceNotSubsetT`, `SliceNotEqualT`, `MapNotEqualT` ### Ordering (6 functions) - `IsIncreasingT[E Ordered]` - Slice elements strictly increasing diff --git a/docs/doc-site/usage/TRACKING.md b/docs/doc-site/usage/TRACKING.md index 5e4e3f54d..aed345e5f 100644 --- a/docs/doc-site/usage/TRACKING.md +++ b/docs/doc-site/usage/TRACKING.md @@ -119,7 +119,7 @@ This table catalogs all upstream PRs and issues from [github.com/stretchr/testif | Reference | Type | Summary | Outcome | |-----------|------|---------|---------| -| [#1147] | Issue | General discussion about generics adoption | ℹ️ Marked "Not Planned" upstream - We implemented our own generics approach (42 functions) | +| [#1147] | Issue | General discussion about generics adoption | ℹ️ Marked "Not Planned" upstream - We implemented our own generics approach (46 functions) | | [#1308] | PR | Comprehensive refactor with generic type parameters | ℹ️ Draft for v2.0.0 upstream - We took a different approach with the same objective | [#1147]: https://github.com/stretchr/testify/issues/1147 diff --git a/internal/assertions/collection.go b/internal/assertions/collection.go index a073d7e96..bcfe38ac1 100644 --- a/internal/assertions/collection.go +++ b/internal/assertions/collection.go @@ -7,6 +7,7 @@ import ( "bytes" "fmt" "iter" + "maps" "reflect" "slices" "strings" @@ -665,6 +666,104 @@ func NotElementsMatchT[E comparable](t T, listA, listB []E, msgAndArgs ...any) ( return true } +// SliceEqualT asserts that 2 slices of comparable elements are equal, +// that is have the same length and contain the same elements in the same order. +// +// See also [slices.Equal]. +// +// # Usage +// +// assertions.SliceEqualT(t, []string{"Hello","World"}, []string{"Hello","World"}) +// +// # Examples +// +// success: []string{"Hello","World"}, []string{"Hello","World"} +// failure: []string{"Hello","World"}, []string{"Hello"} +// +// [comparable-types]: https://go.dev/blog/comparable +func SliceEqualT[E comparable](t T, listA, listB []E, msgAndArgs ...any) (ok bool) { + // Domain: collection + ok = slices.Equal(listA, listB) + if !ok { + return Fail(t, "listA and listB are not equal", msgAndArgs) + } + + return true +} + +// SliceNotEqualT asserts that 2 slices of comparable elements are not equal. +// +// See also [SliceEqualT]. +// +// # Usage +// +// assertions.SliceNotEqualT(t, []string{"Hello","World"}, []string{"Hello"}) +// +// # Examples +// +// success: []string{"Hello","World"}, []string{"Hello"} +// failure: []string{"Hello","World"}, []string{"Hello","World"} +// +// [comparable-types]: https://go.dev/blog/comparable +func SliceNotEqualT[E comparable](t T, listA, listB []E, msgAndArgs ...any) (ok bool) { + // Domain: collection + ok = slices.Equal(listA, listB) + if ok { + return Fail(t, "listA and listB are equal", msgAndArgs) + } + + return true +} + +// MapEqualT asserts that 2 maps of comparable elements are equal, +// that is have the same length and contain the keys with the same elements. +// +// See also [maps.Equal]. +// +// # Usage +// +// assertions.MapEqualT(t, map[string]string{"1": "Hello", "2": "World"}, map[string]string{"1": "Hello", "2": "World"}) +// +// # Examples +// +// success: map[string]string{"1": "Hello", "2": "World"}, map[string]string{"1": "Hello", "2": "World"} +// failure: map[string]string{"2": "Hello", "1": "World"}, map[string]string{"1": "Hello", "2": "World"} +// +// [comparable-types]: https://go.dev/blog/comparable +func MapEqualT[K, V comparable](t T, listA, listB map[K]V, msgAndArgs ...any) (ok bool) { + // Domain: collection + ok = maps.Equal(listA, listB) + if !ok { + return Fail(t, "listA and listB are not equal", msgAndArgs) + } + + return true +} + +// MapNotEqualT asserts that 2 maps of comparable elements are not equal. +// +// See also [MapEqualT]. +// +// # Usage +// +// assertions.MapNotEqualT(t, map[string]string{"2": "Hello", "1": "World"}, map[string]string{"1": "Hello", "2": "World"}) +// +// # Examples +// +// success: map[string]string{"2": "Hello", "1": "World"}, map[string]string{"1": "Hello", "2": "World"} +// failure: map[string]string{"1": "Hello", "2": "World"}, map[string]string{"1": "Hello", "2": "World"} +// +// [comparable-types]: https://go.dev/blog/comparable +func MapNotEqualT[K, V comparable](t T, listA, listB map[K]V, msgAndArgs ...any) (ok bool) { + // Domain: collection + ok = maps.Equal(listA, listB) + if ok { + return Fail(t, "listA and listB are equal", msgAndArgs) + } + + return true +} + func isSubsetMap(t T, list, subset any, subsetMap, actualMap reflect.Value, msgAndArgs ...any) bool { for _, k := range subsetMap.MapKeys() { ev := subsetMap.MapIndex(k) diff --git a/internal/assertions/collection_test.go b/internal/assertions/collection_test.go index 5ba8ff135..37a0b5179 100644 --- a/internal/assertions/collection_test.go +++ b/internal/assertions/collection_test.go @@ -114,6 +114,36 @@ func TestCollectionElementsMatch(t *testing.T) { } } +// TestCollectionSliceEqual tests SliceEqualT and SliceNotEqualT. +func TestCollectionSliceEqual(t *testing.T) { + t.Parallel() + + for tc := range sliceEqualCases() { + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + + listA, listB := tc.makeValues() + t.Run("with SliceEqualT", testSliceEqualAssertion(tc, sliceEqualKind, listA, listB)) + t.Run("with SliceNotEqualT", testSliceEqualAssertion(tc, sliceNotEqualKind, listA, listB)) + }) + } +} + +// TestCollectionMapEqual tests MapEqualT and MapNotEqualT. +func TestCollectionMapEqual(t *testing.T) { + t.Parallel() + + for tc := range mapEqualCases() { + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + + listA, listB := tc.makeValues() + t.Run("with MapEqualT", testMapEqualAssertion(tc, mapEqualKind, listA, listB)) + t.Run("with MapNotEqualT", testMapEqualAssertion(tc, mapNotEqualKind, listA, listB)) + }) + } +} + // TestCollectionErrorMessages tests error message formatting for collection assertions. func TestCollectionErrorMessages(t *testing.T) { t.Parallel() @@ -903,6 +933,245 @@ func unifiedElementsMatchCases() iter.Seq[elementsMatchTestCase] { }) } +// ============================================================================ +// TestCollectionSliceEqual +// ============================================================================ + +// sliceEqualRelationship describes the equality relationship between two slices. +type sliceEqualRelationship int + +const ( + seEqual sliceEqualRelationship = iota + seNotEqual +) + +type sliceEqualAssertionKind int + +const ( + sliceEqualKind sliceEqualAssertionKind = iota + sliceNotEqualKind +) + +type sliceEqualTestCase struct { + name string + makeValues func() (listA, listB any) + relationship sliceEqualRelationship +} + +func testSliceEqualAssertion(tc sliceEqualTestCase, kind sliceEqualAssertionKind, listA, listB any) func(*testing.T) { + return func(t *testing.T) { + t.Parallel() + + mock := new(mockT) + + var result bool + switch a := listA.(type) { + case []int: + b, ok := listB.([]int) + if !ok { + t.Fatalf("test case error: []int listA requires []int listB, got %T", listB) + } + result = testSliceEqualGeneric(mock, kind, a, b) + case []string: + b, ok := listB.([]string) + if !ok { + t.Fatalf("test case error: []string listA requires []string listB, got %T", listB) + } + result = testSliceEqualGeneric(mock, kind, a, b) + case []float64: + b, ok := listB.([]float64) + if !ok { + t.Fatalf("test case error: []float64 listA requires []float64 listB, got %T", listB) + } + result = testSliceEqualGeneric(mock, kind, a, b) + default: + t.Fatalf("unsupported type for SliceEqualT: %T", listA) + } + + shouldPass := expectedStatusForSliceEqualAssertion(kind, tc.relationship) + shouldPassOrFail(t, mock, result, shouldPass) + } +} + +func testSliceEqualGeneric[E comparable](mock T, kind sliceEqualAssertionKind, listA, listB []E) bool { + switch kind { + case sliceEqualKind: + return SliceEqualT(mock, listA, listB) + case sliceNotEqualKind: + return SliceNotEqualT(mock, listA, listB) + default: + panic(fmt.Errorf("test case configuration error: invalid sliceEqualAssertionKind: %d", kind)) + } +} + +func expectedStatusForSliceEqualAssertion(kind sliceEqualAssertionKind, relationship sliceEqualRelationship) bool { + positive := kind == sliceEqualKind + + switch relationship { + case seEqual: + return positive + case seNotEqual: + return !positive + default: + panic(fmt.Errorf("test case configuration error: invalid sliceEqualRelationship: %d", relationship)) + } +} + +func sliceEqualCases() iter.Seq[sliceEqualTestCase] { + return slices.Values([]sliceEqualTestCase{ + // Equal cases + {"empty-empty", func() (any, any) { return []int{}, []int{} }, seEqual}, + {"single-element", func() (any, any) { return []int{1}, []int{1} }, seEqual}, + {"multiple-elements", func() (any, any) { return []int{1, 2, 3}, []int{1, 2, 3} }, seEqual}, + {"strings-equal", func() (any, any) { return []string{"a", "b"}, []string{"a", "b"} }, seEqual}, + {"nil-nil", func() (any, any) { return []int(nil), []int(nil) }, seEqual}, + {"nil-empty", func() (any, any) { return []int(nil), []int{} }, seEqual}, + {"empty-nil", func() (any, any) { return []int{}, []int(nil) }, seEqual}, + {"duplicates", func() (any, any) { return []int{1, 1, 1}, []int{1, 1, 1} }, seEqual}, + {"floats-equal", func() (any, any) { return []float64{1.1, 2.2}, []float64{1.1, 2.2} }, seEqual}, + + // Not equal cases + {"different-length", func() (any, any) { return []int{1, 2}, []int{1} }, seNotEqual}, + {"different-values", func() (any, any) { return []int{1, 2}, []int{1, 3} }, seNotEqual}, + {"different-order", func() (any, any) { return []int{1, 2}, []int{2, 1} }, seNotEqual}, + {"strings-different", func() (any, any) { return []string{"a", "b"}, []string{"a", "c"} }, seNotEqual}, + {"one-empty", func() (any, any) { return []int{1}, []int{} }, seNotEqual}, + {"empty-one", func() (any, any) { return []int{}, []int{1} }, seNotEqual}, + {"different-duplicates", func() (any, any) { return []int{1, 1}, []int{1, 2} }, seNotEqual}, + }) +} + +// ============================================================================ +// TestCollectionMapEqual +// ============================================================================ + +// mapEqualRelationship describes the equality relationship between two maps. +type mapEqualRelationship int + +const ( + meEqual mapEqualRelationship = iota + meNotEqual +) + +type mapEqualAssertionKind int + +const ( + mapEqualKind mapEqualAssertionKind = iota + mapNotEqualKind +) + +type mapEqualTestCase struct { + name string + makeValues func() (listA, listB any) + relationship mapEqualRelationship +} + +func testMapEqualAssertion(tc mapEqualTestCase, kind mapEqualAssertionKind, listA, listB any) func(*testing.T) { + return func(t *testing.T) { + t.Parallel() + + mock := new(mockT) + + var result bool + switch a := listA.(type) { + case map[string]int: + b, ok := listB.(map[string]int) + if !ok { + t.Fatalf("test case error: map[string]int listA requires map[string]int listB, got %T", listB) + } + result = testMapEqualGeneric(mock, kind, a, b) + case map[int]string: + b, ok := listB.(map[int]string) + if !ok { + t.Fatalf("test case error: map[int]string listA requires map[int]string listB, got %T", listB) + } + result = testMapEqualGeneric(mock, kind, a, b) + case map[string]string: + b, ok := listB.(map[string]string) + if !ok { + t.Fatalf("test case error: map[string]string listA requires map[string]string listB, got %T", listB) + } + result = testMapEqualGeneric(mock, kind, a, b) + default: + t.Fatalf("unsupported type for MapEqualT: %T", listA) + } + + shouldPass := expectedStatusForMapEqualAssertion(kind, tc.relationship) + shouldPassOrFail(t, mock, result, shouldPass) + } +} + +func testMapEqualGeneric[K, V comparable](mock T, kind mapEqualAssertionKind, listA, listB map[K]V) bool { + switch kind { + case mapEqualKind: + return MapEqualT(mock, listA, listB) + case mapNotEqualKind: + return MapNotEqualT(mock, listA, listB) + default: + panic(fmt.Errorf("test case configuration error: invalid mapEqualAssertionKind: %d", kind)) + } +} + +func expectedStatusForMapEqualAssertion(kind mapEqualAssertionKind, relationship mapEqualRelationship) bool { + positive := kind == mapEqualKind + + switch relationship { + case meEqual: + return positive + case meNotEqual: + return !positive + default: + panic(fmt.Errorf("test case configuration error: invalid mapEqualRelationship: %d", relationship)) + } +} + +func mapEqualCases() iter.Seq[mapEqualTestCase] { + return slices.Values([]mapEqualTestCase{ + // Equal cases + {"empty-empty", func() (any, any) { return map[string]int{}, map[string]int{} }, meEqual}, + {"single-entry", func() (any, any) { return map[string]int{"a": 1}, map[string]int{"a": 1} }, meEqual}, + { + "multiple-entries", + func() (any, any) { + return map[string]int{"a": 1, "b": 2, "c": 3}, map[string]int{"a": 1, "b": 2, "c": 3} + }, + meEqual, + }, + {"nil-nil", func() (any, any) { return map[string]int(nil), map[string]int(nil) }, meEqual}, + {"nil-empty", func() (any, any) { return map[string]int(nil), map[string]int{} }, meEqual}, + {"empty-nil", func() (any, any) { return map[string]int{}, map[string]int(nil) }, meEqual}, + { + "string-values", + func() (any, any) { + return map[int]string{1: "a", 2: "b"}, map[int]string{1: "a", 2: "b"} + }, + meEqual, + }, + { + "string-string", + func() (any, any) { + return map[string]string{"k1": "v1", "k2": "v2"}, map[string]string{"k1": "v1", "k2": "v2"} + }, + meEqual, + }, + + // Not equal cases + {"different-values", func() (any, any) { return map[string]int{"a": 1}, map[string]int{"a": 2} }, meNotEqual}, + {"different-keys", func() (any, any) { return map[string]int{"a": 1}, map[string]int{"b": 1} }, meNotEqual}, + {"different-length", func() (any, any) { return map[string]int{"a": 1, "b": 2}, map[string]int{"a": 1} }, meNotEqual}, + {"one-empty", func() (any, any) { return map[string]int{"a": 1}, map[string]int{} }, meNotEqual}, + {"empty-one", func() (any, any) { return map[string]int{}, map[string]int{"a": 1} }, meNotEqual}, + {"extra-key", func() (any, any) { return map[string]int{"a": 1}, map[string]int{"a": 1, "b": 2} }, meNotEqual}, + { + "string-different-values", + func() (any, any) { + return map[string]string{"k": "v1"}, map[string]string{"k": "v2"} + }, + meNotEqual, + }, + }) +} + // ============================================================================ // TestCollectionErrorMessages // ============================================================================ diff --git a/internal/assertions/format.go b/internal/assertions/format.go index b523ec0a6..72aaeb0d1 100644 --- a/internal/assertions/format.go +++ b/internal/assertions/format.go @@ -43,7 +43,7 @@ func indentMessageLines(message string, longestLabelLen int) string { if !firstLine { fmt.Fprint(outBuf, "\n\t"+strings.Repeat(" ", longestLabelLen+1)+"\t") } - fmt.Fprint(outBuf, scanner.Text()) //nolint:gosec // gosec false positive: G705: XSS via taint analysis + fmt.Fprint(outBuf, scanner.Text()) } return outBuf.String() diff --git a/internal/assertions/safety_test.go b/internal/assertions/safety_test.go index d3f585d2f..69fa52fc4 100644 --- a/internal/assertions/safety_test.go +++ b/internal/assertions/safety_test.go @@ -90,7 +90,7 @@ func TestNoFileDescriptorLeak_Failure(t *testing.T) { t.Cleanup(func() { if leakedFile != nil { leakedFile.Close() - os.Remove(leakedFile.Name()) //nolint:gosec // G703 path traversal is ok: this is a test. + os.Remove(leakedFile.Name()) } }) diff --git a/require/require_assertions.go b/require/require_assertions.go index 4a5020b6f..99e590932 100644 --- a/require/require_assertions.go +++ b/require/require_assertions.go @@ -2015,6 +2015,34 @@ func MapContainsT[Map ~map[K]V, K comparable, V any](t T, m Map, key K, msgAndAr t.FailNow() } +// MapEqualT asserts that 2 maps of comparable elements are equal, +// that is have the same length and contain the keys with the same elements. +// +// See also [maps.Equal]. +// +// # Usage +// +// assertions.MapEqualT(t, map[string]string{"1": "Hello", "2": "World"}, map[string]string{"1": "Hello", "2": "World"}) +// +// # Examples +// +// success: map[string]string{"1": "Hello", "2": "World"}, map[string]string{"1": "Hello", "2": "World"} +// failure: map[string]string{"2": "Hello", "1": "World"}, map[string]string{"1": "Hello", "2": "World"} +// +// Upon failure, the test [T] is marked as failed and stops execution. +// +// [comparable-types]: https://go.dev/blog/comparable +func MapEqualT[K, V comparable](t T, listA map[K]V, listB map[K]V, msgAndArgs ...any) { + if h, ok := t.(H); ok { + h.Helper() + } + if assertions.MapEqualT[K, V](t, listA, listB, msgAndArgs...) { + return + } + + t.FailNow() +} + // MapNotContainsT asserts that the specified map does not contain a key. // // # Usage @@ -2038,6 +2066,33 @@ func MapNotContainsT[Map ~map[K]V, K comparable, V any](t T, m Map, key K, msgAn t.FailNow() } +// MapNotEqualT asserts that 2 maps of comparable elements are not equal. +// +// See also [MapEqualT]. +// +// # Usage +// +// assertions.MapNotEqualT(t, map[string]string{"2": "Hello", "1": "World"}, map[string]string{"1": "Hello", "2": "World"}) +// +// # Examples +// +// success: map[string]string{"2": "Hello", "1": "World"}, map[string]string{"1": "Hello", "2": "World"} +// failure: map[string]string{"1": "Hello", "2": "World"}, map[string]string{"1": "Hello", "2": "World"} +// +// Upon failure, the test [T] is marked as failed and stops execution. +// +// [comparable-types]: https://go.dev/blog/comparable +func MapNotEqualT[K, V comparable](t T, listA map[K]V, listB map[K]V, msgAndArgs ...any) { + if h, ok := t.(H); ok { + h.Helper() + } + if assertions.MapNotEqualT[K, V](t, listA, listB, msgAndArgs...) { + return + } + + t.FailNow() +} + // Negative asserts that the specified element is strictly negative. // // # Usage @@ -3084,6 +3139,34 @@ func SliceContainsT[Slice ~[]E, E comparable](t T, s Slice, element E, msgAndArg t.FailNow() } +// SliceEqualT asserts that 2 slices of comparable elements are equal, +// that is have the same length and contain the same elements in the same order. +// +// See also [slices.Equal]. +// +// # Usage +// +// assertions.SliceEqualT(t, []string{"Hello","World"}, []string{"Hello","World"}) +// +// # Examples +// +// success: []string{"Hello","World"}, []string{"Hello","World"} +// failure: []string{"Hello","World"}, []string{"Hello"} +// +// Upon failure, the test [T] is marked as failed and stops execution. +// +// [comparable-types]: https://go.dev/blog/comparable +func SliceEqualT[E comparable](t T, listA []E, listB []E, msgAndArgs ...any) { + if h, ok := t.(H); ok { + h.Helper() + } + if assertions.SliceEqualT[E](t, listA, listB, msgAndArgs...) { + return + } + + t.FailNow() +} + // SliceNotContainsT asserts that the specified slice does not contain a comparable element. // // See [SliceContainsT]. @@ -3109,6 +3192,33 @@ func SliceNotContainsT[Slice ~[]E, E comparable](t T, s Slice, element E, msgAnd t.FailNow() } +// SliceNotEqualT asserts that 2 slices of comparable elements are not equal. +// +// See also [SliceEqualT]. +// +// # Usage +// +// assertions.SliceNotEqualT(t, []string{"Hello","World"}, []string{"Hello"}) +// +// # Examples +// +// success: []string{"Hello","World"}, []string{"Hello"} +// failure: []string{"Hello","World"}, []string{"Hello","World"} +// +// Upon failure, the test [T] is marked as failed and stops execution. +// +// [comparable-types]: https://go.dev/blog/comparable +func SliceNotEqualT[E comparable](t T, listA []E, listB []E, msgAndArgs ...any) { + if h, ok := t.(H); ok { + h.Helper() + } + if assertions.SliceNotEqualT[E](t, listA, listB, msgAndArgs...) { + return + } + + t.FailNow() +} + // SliceNotSubsetT asserts that a slice of comparable elements does not contain all the elements given in the subset. // // # Usage diff --git a/require/require_assertions_test.go b/require/require_assertions_test.go index 0217339a7..96f590e99 100644 --- a/require/require_assertions_test.go +++ b/require/require_assertions_test.go @@ -1611,6 +1611,29 @@ func TestMapContainsT(t *testing.T) { }) } +func TestMapEqualT(t *testing.T) { + t.Parallel() + + t.Run("success", func(t *testing.T) { + t.Parallel() + + mock := new(mockFailNowT) + MapEqualT(mock, map[string]string{"1": "Hello", "2": "World"}, map[string]string{"1": "Hello", "2": "World"}) + // require functions don't return a value + }) + + t.Run("failure", func(t *testing.T) { + t.Parallel() + + mock := new(mockFailNowT) + MapEqualT(mock, map[string]string{"2": "Hello", "1": "World"}, map[string]string{"1": "Hello", "2": "World"}) + // require functions don't return a value + if !mock.failed { + t.Error("MapEqualT should call FailNow()") + } + }) +} + func TestMapNotContainsT(t *testing.T) { t.Parallel() @@ -1634,6 +1657,29 @@ func TestMapNotContainsT(t *testing.T) { }) } +func TestMapNotEqualT(t *testing.T) { + t.Parallel() + + t.Run("success", func(t *testing.T) { + t.Parallel() + + mock := new(mockFailNowT) + MapNotEqualT(mock, map[string]string{"2": "Hello", "1": "World"}, map[string]string{"1": "Hello", "2": "World"}) + // require functions don't return a value + }) + + t.Run("failure", func(t *testing.T) { + t.Parallel() + + mock := new(mockFailNowT) + MapNotEqualT(mock, map[string]string{"1": "Hello", "2": "World"}, map[string]string{"1": "Hello", "2": "World"}) + // require functions don't return a value + if !mock.failed { + t.Error("MapNotEqualT should call FailNow()") + } + }) +} + func TestNegative(t *testing.T) { t.Parallel() @@ -2509,6 +2555,29 @@ func TestSliceContainsT(t *testing.T) { }) } +func TestSliceEqualT(t *testing.T) { + t.Parallel() + + t.Run("success", func(t *testing.T) { + t.Parallel() + + mock := new(mockFailNowT) + SliceEqualT(mock, []string{"Hello", "World"}, []string{"Hello", "World"}) + // require functions don't return a value + }) + + t.Run("failure", func(t *testing.T) { + t.Parallel() + + mock := new(mockFailNowT) + SliceEqualT(mock, []string{"Hello", "World"}, []string{"Hello"}) + // require functions don't return a value + if !mock.failed { + t.Error("SliceEqualT should call FailNow()") + } + }) +} + func TestSliceNotContainsT(t *testing.T) { t.Parallel() @@ -2532,6 +2601,29 @@ func TestSliceNotContainsT(t *testing.T) { }) } +func TestSliceNotEqualT(t *testing.T) { + t.Parallel() + + t.Run("success", func(t *testing.T) { + t.Parallel() + + mock := new(mockFailNowT) + SliceNotEqualT(mock, []string{"Hello", "World"}, []string{"Hello"}) + // require functions don't return a value + }) + + t.Run("failure", func(t *testing.T) { + t.Parallel() + + mock := new(mockFailNowT) + SliceNotEqualT(mock, []string{"Hello", "World"}, []string{"Hello", "World"}) + // require functions don't return a value + if !mock.failed { + t.Error("SliceNotEqualT should call FailNow()") + } + }) +} + func TestSliceNotSubsetT(t *testing.T) { t.Parallel() diff --git a/require/require_examples_test.go b/require/require_examples_test.go index 6b778703b..8e061279a 100644 --- a/require/require_examples_test.go +++ b/require/require_examples_test.go @@ -581,6 +581,14 @@ func ExampleMapContainsT() { // Output: passed } +func ExampleMapEqualT() { + t := new(testing.T) // should come from testing, e.g. func TestMapEqualT(t *testing.T) + require.MapEqualT(t, map[string]string{"1": "Hello", "2": "World"}, map[string]string{"1": "Hello", "2": "World"}) + fmt.Println("passed") + + // Output: passed +} + func ExampleMapNotContainsT() { t := new(testing.T) // should come from testing, e.g. func TestMapNotContainsT(t *testing.T) require.MapNotContainsT(t, map[string]string{"A": "B"}, "C") @@ -589,6 +597,14 @@ func ExampleMapNotContainsT() { // Output: passed } +func ExampleMapNotEqualT() { + t := new(testing.T) // should come from testing, e.g. func TestMapNotEqualT(t *testing.T) + require.MapNotEqualT(t, map[string]string{"2": "Hello", "1": "World"}, map[string]string{"1": "Hello", "2": "World"}) + fmt.Println("passed") + + // Output: passed +} + func ExampleNegative() { t := new(testing.T) // should come from testing, e.g. func TestNegative(t *testing.T) require.Negative(t, -1) @@ -920,6 +936,14 @@ func ExampleSliceContainsT() { // Output: passed } +func ExampleSliceEqualT() { + t := new(testing.T) // should come from testing, e.g. func TestSliceEqualT(t *testing.T) + require.SliceEqualT(t, []string{"Hello", "World"}, []string{"Hello", "World"}) + fmt.Println("passed") + + // Output: passed +} + func ExampleSliceNotContainsT() { t := new(testing.T) // should come from testing, e.g. func TestSliceNotContainsT(t *testing.T) require.SliceNotContainsT(t, []string{"A", "B"}, "C") @@ -928,6 +952,14 @@ func ExampleSliceNotContainsT() { // Output: passed } +func ExampleSliceNotEqualT() { + t := new(testing.T) // should come from testing, e.g. func TestSliceNotEqualT(t *testing.T) + require.SliceNotEqualT(t, []string{"Hello", "World"}, []string{"Hello"}) + fmt.Println("passed") + + // Output: passed +} + func ExampleSliceNotSubsetT() { t := new(testing.T) // should come from testing, e.g. func TestSliceNotSubsetT(t *testing.T) require.SliceNotSubsetT(t, []int{1, 2, 3}, []int{4, 5}) diff --git a/require/require_format.go b/require/require_format.go index 2491ca815..08773917a 100644 --- a/require/require_format.go +++ b/require/require_format.go @@ -991,6 +991,20 @@ func MapContainsTf[Map ~map[K]V, K comparable, V any](t T, m Map, key K, msg str t.FailNow() } +// MapEqualTf is the same as [MapEqualT], but it accepts a format string to format arguments like [fmt.Printf]. +// +// Upon failure, the test [T] is marked as failed and stops execution. +func MapEqualTf[K, V comparable](t T, listA map[K]V, listB map[K]V, msg string, args ...any) { + if h, ok := t.(H); ok { + h.Helper() + } + if assertions.MapEqualT[K, V](t, listA, listB, forwardArgs(msg, args)) { + return + } + + t.FailNow() +} + // MapNotContainsTf is the same as [MapNotContainsT], but it accepts a format string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and stops execution. @@ -1005,6 +1019,20 @@ func MapNotContainsTf[Map ~map[K]V, K comparable, V any](t T, m Map, key K, msg t.FailNow() } +// MapNotEqualTf is the same as [MapNotEqualT], but it accepts a format string to format arguments like [fmt.Printf]. +// +// Upon failure, the test [T] is marked as failed and stops execution. +func MapNotEqualTf[K, V comparable](t T, listA map[K]V, listB map[K]V, msg string, args ...any) { + if h, ok := t.(H); ok { + h.Helper() + } + if assertions.MapNotEqualT[K, V](t, listA, listB, forwardArgs(msg, args)) { + return + } + + t.FailNow() +} + // Negativef is the same as [Negative], but it accepts a format string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and stops execution. @@ -1551,6 +1579,20 @@ func SliceContainsTf[Slice ~[]E, E comparable](t T, s Slice, element E, msg stri t.FailNow() } +// SliceEqualTf is the same as [SliceEqualT], but it accepts a format string to format arguments like [fmt.Printf]. +// +// Upon failure, the test [T] is marked as failed and stops execution. +func SliceEqualTf[E comparable](t T, listA []E, listB []E, msg string, args ...any) { + if h, ok := t.(H); ok { + h.Helper() + } + if assertions.SliceEqualT[E](t, listA, listB, forwardArgs(msg, args)) { + return + } + + t.FailNow() +} + // SliceNotContainsTf is the same as [SliceNotContainsT], but it accepts a format string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and stops execution. @@ -1565,6 +1607,20 @@ func SliceNotContainsTf[Slice ~[]E, E comparable](t T, s Slice, element E, msg s t.FailNow() } +// SliceNotEqualTf is the same as [SliceNotEqualT], but it accepts a format string to format arguments like [fmt.Printf]. +// +// Upon failure, the test [T] is marked as failed and stops execution. +func SliceNotEqualTf[E comparable](t T, listA []E, listB []E, msg string, args ...any) { + if h, ok := t.(H); ok { + h.Helper() + } + if assertions.SliceNotEqualT[E](t, listA, listB, forwardArgs(msg, args)) { + return + } + + t.FailNow() +} + // SliceNotSubsetTf is the same as [SliceNotSubsetT], but it accepts a format string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and stops execution. diff --git a/require/require_format_test.go b/require/require_format_test.go index c1ab07271..1c1ed3b78 100644 --- a/require/require_format_test.go +++ b/require/require_format_test.go @@ -1611,6 +1611,29 @@ func TestMapContainsTf(t *testing.T) { }) } +func TestMapEqualTf(t *testing.T) { + t.Parallel() + + t.Run("success", func(t *testing.T) { + t.Parallel() + + mock := new(mockFailNowT) + MapEqualTf(mock, map[string]string{"1": "Hello", "2": "World"}, map[string]string{"1": "Hello", "2": "World"}, "test message") + // require functions don't return a value + }) + + t.Run("failure", func(t *testing.T) { + t.Parallel() + + mock := new(mockFailNowT) + MapEqualTf(mock, map[string]string{"2": "Hello", "1": "World"}, map[string]string{"1": "Hello", "2": "World"}, "test message") + // require functions don't return a value + if !mock.failed { + t.Error("MapEqualTf should call FailNow()") + } + }) +} + func TestMapNotContainsTf(t *testing.T) { t.Parallel() @@ -1634,6 +1657,29 @@ func TestMapNotContainsTf(t *testing.T) { }) } +func TestMapNotEqualTf(t *testing.T) { + t.Parallel() + + t.Run("success", func(t *testing.T) { + t.Parallel() + + mock := new(mockFailNowT) + MapNotEqualTf(mock, map[string]string{"2": "Hello", "1": "World"}, map[string]string{"1": "Hello", "2": "World"}, "test message") + // require functions don't return a value + }) + + t.Run("failure", func(t *testing.T) { + t.Parallel() + + mock := new(mockFailNowT) + MapNotEqualTf(mock, map[string]string{"1": "Hello", "2": "World"}, map[string]string{"1": "Hello", "2": "World"}, "test message") + // require functions don't return a value + if !mock.failed { + t.Error("MapNotEqualTf should call FailNow()") + } + }) +} + func TestNegativef(t *testing.T) { t.Parallel() @@ -2509,6 +2555,29 @@ func TestSliceContainsTf(t *testing.T) { }) } +func TestSliceEqualTf(t *testing.T) { + t.Parallel() + + t.Run("success", func(t *testing.T) { + t.Parallel() + + mock := new(mockFailNowT) + SliceEqualTf(mock, []string{"Hello", "World"}, []string{"Hello", "World"}, "test message") + // require functions don't return a value + }) + + t.Run("failure", func(t *testing.T) { + t.Parallel() + + mock := new(mockFailNowT) + SliceEqualTf(mock, []string{"Hello", "World"}, []string{"Hello"}, "test message") + // require functions don't return a value + if !mock.failed { + t.Error("SliceEqualTf should call FailNow()") + } + }) +} + func TestSliceNotContainsTf(t *testing.T) { t.Parallel() @@ -2532,6 +2601,29 @@ func TestSliceNotContainsTf(t *testing.T) { }) } +func TestSliceNotEqualTf(t *testing.T) { + t.Parallel() + + t.Run("success", func(t *testing.T) { + t.Parallel() + + mock := new(mockFailNowT) + SliceNotEqualTf(mock, []string{"Hello", "World"}, []string{"Hello"}, "test message") + // require functions don't return a value + }) + + t.Run("failure", func(t *testing.T) { + t.Parallel() + + mock := new(mockFailNowT) + SliceNotEqualTf(mock, []string{"Hello", "World"}, []string{"Hello", "World"}, "test message") + // require functions don't return a value + if !mock.failed { + t.Error("SliceNotEqualTf should call FailNow()") + } + }) +} + func TestSliceNotSubsetTf(t *testing.T) { t.Parallel()