From 188fc30d6b4c180bd6599acaa8437e8d22ef0ebd Mon Sep 17 00:00:00 2001 From: TheRodzz <81969589+TheRodzz@users.noreply.github.com> Date: Tue, 25 Aug 2026 03:42:10 +0000 Subject: [PATCH] Add ErrorNotContains assertion --- assert/assertion_format.go | 11 ++++++++++ assert/assertion_forward.go | 22 ++++++++++++++++++++ assert/assertions.go | 21 +++++++++++++++++++ assert/assertions_test.go | 31 ++++++++++++++++++++++++++++ assert/forward_assertions_test.go | 21 +++++++++++++++++++ require/forward_requirements_test.go | 14 +++++++++++++ require/require.go | 29 ++++++++++++++++++++++++++ require/require_forward.go | 24 +++++++++++++++++++++ require/requirements_test.go | 12 +++++++++++ 9 files changed, 185 insertions(+) diff --git a/assert/assertion_format.go b/assert/assertion_format.go index a19a89279..8a8a6c616 100644 --- a/assert/assertion_format.go +++ b/assert/assertion_format.go @@ -156,6 +156,17 @@ func ErrorContainsf(t TestingT, theError error, contains string, msg string, arg return ErrorContains(t, theError, contains, append([]interface{}{msg}, args...)...) } +// ErrorNotContainsf asserts that a function returned a non-nil error (i.e. an +// error) and that the error does not contain the specified substring. +// +// assert.ErrorNotContainsf(t, err, expectedErrorSubString, "error message %s", "formatted") +func ErrorNotContainsf(t TestingT, theError error, contains string, msg string, args ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + return ErrorNotContains(t, theError, contains, append([]interface{}{msg}, args...)...) +} + // ErrorIsf asserts that at least one of the errors in err's chain matches target. // This is a wrapper for errors.Is. func ErrorIsf(t TestingT, err error, target error, msg string, args ...interface{}) bool { diff --git a/assert/assertion_forward.go b/assert/assertion_forward.go index cd2a86061..6ce95283f 100644 --- a/assert/assertion_forward.go +++ b/assert/assertion_forward.go @@ -293,6 +293,28 @@ func (a *Assertions) ErrorContainsf(theError error, contains string, msg string, return ErrorContainsf(a.t, theError, contains, msg, args...) } +// ErrorNotContains asserts that a function returned a non-nil error (i.e. an +// error) and that the error does not contain the specified substring. +// +// a.ErrorNotContains(err, expectedErrorSubString) +func (a *Assertions) ErrorNotContains(theError error, contains string, msgAndArgs ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return ErrorNotContains(a.t, theError, contains, msgAndArgs...) +} + +// ErrorNotContainsf asserts that a function returned a non-nil error (i.e. an +// error) and that the error does not contain the specified substring. +// +// a.ErrorNotContainsf(err, expectedErrorSubString, "error message %s", "formatted") +func (a *Assertions) ErrorNotContainsf(theError error, contains string, msg string, args ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return ErrorNotContainsf(a.t, theError, contains, msg, args...) +} + // ErrorIs asserts that at least one of the errors in err's chain matches target. // This is a wrapper for errors.Is. func (a *Assertions) ErrorIs(err error, target error, msgAndArgs ...interface{}) bool { diff --git a/assert/assertions.go b/assert/assertions.go index 166f63726..504c1e575 100644 --- a/assert/assertions.go +++ b/assert/assertions.go @@ -1707,6 +1707,27 @@ func ErrorContains(t TestingT, theError error, contains string, msgAndArgs ...in return true } +// ErrorNotContains asserts that a function returned a non-nil error (i.e. an +// error) and that the error does not contain the specified substring. +// +// actualObj, err := SomeFunction() +// assert.ErrorNotContains(t, err, expectedErrorSubString) +func ErrorNotContains(t TestingT, theError error, contains string, msgAndArgs ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if !Error(t, theError, msgAndArgs...) { + return false + } + + actual := theError.Error() + if strings.Contains(actual, contains) { + return Fail(t, fmt.Sprintf("Error %s contains %#v", truncatingFormat("%#v", actual), contains), msgAndArgs...) + } + + return true +} + // matchRegexp return true if a specified regexp matches a string. func matchRegexp(rx interface{}, str interface{}) bool { var r *regexp.Regexp diff --git a/assert/assertions_test.go b/assert/assertions_test.go index 11642e096..74f5628ff 100644 --- a/assert/assertions_test.go +++ b/assert/assertions_test.go @@ -1760,6 +1760,26 @@ func TestErrorContains(t *testing.T) { "ErrorContains should return true") } +func TestErrorNotContains(t *testing.T) { + t.Parallel() + + mockT := new(testing.T) + + // start with a nil error + var err error + False(t, ErrorNotContains(mockT, err, ""), + "ErrorNotContains should return false for nil arg") + + // now set an error + err = errors.New("some error: another error") + True(t, ErrorNotContains(mockT, err, "bad error"), + "ErrorNotContains should return true for different error string") + False(t, ErrorNotContains(mockT, err, "some error"), + "ErrorNotContains should return false") + False(t, ErrorNotContains(mockT, err, "another error"), + "ErrorNotContains should return false") +} + func Test_isEmpty(t *testing.T) { t.Parallel() @@ -4150,6 +4170,17 @@ func TestErrorContainsWithErrorTooLongToPrint(t *testing.T) { Contains(t, mockT.errorString(), `<... truncated> does not contain "EOF"`) } +func TestErrorNotContainsWithErrorTooLongToPrint(t *testing.T) { + t.Parallel() + mockT := new(mockTestingT) + longSlice := make([]int, 1_000_000) + ErrorNotContains(mockT, fmt.Errorf("long: %v", longSlice), "long:") + Contains(t, mockT.errorString(), ` + Error Trace: + Error: Error "long: [0 0 0`) + Contains(t, mockT.errorString(), `<... truncated> contains "long:"`) +} + func TestZeroWithSliceTooLongToPrint(t *testing.T) { t.Parallel() mockT := new(mockTestingT) diff --git a/assert/forward_assertions_test.go b/assert/forward_assertions_test.go index 5523422fe..029c66170 100644 --- a/assert/forward_assertions_test.go +++ b/assert/forward_assertions_test.go @@ -366,6 +366,27 @@ func TestErrorContainsWrapper(t *testing.T) { "ErrorContains should return true") } +func TestErrorNotContainsWrapper(t *testing.T) { + t.Parallel() + + assert := New(t) + mockAssert := New(new(testing.T)) + + // start with a nil error + var err error + assert.False(mockAssert.ErrorNotContains(err, ""), + "ErrorNotContains should return false for nil arg") + + // now set an error + err = errors.New("some error: another error") + assert.True(mockAssert.ErrorNotContains(err, "bad error"), + "ErrorNotContains should return true for different error string") + assert.False(mockAssert.ErrorNotContains(err, "some error"), + "ErrorNotContains should return false") + assert.False(mockAssert.ErrorNotContains(err, "another error"), + "ErrorNotContains should return false") +} + func TestEqualErrorWrapper(t *testing.T) { t.Parallel() diff --git a/require/forward_requirements_test.go b/require/forward_requirements_test.go index 617bfb2c3..1563fe869 100644 --- a/require/forward_requirements_test.go +++ b/require/forward_requirements_test.go @@ -254,6 +254,20 @@ func TestErrorContainsWrapper(t *testing.T) { } } +func TestErrorNotContainsWrapper(t *testing.T) { + t.Parallel() + + require := New(t) + require.ErrorNotContains(errors.New("some error: another error"), "different error") + + mockT := new(MockT) + mockRequire := New(mockT) + mockRequire.ErrorNotContains(errors.New("some error: another error"), "some error") + if !mockT.Failed { + t.Error("Check should fail") + } +} + func TestEqualErrorWrapper(t *testing.T) { t.Parallel() diff --git a/require/require.go b/require/require.go index 652871f2e..c25185fc4 100644 --- a/require/require.go +++ b/require/require.go @@ -363,6 +363,35 @@ func ErrorContainsf(t TestingT, theError error, contains string, msg string, arg t.FailNow() } +// ErrorNotContains asserts that a function returned a non-nil error (i.e. an +// error) and that the error does not contain the specified substring. +// +// actualObj, err := SomeFunction() +// require.ErrorNotContains(t, err, expectedErrorSubString) +func ErrorNotContains(t TestingT, theError error, contains string, msgAndArgs ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.ErrorNotContains(t, theError, contains, msgAndArgs...) { + return + } + t.FailNow() +} + +// ErrorNotContainsf asserts that a function returned a non-nil error (i.e. an +// error) and that the error does not contain the specified substring. +// +// require.ErrorNotContainsf(t, err, expectedErrorSubString, "error message %s", "formatted") +func ErrorNotContainsf(t TestingT, theError error, contains string, msg string, args ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.ErrorNotContainsf(t, theError, contains, msg, args...) { + return + } + t.FailNow() +} + // ErrorIs asserts that at least one of the errors in err's chain matches target. // This is a wrapper for errors.Is. func ErrorIs(t TestingT, err error, target error, msgAndArgs ...interface{}) { diff --git a/require/require_forward.go b/require/require_forward.go index edac147ef..633775d56 100644 --- a/require/require_forward.go +++ b/require/require_forward.go @@ -294,6 +294,30 @@ func (a *Assertions) ErrorContainsf(theError error, contains string, msg string, ErrorContainsf(a.t, theError, contains, msg, args...) } +// ErrorNotContains asserts that a function returned a non-nil error (i.e. an +// error) and that the error does not contain the specified substring. +// +// actualObj, err := SomeFunction() +// a.ErrorNotContains(err, expectedErrorSubString) +func (a *Assertions) ErrorNotContains(theError error, contains string, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + ErrorNotContains(a.t, theError, contains, msgAndArgs...) +} + +// ErrorNotContainsf asserts that a function returned a non-nil error (i.e. an +// error) and that the error does not contain the specified substring. +// +// actualObj, err := SomeFunction() +// a.ErrorNotContainsf(err, expectedErrorSubString, "error message %s", "formatted") +func (a *Assertions) ErrorNotContainsf(theError error, contains string, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + ErrorNotContainsf(a.t, theError, contains, msg, args...) +} + // ErrorIs asserts that at least one of the errors in err's chain matches target. // This is a wrapper for errors.Is. func (a *Assertions) ErrorIs(err error, target error, msgAndArgs ...interface{}) { diff --git a/require/requirements_test.go b/require/requirements_test.go index 7cb63a554..55fb2d026 100644 --- a/require/requirements_test.go +++ b/require/requirements_test.go @@ -240,6 +240,18 @@ func TestErrorContains(t *testing.T) { } } +func TestErrorNotContains(t *testing.T) { + t.Parallel() + + ErrorNotContains(t, errors.New("some error: another error"), "different error") + + mockT := new(MockT) + ErrorNotContains(mockT, errors.New("some error"), "some error") + if !mockT.Failed { + t.Error("Check should fail") + } +} + func TestEqualError(t *testing.T) { t.Parallel()