testerror/error.go (59 lines of code) (raw):
package testerror
import (
"errors"
"fmt"
"strings"
"testing"
)
// Error is a simple error type that allows us to chain checks using methods.
// However due to Go's way of handling interface types, when a nil *Error value is used as
// an error type (e.g. when passed into a func accepting an error) the underlying concrete
// type is *Error and it will not pass the usual error != nil check.
// Instead use the AsError() method to get a regular error type.
// Or use the reflect package `val := reflect.ValueOf(myCheckError); val.IsNil()`.
type Error struct {
msg string
}
func New(msg string) *Error {
return &Error{
msg: msg,
}
}
func Newf(format string, args ...any) *Error {
return &Error{
msg: fmt.Sprintf(format, args...),
}
}
// Implement Error interface
func (e *Error) Error() string {
return e.msg
}
// AsError returns a regular error type that can be used in the usual way.
// This fixes some issues when comparing nil types, which can fail as the underlying types are different.
// e.g. comparing the a nil error interface type to a nil *Error type from this package can fail.
func (e *Error) AsError() error {
if e == nil {
return nil
}
return errors.New(e.msg)
}
func (e *Error) ErrorIsNil(t *testing.T) {
if e != nil {
t.Error(e.msg)
}
}
func (e *Error) ErrorIsNilFatal(t *testing.T) {
if e != nil {
t.Fatal(e.msg)
}
}
func (e *Error) ErrorNotNil(t *testing.T) {
if e == nil {
t.Error("error is nil")
}
}
func (e *Error) ErrorNotNilFatal(t *testing.T) {
if e == nil {
t.Fatal("error is nil")
}
}
func (e *Error) ErrorContains(t *testing.T, substr string) {
if !strings.Contains(e.msg, substr) {
t.Errorf("error '%s' does not contain substring '%s'", e.msg, substr)
}
}
func (e *Error) ErrorNotContains(t *testing.T, substr string) {
if strings.Contains(e.msg, substr) {
t.Errorf("error '%s' does contain substring '%s'", e.msg, substr)
}
}