diff --git a/assert/assertion_format.go b/assert/assertion_format.go index 546fe1fb2..190634165 100644 --- a/assert/assertion_format.go +++ b/assert/assertion_format.go @@ -621,6 +621,15 @@ func NotEqualValuesf(t TestingT, expected interface{}, actual interface{}, msg s return NotEqualValues(t, expected, actual, append([]interface{}{msg}, args...)...) } +// NotErrorAsf asserts that none of the errors in err's chain matches target, +// but if so, sets target to that error value. +func NotErrorAsf(t TestingT, err error, target interface{}, msg string, args ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + return NotErrorAs(t, err, target, append([]interface{}{msg}, args...)...) +} + // NotErrorIsf asserts that none of the errors in err's chain matches target. // This is a wrapper for errors.Is. func NotErrorIsf(t TestingT, err error, target error, msg string, args ...interface{}) bool { diff --git a/assert/assertion_forward.go b/assert/assertion_forward.go index 8504dca9f..21629087b 100644 --- a/assert/assertion_forward.go +++ b/assert/assertion_forward.go @@ -1234,6 +1234,24 @@ func (a *Assertions) NotEqualf(expected interface{}, actual interface{}, msg str return NotEqualf(a.t, expected, actual, msg, args...) } +// NotErrorAs asserts that none of the errors in err's chain matches target, +// but if so, sets target to that error value. +func (a *Assertions) NotErrorAs(err error, target interface{}, msgAndArgs ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return NotErrorAs(a.t, err, target, msgAndArgs...) +} + +// NotErrorAsf asserts that none of the errors in err's chain matches target, +// but if so, sets target to that error value. +func (a *Assertions) NotErrorAsf(err error, target interface{}, msg string, args ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return NotErrorAsf(a.t, err, target, msg, args...) +} + // NotErrorIs asserts that none of the errors in err's chain matches target. // This is a wrapper for errors.Is. func (a *Assertions) NotErrorIs(err error, target error, msgAndArgs ...interface{}) bool { diff --git a/assert/assertions.go b/assert/assertions.go index 104a0c936..44b854da6 100644 --- a/assert/assertions.go +++ b/assert/assertions.go @@ -2149,6 +2149,24 @@ func ErrorAs(t TestingT, err error, target interface{}, msgAndArgs ...interface{ ), msgAndArgs...) } +// NotErrorAs asserts that none of the errors in err's chain matches target, +// but if so, sets target to that error value. +func NotErrorAs(t TestingT, err error, target interface{}, msgAndArgs ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if !errors.As(err, target) { + return true + } + + chain := buildErrorChainString(err) + + return Fail(t, fmt.Sprintf("Target error should not be in err chain:\n"+ + "found: %q\n"+ + "in chain: %s", target, chain, + ), msgAndArgs...) +} + func buildErrorChainString(err error) string { if err == nil { return "" diff --git a/assert/assertions_test.go b/assert/assertions_test.go index 064b92f4a..20d63b108 100644 --- a/assert/assertions_test.go +++ b/assert/assertions_test.go @@ -3269,7 +3269,6 @@ func TestNotErrorIs(t *testing.T) { } func TestErrorAs(t *testing.T) { - mockT := new(testing.T) tests := []struct { err error result bool @@ -3282,9 +3281,38 @@ func TestErrorAs(t *testing.T) { tt := tt var target *customError t.Run(fmt.Sprintf("ErrorAs(%#v,%#v)", tt.err, target), func(t *testing.T) { + mockT := new(testing.T) res := ErrorAs(mockT, tt.err, &target) if res != tt.result { - t.Errorf("ErrorAs(%#v,%#v) should return %t)", tt.err, target, tt.result) + t.Errorf("ErrorAs(%#v,%#v) should return %t", tt.err, target, tt.result) + } + if res == mockT.Failed() { + t.Errorf("The test result (%t) should be reflected in the testing.T type (%t)", res, !mockT.Failed()) + } + }) + } +} + +func TestNotErrorAs(t *testing.T) { + tests := []struct { + err error + result bool + }{ + {fmt.Errorf("wrap: %w", &customError{}), false}, + {io.EOF, true}, + {nil, true}, + } + for _, tt := range tests { + tt := tt + var target *customError + t.Run(fmt.Sprintf("NotErrorAs(%#v,%#v)", tt.err, target), func(t *testing.T) { + mockT := new(testing.T) + res := NotErrorAs(mockT, tt.err, &target) + if res != tt.result { + t.Errorf("NotErrorAs(%#v,%#v) should not return %t", tt.err, target, tt.result) + } + if res == mockT.Failed() { + t.Errorf("The test result (%t) should be reflected in the testing.T type (%t)", res, !mockT.Failed()) } }) } diff --git a/require/require.go b/require/require.go index d0c73ff13..50ec19e13 100644 --- a/require/require.go +++ b/require/require.go @@ -1559,6 +1559,30 @@ func NotEqualf(t TestingT, expected interface{}, actual interface{}, msg string, t.FailNow() } +// NotErrorAs asserts that none of the errors in err's chain matches target, +// but if so, sets target to that error value. +func NotErrorAs(t TestingT, err error, target interface{}, msgAndArgs ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.NotErrorAs(t, err, target, msgAndArgs...) { + return + } + t.FailNow() +} + +// NotErrorAsf asserts that none of the errors in err's chain matches target, +// but if so, sets target to that error value. +func NotErrorAsf(t TestingT, err error, target interface{}, msg string, args ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.NotErrorAsf(t, err, target, msg, args...) { + return + } + t.FailNow() +} + // NotErrorIs asserts that none of the errors in err's chain matches target. // This is a wrapper for errors.Is. func NotErrorIs(t TestingT, err error, target error, msgAndArgs ...interface{}) { diff --git a/require/require_forward.go b/require/require_forward.go index 3c15ca36b..1bd87304f 100644 --- a/require/require_forward.go +++ b/require/require_forward.go @@ -1235,6 +1235,24 @@ func (a *Assertions) NotEqualf(expected interface{}, actual interface{}, msg str NotEqualf(a.t, expected, actual, msg, args...) } +// NotErrorAs asserts that none of the errors in err's chain matches target, +// but if so, sets target to that error value. +func (a *Assertions) NotErrorAs(err error, target interface{}, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + NotErrorAs(a.t, err, target, msgAndArgs...) +} + +// NotErrorAsf asserts that none of the errors in err's chain matches target, +// but if so, sets target to that error value. +func (a *Assertions) NotErrorAsf(err error, target interface{}, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + NotErrorAsf(a.t, err, target, msg, args...) +} + // NotErrorIs asserts that none of the errors in err's chain matches target. // This is a wrapper for errors.Is. func (a *Assertions) NotErrorIs(err error, target error, msgAndArgs ...interface{}) {