changelog generator & diff tool
stormlightlabs.github.io/git-storm/
changelog
changeset
markdown
golang
git
1// package testutils contains assertions with struct [expect]
2//
3// Adapted from https://www.alexedwards.net/blog/the-9-go-test-assertions-i-use
4package testutils
5
6import (
7 "errors"
8 "fmt"
9 "reflect"
10 "regexp"
11 "testing"
12)
13
14type expect struct{}
15
16// Expect is the exported instance users call.
17var Expect = expect{}
18
19// Equal asserts that got and want are equal (using reflect.DeepEqual where necessary).
20func (e expect) Equal(t *testing.T, got, want any, args ...any) {
21 t.Helper()
22 if !isDeepEqual(got, want) {
23 t.Errorf("Equal assertion failed: got %#v; want %#v", got, want)
24 if len(args) > 0 {
25 t.Logf("Message: %v", fmt.Sprint(args...))
26 }
27 }
28}
29
30// NotEqual asserts that got and want are *not* equal.
31func (e expect) NotEqual(t *testing.T, got, want any, args ...any) {
32 t.Helper()
33 if isDeepEqual(got, want) {
34 t.Errorf("NotEqual assertion failed: got %#v; expected different value", got)
35 if len(args) > 0 {
36 t.Logf("Message: %v", fmt.Sprint(args...))
37 }
38 }
39}
40
41// True asserts that the boolean got is true.
42func (e expect) True(t *testing.T, got bool, args ...any) {
43 t.Helper()
44 if !got {
45 t.Errorf("True assertion failed: got false; want true")
46 if len(args) > 0 {
47 t.Logf("Message: %v", fmt.Sprint(args...))
48 }
49 }
50}
51
52// False asserts that the boolean got is false.
53func (e expect) False(t *testing.T, got bool, args ...any) {
54 t.Helper()
55 if got {
56 t.Errorf("False assertion failed: got true; want false")
57 if len(args) > 0 {
58 t.Logf("Message: %v", fmt.Sprint(args...))
59 }
60 }
61}
62
63// Nil asserts that got is nil.
64func (e expect) Nil(t *testing.T, got any, args ...any) {
65 t.Helper()
66 if !isNil(got) {
67 t.Errorf("Nil assertion failed: got non-nil value %#v", got)
68 if len(args) > 0 {
69 t.Logf("Message: %v", fmt.Sprint(args...))
70 }
71 }
72}
73
74// NotNil asserts that got is *not* nil.
75func (e expect) NotNil(t *testing.T, got any, args ...any) {
76 t.Helper()
77 if isNil(got) {
78 t.Errorf("NotNil assertion failed: got nil; want non-nil")
79 if len(args) > 0 {
80 t.Logf("Message: %v", fmt.Sprint(args...))
81 }
82 }
83}
84
85// ErrorIs asserts that err wraps or is target.
86func (e expect) ErrorIs(t *testing.T, err, target error, args ...any) {
87 t.Helper()
88 if !errors.Is(err, target) {
89 t.Errorf("ErrorIs assertion failed: got error %#v; want error matching %#v", err, target)
90 if len(args) > 0 {
91 t.Logf("Message: %v", fmt.Sprint(args...))
92 }
93 }
94}
95
96// ErrorAs asserts that err can be assigned to target via [errors.As].
97func (e expect) ErrorAs(t *testing.T, err error, target any, args ...any) {
98 t.Helper()
99 if err == nil {
100 t.Errorf("ErrorAs assertion failed: got nil; want assignable to %T", target)
101 return
102 }
103 if !errors.As(err, &target) {
104 t.Errorf("ErrorAs assertion failed: got error %#v; want assignable to %T", err, target)
105 if len(args) > 0 {
106 t.Logf("Message: %v", fmt.Sprint(args...))
107 }
108 }
109}
110
111// MatchesRegexp asserts that the string got matches the given regex pattern.
112func (e expect) MatchesRegexp(t *testing.T, got, pattern string, args ...any) {
113 t.Helper()
114 matched, err := regexp.MatchString(pattern, got)
115 if err != nil {
116 t.Fatalf("MatchesRegexp assertion: invalid pattern %q: %v", pattern, err)
117 return
118 }
119 if !matched {
120 t.Errorf("MatchesRegexp assertion failed: got %#v; want to match pattern %q", got, pattern)
121 if len(args) > 0 {
122 t.Logf("Message: %v", fmt.Sprint(args...))
123 }
124 }
125}
126
127// isDeepEqual handles deep equality including nil checks.
128func isDeepEqual(a, b any) bool {
129 if isNil(a) && isNil(b) {
130 return true
131 }
132 return reflect.DeepEqual(a, b)
133}
134
135// isNil tests nil for interface, pointer, slice, map, chan, func types.
136func isNil(v any) bool {
137 if v == nil {
138 return true
139 }
140 rv := reflect.ValueOf(v)
141 switch rv.Kind() {
142 case reflect.Chan, reflect.Func, reflect.Interface, reflect.Map, reflect.Pointer, reflect.Slice, reflect.UnsafePointer:
143 return rv.IsNil()
144 default:
145 return false
146 }
147}