blob: e8cd901a4bb4a69b916ca1302c737ff976b561e0 [file] [log] [blame]
package assert
import (
"fmt"
"os"
"testing"
gocmp "github.com/google/go-cmp/cmp"
"gotest.tools/v3/assert/cmp"
)
type fakeTestingT struct {
failNowed bool
failed bool
msgs []string
}
func (f *fakeTestingT) FailNow() {
f.failNowed = true
}
func (f *fakeTestingT) Fail() {
f.failed = true
}
func (f *fakeTestingT) Log(args ...interface{}) {
f.msgs = append(f.msgs, args[0].(string))
}
func (f *fakeTestingT) Helper() {}
func TestAssert_WithBinaryExpression_Failures(t *testing.T) {
t.Run("equal", func(t *testing.T) {
fakeT := &fakeTestingT{}
Assert(fakeT, 1 == 6)
expectFailNowed(t, fakeT, "assertion failed: 1 is not 6")
})
t.Run("not equal", func(t *testing.T) {
fakeT := &fakeTestingT{}
a := 1
Assert(fakeT, a != 1)
expectFailNowed(t, fakeT, "assertion failed: a is 1")
})
t.Run("greater than", func(t *testing.T) {
fakeT := &fakeTestingT{}
Assert(fakeT, 1 > 5)
expectFailNowed(t, fakeT, "assertion failed: 1 is <= 5")
})
t.Run("less than", func(t *testing.T) {
fakeT := &fakeTestingT{}
Assert(fakeT, 5 < 1)
expectFailNowed(t, fakeT, "assertion failed: 5 is >= 1")
})
t.Run("greater than or equal", func(t *testing.T) {
fakeT := &fakeTestingT{}
Assert(fakeT, 1 >= 5)
expectFailNowed(t, fakeT, "assertion failed: 1 is less than 5")
})
t.Run("less than or equal", func(t *testing.T) {
fakeT := &fakeTestingT{}
Assert(fakeT, 6 <= 2)
expectFailNowed(t, fakeT, "assertion failed: 6 is greater than 2")
})
}
func TestAssertWithBoolIdent(t *testing.T) {
fakeT := &fakeTestingT{}
var ok bool
Assert(fakeT, ok)
expectFailNowed(t, fakeT, "assertion failed: ok is false")
}
func TestAssertWithBoolFailureNotEqual(t *testing.T) {
fakeT := &fakeTestingT{}
var err error
Assert(fakeT, err != nil)
expectFailNowed(t, fakeT, "assertion failed: err is nil")
}
func TestAssertWithBoolFailureNotTrue(t *testing.T) {
fakeT := &fakeTestingT{}
badNews := true
Assert(fakeT, !badNews)
expectFailNowed(t, fakeT, "assertion failed: badNews is true")
}
func TestAssertWithBoolFailureAndExtraMessage(t *testing.T) {
fakeT := &fakeTestingT{}
Assert(fakeT, 1 > 5, "sometimes things fail")
expectFailNowed(t, fakeT, "assertion failed: 1 is <= 5: sometimes things fail")
}
func TestAssertWithBoolSuccess(t *testing.T) {
fakeT := &fakeTestingT{}
Assert(fakeT, 1 < 5)
expectSuccess(t, fakeT)
}
func TestAssertWithBoolMultiLineFailure(t *testing.T) {
fakeT := &fakeTestingT{}
Assert(fakeT, func() bool {
for range []int{1, 2, 3, 4} {
}
return false
}())
expectFailNowed(t, fakeT, `assertion failed: expression is false: func() bool {
for range []int{1, 2, 3, 4} {
}
return false
}()`)
}
type exampleComparison struct {
success bool
message string
}
func (c exampleComparison) Compare() (bool, string) {
return c.success, c.message
}
func TestAssertWithComparisonSuccess(t *testing.T) {
fakeT := &fakeTestingT{}
cmp := exampleComparison{success: true}
Assert(fakeT, cmp.Compare)
expectSuccess(t, fakeT)
}
func TestAssertWithComparisonFailure(t *testing.T) {
fakeT := &fakeTestingT{}
cmp := exampleComparison{message: "oops, not good"}
Assert(fakeT, cmp.Compare)
expectFailNowed(t, fakeT, "assertion failed: oops, not good")
}
func TestAssertWithComparisonAndExtraMessage(t *testing.T) {
fakeT := &fakeTestingT{}
cmp := exampleComparison{message: "oops, not good"}
Assert(fakeT, cmp.Compare, "extra stuff %v", true)
expectFailNowed(t, fakeT, "assertion failed: oops, not good: extra stuff true")
}
type customError struct {
field bool
}
func (e *customError) Error() string {
// access a field of the receiver to simulate the behaviour of most
// implementations, and test handling of non-nil typed errors.
e.field = true
return "custom error"
}
func TestNilError(t *testing.T) {
t.Run("nil interface", func(t *testing.T) {
fakeT := &fakeTestingT{}
var err error
NilError(fakeT, err)
expectSuccess(t, fakeT)
})
t.Run("nil literal", func(t *testing.T) {
fakeT := &fakeTestingT{}
NilError(fakeT, nil)
expectSuccess(t, fakeT)
})
t.Run("interface with non-nil type", func(t *testing.T) {
fakeT := &fakeTestingT{}
var customErr *customError
NilError(fakeT, customErr)
expected := "assertion failed: error is not nil: error has type *assert.customError"
expectFailNowed(t, fakeT, expected)
})
t.Run("non-nil error", func(t *testing.T) {
fakeT := &fakeTestingT{}
NilError(fakeT, fmt.Errorf("this is the error"))
expectFailNowed(t, fakeT, "assertion failed: error is not nil: this is the error")
})
t.Run("non-nil error with struct type", func(t *testing.T) {
fakeT := &fakeTestingT{}
err := structError{}
NilError(fakeT, err)
expectFailNowed(t, fakeT, "assertion failed: error is not nil: this is a struct")
})
t.Run("non-nil error with map type", func(t *testing.T) {
fakeT := &fakeTestingT{}
var err mapError
NilError(fakeT, err)
expectFailNowed(t, fakeT, "assertion failed: error is not nil: ")
})
}
type structError struct{}
func (structError) Error() string {
return "this is a struct"
}
type mapError map[int]string
func (m mapError) Error() string {
return m[0]
}
func TestCheckFailure(t *testing.T) {
fakeT := &fakeTestingT{}
if Check(fakeT, 1 == 2) {
t.Error("expected check to return false on failure")
}
expectFailed(t, fakeT, "assertion failed: 1 is not 2")
}
func TestCheckSuccess(t *testing.T) {
fakeT := &fakeTestingT{}
if !Check(fakeT, true) {
t.Error("expected check to return true on success")
}
expectSuccess(t, fakeT)
}
func TestCheckEqualFailure(t *testing.T) {
fakeT := &fakeTestingT{}
actual, expected := 5, 9
Check(fakeT, cmp.Equal(actual, expected))
expectFailed(t, fakeT, "assertion failed: 5 (actual int) != 9 (expected int)")
}
func TestCheck_MultipleFunctionsOnTheSameLine(t *testing.T) {
fakeT := &fakeTestingT{}
f := func(b bool) {}
f(Check(fakeT, false))
// TODO: update the expected when there is a more correct fix
expectFailed(t, fakeT,
"assertion failed: but assert failed to find the expression to print")
}
func TestEqualSuccess(t *testing.T) {
fakeT := &fakeTestingT{}
Equal(fakeT, 1, 1)
expectSuccess(t, fakeT)
Equal(fakeT, "abcd", "abcd")
expectSuccess(t, fakeT)
}
func TestEqualFailure(t *testing.T) {
fakeT := &fakeTestingT{}
actual, expected := 1, 3
Equal(fakeT, actual, expected)
expectFailNowed(t, fakeT, "assertion failed: 1 (actual int) != 3 (expected int)")
}
func TestEqualFailureTypes(t *testing.T) {
fakeT := &fakeTestingT{}
Equal(fakeT, 3, uint(3))
expectFailNowed(t, fakeT, `assertion failed: 3 (int) != 3 (uint)`)
}
func TestEqualFailureWithSelectorArgument(t *testing.T) {
fakeT := &fakeTestingT{}
type tc struct {
expected string
}
var testcase = tc{expected: "foo"}
Equal(fakeT, "ok", testcase.expected)
expectFailNowed(t, fakeT,
"assertion failed: ok (string) != foo (testcase.expected string)")
}
func TestEqualFailureWithIndexExpr(t *testing.T) {
fakeT := &fakeTestingT{}
expected := map[string]string{"foo": "bar"}
Equal(fakeT, "ok", expected["foo"])
expectFailNowed(t, fakeT,
`assertion failed: ok (string) != bar (expected["foo"] string)`)
}
func TestEqualFailureWithCallExprArgument(t *testing.T) {
fakeT := &fakeTestingT{}
ce := customError{}
Equal(fakeT, "", ce.Error())
expectFailNowed(t, fakeT,
"assertion failed: (string) != custom error (string)")
}
func TestAssertFailureWithOfflineComparison(t *testing.T) {
fakeT := &fakeTestingT{}
a := 1
b := 2
// store comparison in a variable, so ast lookup can't find it
comparison := cmp.Equal(a, b)
Assert(fakeT, comparison)
// expected value wont have variable names
expectFailNowed(t, fakeT, "assertion failed: 1 (int) != 2 (int)")
}
type testingT interface {
Errorf(msg string, args ...interface{})
Fatalf(msg string, args ...interface{})
}
func expectFailNowed(t testingT, fakeT *fakeTestingT, expected string) {
if ht, ok := t.(helperT); ok {
ht.Helper()
}
if fakeT.failed {
t.Errorf("should not have failed, got messages %s", fakeT.msgs)
}
if !fakeT.failNowed {
t.Fatalf("should have failNowed with message %s", expected)
}
if fakeT.msgs[0] != expected {
t.Fatalf("should have failure message %q, got %q", expected, fakeT.msgs[0])
}
}
func expectFailed(t testingT, fakeT *fakeTestingT, expected string) {
if ht, ok := t.(helperT); ok {
ht.Helper()
}
if fakeT.failNowed {
t.Errorf("should not have failNowed, got messages %s", fakeT.msgs)
}
if !fakeT.failed {
t.Fatalf("should have failed with message %s", expected)
}
if fakeT.msgs[0] != expected {
t.Fatalf("should have failure message %q, got %q", expected, fakeT.msgs[0])
}
}
func expectSuccess(t testingT, fakeT *fakeTestingT) {
if ht, ok := t.(helperT); ok {
ht.Helper()
}
if fakeT.failNowed {
t.Errorf("should not have failNowed, got messages %s", fakeT.msgs)
}
if fakeT.failed {
t.Errorf("should not have failed, got messages %s", fakeT.msgs)
}
}
type stub struct {
a string
b int
}
func TestDeepEqualSuccess(t *testing.T) {
actual := stub{"ok", 1}
expected := stub{"ok", 1}
fakeT := &fakeTestingT{}
DeepEqual(fakeT, actual, expected, gocmp.AllowUnexported(stub{}))
expectSuccess(t, fakeT)
}
func TestDeepEqualFailure(t *testing.T) {
actual := stub{"ok", 1}
expected := stub{"ok", 2}
fakeT := &fakeTestingT{}
DeepEqual(fakeT, actual, expected, gocmp.AllowUnexported(stub{}))
if !fakeT.failNowed {
t.Fatal("should have failNowed")
}
}
func TestErrorFailure(t *testing.T) {
t.Run("nil error", func(t *testing.T) {
fakeT := &fakeTestingT{}
var err error
Error(fakeT, err, "this error")
expectFailNowed(t, fakeT, "assertion failed: expected an error, got nil")
})
t.Run("different error", func(t *testing.T) {
fakeT := &fakeTestingT{}
err := fmt.Errorf("the actual error")
Error(fakeT, err, "this error")
expected := `assertion failed: expected error "this error", got "the actual error"`
expectFailNowed(t, fakeT, expected)
})
}
func TestErrorContainsFailure(t *testing.T) {
t.Run("nil error", func(t *testing.T) {
fakeT := &fakeTestingT{}
var err error
ErrorContains(fakeT, err, "this error")
expectFailNowed(t, fakeT, "assertion failed: expected an error, got nil")
})
t.Run("different error", func(t *testing.T) {
fakeT := &fakeTestingT{}
err := fmt.Errorf("the actual error")
ErrorContains(fakeT, err, "this error")
expected := `assertion failed: expected error to contain "this error", got "the actual error"`
expectFailNowed(t, fakeT, expected)
})
}
func TestErrorTypeFailure(t *testing.T) {
t.Run("nil error", func(t *testing.T) {
fakeT := &fakeTestingT{}
var err error
ErrorType(fakeT, err, os.IsNotExist)
expectFailNowed(t, fakeT, "assertion failed: error is nil, not os.IsNotExist")
})
t.Run("different error", func(t *testing.T) {
fakeT := &fakeTestingT{}
err := fmt.Errorf("the actual error")
ErrorType(fakeT, err, os.IsNotExist)
expected := `assertion failed: error is the actual error (*errors.errorString), not os.IsNotExist`
expectFailNowed(t, fakeT, expected)
})
}
func TestErrorIs(t *testing.T) {
t.Run("nil error", func(t *testing.T) {
fakeT := &fakeTestingT{}
var err error
ErrorIs(fakeT, err, os.ErrNotExist)
expected := `assertion failed: error is nil, not "file does not exist" (os.ErrNotExist)`
expectFailNowed(t, fakeT, expected)
})
t.Run("different error", func(t *testing.T) {
fakeT := &fakeTestingT{}
err := fmt.Errorf("the actual error")
ErrorIs(fakeT, err, os.ErrNotExist)
expected := `assertion failed: error is "the actual error", not "file does not exist" (os.ErrNotExist)`
expectFailNowed(t, fakeT, expected)
})
t.Run("same error", func(t *testing.T) {
fakeT := &fakeTestingT{}
err := fmt.Errorf("some wrapping: %w", os.ErrNotExist)
ErrorIs(fakeT, err, os.ErrNotExist)
expectSuccess(t, fakeT)
})
}