From bff98a43e46221ec620f2a629bb8c2e65157376b Mon Sep 17 00:00:00 2001 From: Brandon Dyck Date: Sun, 8 Sep 2024 23:51:02 -0600 Subject: [PATCH] test: added ErrorAs --- README.md | 2 ++ examples_test.go | 11 +++++++++++ internal/assertions/assertions.go | 17 ++++++++++++++++ must/examples_test.go | 11 +++++++++++ must/must.go | 7 +++++++ must/must_test.go | 32 +++++++++++++++++++++++++++++++ test.go | 7 +++++++ test_test.go | 32 +++++++++++++++++++++++++++++++ 8 files changed, 119 insertions(+) diff --git a/README.md b/README.md index 8e020aa..72810e6 100644 --- a/README.md +++ b/README.md @@ -18,6 +18,8 @@ There are five key packages, - `portal` - utilities for allocating free ports for network listeners in tests ### Changes +:ballot_box_with_check: v1.11.0 adds an ErrorAs helper + :ballot_box_with_check: v1.10.0 adds a `util` package for helpers that return values - Adds ability to create and automatically clean up temporary files diff --git a/examples_test.go b/examples_test.go index 93f368e..32a2d91 100644 --- a/examples_test.go +++ b/examples_test.go @@ -318,6 +318,17 @@ func ExampleErrorIs() { // Output: } +func ExampleErrorAs() { + e1 := errors.New("e1") + e2 := FakeError("foo") + e3 := errors.New("e3") + errorChain := errors.Join(e1, e2, e3) + var target FakeError + ErrorAs(t, errorChain, &target) + fmt.Println(target.Error()) + // Output: foo +} + func ExampleFalse() { False(t, 1 == int('a')) // Output: diff --git a/internal/assertions/assertions.go b/internal/assertions/assertions.go index 2df2bd9..e6c5f8a 100644 --- a/internal/assertions/assertions.go +++ b/internal/assertions/assertions.go @@ -162,6 +162,23 @@ func ErrorIs(err error, target error) (s string) { return } +func ErrorAs[E error, Target *E](err error, target Target) (s string) { + if err == nil { + s = "expected error; got nil\n" + return + } + if target == nil { + s = "expected target not to be nil" + return + } + if !errors.As(err, target) { + s = "expected errors.As match\n" + s += bullet(" error: %v\n", err) + s += bullet("target: %v\n", target) + } + return +} + func NoError(err error) (s string) { if err != nil { s = "expected nil error\n" diff --git a/must/examples_test.go b/must/examples_test.go index f58ae32..b23238c 100644 --- a/must/examples_test.go +++ b/must/examples_test.go @@ -320,6 +320,17 @@ func ExampleErrorIs() { // Output: } +func ExampleErrorAs() { + e1 := errors.New("e1") + e2 := FakeError("foo") + e3 := errors.New("e3") + errorChain := errors.Join(e1, e2, e3) + var target FakeError + ErrorAs(t, errorChain, &target) + fmt.Println(target.Error()) + // Output: foo +} + func ExampleFalse() { False(t, 1 == int('a')) // Output: diff --git a/must/must.go b/must/must.go index 6473d01..62fd7e3 100644 --- a/must/must.go +++ b/must/must.go @@ -69,6 +69,13 @@ func ErrorIs(t T, err error, target error, settings ...Setting) { invoke(t, assertions.ErrorIs(err, target), settings...) } +// ErrorAs asserts err's tree contains an error that matches target. +// If so, it sets target to the error value. +func ErrorAs[E error, Target *E](t T, err error, target Target, settings ...Setting) { + t.Helper() + invoke(t, assertions.ErrorAs(err, target), settings...) +} + // NoError asserts err is a nil error. func NoError(t T, err error, settings ...Setting) { t.Helper() diff --git a/must/must_test.go b/must/must_test.go index db112fb..56aabb0 100644 --- a/must/must_test.go +++ b/must/must_test.go @@ -165,6 +165,38 @@ func TestErrorIs_nil(t *testing.T) { ErrorIs(tc, nil, err) } +type FakeError string + +func (e FakeError) Error() string { + return string(e) +} + +func TestErrorAs(t *testing.T) { + tc := newCase(t, `expected errors.As match`) + t.Cleanup(tc.assert) + + var target FakeError + e := errors.New("foo") + ErrorAs(tc, e, &target) +} + +func TestErrorAs_nilErr(t *testing.T) { + tc := newCase(t, `expected error; got nil`) + t.Cleanup(tc.assert) + + var target FakeError + ErrorAs(tc, nil, &target) +} + +func TestErrorAs_nilTarget(t *testing.T) { + tc := newCase(t, `expected target not to be nil`) + t.Cleanup(tc.assert) + + var target *FakeError + e := errors.New("foo") + ErrorAs(tc, e, target) +} + func TestNoError(t *testing.T) { tc := newCase(t, `expected nil error`) t.Cleanup(tc.assert) diff --git a/test.go b/test.go index bce3e52..33c0dec 100644 --- a/test.go +++ b/test.go @@ -67,6 +67,13 @@ func ErrorIs(t T, err error, target error, settings ...Setting) { invoke(t, assertions.ErrorIs(err, target), settings...) } +// ErrorAs asserts err's tree contains an error that matches target. +// If so, it sets target to the error value. +func ErrorAs[E error, Target *E](t T, err error, target Target, settings ...Setting) { + t.Helper() + invoke(t, assertions.ErrorAs(err, target), settings...) +} + // NoError asserts err is a nil error. func NoError(t T, err error, settings ...Setting) { t.Helper() diff --git a/test_test.go b/test_test.go index f784a42..0f76dcd 100644 --- a/test_test.go +++ b/test_test.go @@ -163,6 +163,38 @@ func TestErrorIs_nil(t *testing.T) { ErrorIs(tc, nil, err) } +type FakeError string + +func (e FakeError) Error() string { + return string(e) +} + +func TestErrorAs(t *testing.T) { + tc := newCase(t, `expected errors.As match`) + t.Cleanup(tc.assert) + + var target FakeError + e := errors.New("foo") + ErrorAs(tc, e, &target) +} + +func TestErrorAs_nilErr(t *testing.T) { + tc := newCase(t, `expected error; got nil`) + t.Cleanup(tc.assert) + + var target FakeError + ErrorAs(tc, nil, &target) +} + +func TestErrorAs_nilTarget(t *testing.T) { + tc := newCase(t, `expected target not to be nil`) + t.Cleanup(tc.assert) + + var target *FakeError + e := errors.New("foo") + ErrorAs(tc, e, target) +} + func TestNoError(t *testing.T) { tc := newCase(t, `expected nil error`) t.Cleanup(tc.assert)