changelog generator & diff tool stormlightlabs.github.io/git-storm/
changelog changeset markdown golang git
at main 3.9 kB view raw
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}