Skip to content

Commit f784abc

Browse files
Merge pull request #1345 from craig65535/fix-assert-erroras
assert.ErrorAs: log target type
2 parents 7c367bb + c60c3bd commit f784abc

File tree

2 files changed

+99
-39
lines changed

2 files changed

+99
-39
lines changed

assert/assertions.go

Lines changed: 36 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2102,7 +2102,7 @@ func ErrorIs(t TestingT, err, target error, msgAndArgs ...interface{}) bool {
21022102
expectedText = target.Error()
21032103
}
21042104

2105-
chain := buildErrorChainString(err)
2105+
chain := buildErrorChainString(err, false)
21062106

21072107
return Fail(t, fmt.Sprintf("Target error should be in err chain:\n"+
21082108
"expected: %q\n"+
@@ -2125,7 +2125,7 @@ func NotErrorIs(t TestingT, err, target error, msgAndArgs ...interface{}) bool {
21252125
expectedText = target.Error()
21262126
}
21272127

2128-
chain := buildErrorChainString(err)
2128+
chain := buildErrorChainString(err, false)
21292129

21302130
return Fail(t, fmt.Sprintf("Target error should not be in err chain:\n"+
21312131
"found: %q\n"+
@@ -2143,11 +2143,11 @@ func ErrorAs(t TestingT, err error, target interface{}, msgAndArgs ...interface{
21432143
return true
21442144
}
21452145

2146-
chain := buildErrorChainString(err)
2146+
chain := buildErrorChainString(err, true)
21472147

21482148
return Fail(t, fmt.Sprintf("Should be in error chain:\n"+
2149-
"expected: %q\n"+
2150-
"in chain: %s", target, chain,
2149+
"expected: %s\n"+
2150+
"in chain: %s", reflect.ValueOf(target).Elem().Type(), chain,
21512151
), msgAndArgs...)
21522152
}
21532153

@@ -2161,24 +2161,46 @@ func NotErrorAs(t TestingT, err error, target interface{}, msgAndArgs ...interfa
21612161
return true
21622162
}
21632163

2164-
chain := buildErrorChainString(err)
2164+
chain := buildErrorChainString(err, true)
21652165

21662166
return Fail(t, fmt.Sprintf("Target error should not be in err chain:\n"+
2167-
"found: %q\n"+
2168-
"in chain: %s", target, chain,
2167+
"found: %s\n"+
2168+
"in chain: %s", reflect.ValueOf(target).Elem().Type(), chain,
21692169
), msgAndArgs...)
21702170
}
21712171

2172-
func buildErrorChainString(err error) string {
2172+
func unwrapAll(err error) (errs []error) {
2173+
errs = append(errs, err)
2174+
switch x := err.(type) {
2175+
case interface{ Unwrap() error }:
2176+
err = x.Unwrap()
2177+
if err == nil {
2178+
return
2179+
}
2180+
errs = append(errs, unwrapAll(err)...)
2181+
case interface{ Unwrap() []error }:
2182+
for _, err := range x.Unwrap() {
2183+
errs = append(errs, unwrapAll(err)...)
2184+
}
2185+
}
2186+
return
2187+
}
2188+
2189+
func buildErrorChainString(err error, withType bool) string {
21732190
if err == nil {
21742191
return ""
21752192
}
21762193

2177-
e := errors.Unwrap(err)
2178-
chain := fmt.Sprintf("%q", err.Error())
2179-
for e != nil {
2180-
chain += fmt.Sprintf("\n\t%q", e.Error())
2181-
e = errors.Unwrap(e)
2194+
var chain string
2195+
errs := unwrapAll(err)
2196+
for i := range errs {
2197+
if i != 0 {
2198+
chain += "\n\t"
2199+
}
2200+
chain += fmt.Sprintf("%q", errs[i].Error())
2201+
if withType {
2202+
chain += fmt.Sprintf(" (%T)", errs[i])
2203+
}
21822204
}
21832205
return chain
21842206
}

assert/assertions_test.go

Lines changed: 63 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -3175,11 +3175,13 @@ func parseLabeledOutput(output string) []labeledContent {
31753175
}
31763176

31773177
type captureTestingT struct {
3178-
msg string
3178+
failed bool
3179+
msg string
31793180
}
31803181

31813182
func (ctt *captureTestingT) Errorf(format string, args ...interface{}) {
31823183
ctt.msg = fmt.Sprintf(format, args...)
3184+
ctt.failed = true
31833185
}
31843186

31853187
func (ctt *captureTestingT) checkResultAndErrMsg(t *testing.T, expectedRes, res bool, expectedErrMsg string) {
@@ -3188,6 +3190,10 @@ func (ctt *captureTestingT) checkResultAndErrMsg(t *testing.T, expectedRes, res
31883190
t.Errorf("Should return %t", expectedRes)
31893191
return
31903192
}
3193+
if res == ctt.failed {
3194+
t.Errorf("The test result (%t) should be reflected in the testing.T type (%t)", res, !ctt.failed)
3195+
return
3196+
}
31913197
contents := parseLabeledOutput(ctt.msg)
31923198
if res == true {
31933199
if contents != nil {
@@ -3348,50 +3354,82 @@ func TestNotErrorIs(t *testing.T) {
33483354

33493355
func TestErrorAs(t *testing.T) {
33503356
tests := []struct {
3351-
err error
3352-
result bool
3357+
err error
3358+
result bool
3359+
resultErrMsg string
33533360
}{
3354-
{fmt.Errorf("wrap: %w", &customError{}), true},
3355-
{io.EOF, false},
3356-
{nil, false},
3361+
{
3362+
err: fmt.Errorf("wrap: %w", &customError{}),
3363+
result: true,
3364+
},
3365+
{
3366+
err: io.EOF,
3367+
result: false,
3368+
resultErrMsg: "" +
3369+
"Should be in error chain:\n" +
3370+
"expected: *assert.customError\n" +
3371+
"in chain: \"EOF\" (*errors.errorString)\n",
3372+
},
3373+
{
3374+
err: nil,
3375+
result: false,
3376+
resultErrMsg: "" +
3377+
"Should be in error chain:\n" +
3378+
"expected: *assert.customError\n" +
3379+
"in chain: \n",
3380+
},
3381+
{
3382+
err: fmt.Errorf("abc: %w", errors.New("def")),
3383+
result: false,
3384+
resultErrMsg: "" +
3385+
"Should be in error chain:\n" +
3386+
"expected: *assert.customError\n" +
3387+
"in chain: \"abc: def\" (*fmt.wrapError)\n" +
3388+
"\t\"def\" (*errors.errorString)\n",
3389+
},
33573390
}
33583391
for _, tt := range tests {
33593392
tt := tt
33603393
var target *customError
33613394
t.Run(fmt.Sprintf("ErrorAs(%#v,%#v)", tt.err, target), func(t *testing.T) {
3362-
mockT := new(testing.T)
3395+
mockT := new(captureTestingT)
33633396
res := ErrorAs(mockT, tt.err, &target)
3364-
if res != tt.result {
3365-
t.Errorf("ErrorAs(%#v,%#v) should return %t", tt.err, target, tt.result)
3366-
}
3367-
if res == mockT.Failed() {
3368-
t.Errorf("The test result (%t) should be reflected in the testing.T type (%t)", res, !mockT.Failed())
3369-
}
3397+
mockT.checkResultAndErrMsg(t, tt.result, res, tt.resultErrMsg)
33703398
})
33713399
}
33723400
}
33733401

33743402
func TestNotErrorAs(t *testing.T) {
33753403
tests := []struct {
3376-
err error
3377-
result bool
3404+
err error
3405+
result bool
3406+
resultErrMsg string
33783407
}{
3379-
{fmt.Errorf("wrap: %w", &customError{}), false},
3380-
{io.EOF, true},
3381-
{nil, true},
3408+
{
3409+
err: fmt.Errorf("wrap: %w", &customError{}),
3410+
result: false,
3411+
resultErrMsg: "" +
3412+
"Target error should not be in err chain:\n" +
3413+
"found: *assert.customError\n" +
3414+
"in chain: \"wrap: fail\" (*fmt.wrapError)\n" +
3415+
"\t\"fail\" (*assert.customError)\n",
3416+
},
3417+
{
3418+
err: io.EOF,
3419+
result: true,
3420+
},
3421+
{
3422+
err: nil,
3423+
result: true,
3424+
},
33823425
}
33833426
for _, tt := range tests {
33843427
tt := tt
33853428
var target *customError
33863429
t.Run(fmt.Sprintf("NotErrorAs(%#v,%#v)", tt.err, target), func(t *testing.T) {
3387-
mockT := new(testing.T)
3430+
mockT := new(captureTestingT)
33883431
res := NotErrorAs(mockT, tt.err, &target)
3389-
if res != tt.result {
3390-
t.Errorf("NotErrorAs(%#v,%#v) should not return %t", tt.err, target, tt.result)
3391-
}
3392-
if res == mockT.Failed() {
3393-
t.Errorf("The test result (%t) should be reflected in the testing.T type (%t)", res, !mockT.Failed())
3394-
}
3432+
mockT.checkResultAndErrMsg(t, tt.result, res, tt.resultErrMsg)
33953433
})
33963434
}
33973435
}

0 commit comments

Comments
 (0)