Skip to content

Instantly share code, notes, and snippets.

@veggiemonk
Last active December 17, 2022 00:09
Show Gist options
  • Save veggiemonk/e61da4af14cfda4e8f7f50c44526fe51 to your computer and use it in GitHub Desktop.
Save veggiemonk/e61da4af14cfda4e8f7f50c44526fe51 to your computer and use it in GitHub Desktop.
go testing framework with generics
package testutils
import "testing"
func equal[T comparable](a, b T) bool {
return a == b
}
func Equal[T comparable](t *testing.T, expected, actual T) {
if !equal(expected, actual) {
t.Fatalf("Expected %v, got %v", expected, actual)
}
}
func NotEqual[T comparable](t *testing.T, expected, actual T) {
if equal(expected, actual) {
t.Fatalf("Expected %v, got %v", expected, actual)
}
}
func Nil(t *testing.T, obj any) {
if obj != nil {
t.Fatalf("Expected nil, got %v", obj)
}
}
func NotNil(t *testing.T, obj any) {
if obj == nil {
t.Fatalf("Expected not nil, got nil")
}
}
func contains[T comparable](haystack []T, needle T) bool {
for _, item := range haystack {
if item == needle {
return true
}
}
return false
}
func Contains[T comparable](t *testing.T, haystack []T, needle T) {
if !contains(haystack, needle) {
t.Fatalf("Expected %v to contain %v", haystack, needle)
}
}
func NotContains[T comparable](t *testing.T, haystack []T, needle T) {
if contains(haystack, needle) {
t.Fatalf("Expected %v to not contain %v", haystack, needle)
}
}
func True(t *testing.T, condition bool) {
if !condition {
t.Fatalf("Expected true, got false")
}
}
func False(t *testing.T, condition bool) {
if condition {
t.Fatalf("Expected false, got true")
}
}
package testutils
import (
"path/filepath"
"reflect"
"runtime"
"strconv"
"strings"
"testing"
"github.com/google/go-cmp/cmp"
"github.com/google/go-cmp/cmp/cmpopts"
)
// Diff compares two items and returns a human-readable diff string. If the
// items are equal, the string is empty.
func Diff[T any](got, want T) string {
opts := cmp.Options{
cmp.Exporter(func(reflect.Type) bool { return true }),
cmpopts.EquateEmpty(),
}
diff := cmp.Diff(got, want, opts...)
if diff != "" {
return "\n-got +want\n" + diff
}
return ""
}
// Callers prints the stack trace of everything up til the line where Callers()
// was invoked.
func Callers() string {
var pc [50]uintptr
n := runtime.Callers(2, pc[:]) //nolint:gomnd // skip runtime.Callers + Callers
callsites := make([]string, 0, n)
frames := runtime.CallersFrames(pc[:n])
for frame, more := frames.Next(); more; frame, more = frames.Next() {
callsites = append(callsites, frame.File+":"+strconv.Itoa(frame.Line))
}
callsites = callsites[:len(callsites)-1] // skip testing.tRunner
if len(callsites) == 1 {
return ""
}
var b strings.Builder
for i := len(callsites) - 1; i >= 0; i-- {
if b.Len() > 0 {
b.WriteString(" -> ")
}
b.WriteString(filepath.Base(callsites[i]))
}
return "\n" + b.String() + ":"
}
func AssertEqual[C comparable](t *testing.T, got, want C) {
if diff := Diff(got, want); diff != "" {
t.Error(Callers(), diff)
}
}
// func TestExample(t *testing.T) {
// type TT struct {
// got string
// want string
// }
//
// assert := func(t *testing.T, tt TT) {
// if diff := Diff(tt.got, tt.want); diff != "" {
// t.Error(Callers(), diff)
// }
// }
//
// t.Run(
// "1", func(t *testing.T) {
// t.Parallel()
// assert(t, TT{"lorem ipsum dolor amet", "lorem ipsum dolor sit amet"})
// },
// )
//
// t.Run(
// "2", func(t *testing.T) {
// t.Parallel()
// assert(t, TT{"the quick fox jumped over lazy dog", "the quick brown fox jumped over the lazy dog"})
// },
// )
//
// t.Run(
// "2", func(t *testing.T) {
// t.Parallel()
// assert(t, TT{"Sphinx of black quartz judge my vow", "Sphinx of black quartz, judge my vow"})
// },
// )
// }
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment