New assertion: should.WrapError
ShouldWrap asserts that the first argument (which must be an error
value) 'wraps' the second/final argument (which must also be an error
value). It relies on errors.Is to make the determination.
See https://golang.org/pkg/errors/#Is
diff --git a/messages.go b/messages.go
index 72782b0..178d7e4 100644
--- a/messages.go
+++ b/messages.go
@@ -91,6 +91,8 @@
shouldBeError = "Expected an error value (but was '%v' instead)!"
shouldBeErrorInvalidComparisonValue = "The final argument to this assertion must be a string or an error value (you provided: '%v')."
+ shouldWrapInvalidTypes = "The first and last arguments to this assertion must both be error values (you provided: '%v' and '%v')."
+
shouldUseTimes = "You must provide time instances as arguments to this assertion."
shouldUseTimeSlice = "You must provide a slice of time instances as the first argument to this assertion."
shouldUseDurationAndTime = "You must provide a duration and a time as arguments to this assertion."
diff --git a/should/should.go b/should/should.go
index a5817ed..b85bf2c 100644
--- a/should/should.go
+++ b/should/should.go
@@ -67,4 +67,5 @@
PointTo = assertions.ShouldPointTo
Resemble = assertions.ShouldResemble
StartWith = assertions.ShouldStartWith
+ Wrap = assertions.ShouldWrap
)
diff --git a/type.go b/type.go
index d2d1dc8..1984fe8 100644
--- a/type.go
+++ b/type.go
@@ -1,6 +1,7 @@
package assertions
import (
+ "errors"
"fmt"
"reflect"
)
@@ -130,5 +131,24 @@
return ShouldEqual(fmt.Sprint(actual), fmt.Sprint(expected[0]))
}
+// ShouldWrap asserts that the first argument (which must be an error value)
+// 'wraps' the second/final argument (which must also be an error value).
+// It relies on errors.Is to make the determination (https://golang.org/pkg/errors/#Is).
+func ShouldWrap(actual interface{}, expected ...interface{}) string {
+ if fail := need(1, expected); fail != success {
+ return fail
+ }
+
+ if !isError(actual) || !isError(expected[0]) {
+ return fmt.Sprintf(shouldWrapInvalidTypes, reflect.TypeOf(actual), reflect.TypeOf(expected[0]))
+ }
+
+ if !errors.Is(actual.(error), expected[0].(error)) {
+ return fmt.Sprintf(`Expected error("%s") to wrap error("%s") but it didn't.`, actual, expected[0])
+ }
+
+ return success
+}
+
func isString(value interface{}) bool { _, ok := value.(string); return ok }
func isError(value interface{}) bool { _, ok := value.(error); return ok }
diff --git a/type_test.go b/type_test.go
index 25c3aae..4e0ba33 100644
--- a/type_test.go
+++ b/type_test.go
@@ -3,6 +3,7 @@
import (
"bytes"
"errors"
+ "fmt"
"io"
"net/http"
)
@@ -88,3 +89,21 @@
this.pass(so(error1, ShouldBeError, error1))
this.pass(so(error1, ShouldBeError, error1.Error()))
}
+
+func (this *AssertionsFixture) TestShouldWrapError() {
+ inner := fmt.Errorf("inner")
+ middle := fmt.Errorf("middle(%w)", inner)
+ outer := fmt.Errorf("outer(%w)", middle)
+
+ this.fail(so(outer, ShouldWrap, "too", "many"), "This assertion requires exactly 1 comparison values (you provided 2).")
+ this.fail(so(outer, ShouldWrap), "This assertion requires exactly 1 comparison values (you provided 0).")
+
+ this.fail(so(42, ShouldWrap, 42), "The first and last arguments to this assertion must both be error values (you provided: 'int' and 'int').")
+ this.fail(so(inner, ShouldWrap, 42), "The first and last arguments to this assertion must both be error values (you provided: '*errors.errorString' and 'int').")
+ this.fail(so(42, ShouldWrap, inner), "The first and last arguments to this assertion must both be error values (you provided: 'int' and '*errors.errorString').")
+
+ this.fail(so(inner, ShouldWrap, outer), `Expected error("inner") to wrap error("outer(middle(inner))") but it didn't.`)
+ this.pass(so(middle, ShouldWrap, inner))
+ this.pass(so(outer, ShouldWrap, middle))
+ this.pass(so(outer, ShouldWrap, inner))
+}