version 1

This commit is contained in:
Sky Johnson 2025-02-15 16:52:59 -06:00
parent f948ec8174
commit e80b22c45a
9 changed files with 393 additions and 1 deletions

View File

@ -1,3 +1,47 @@
# Assert
Very simple tools for test assertions.
Very simple tools for test assertions. Why doesn't Go have assertions built in? Anyone's guess!
All credit to [Eduard Urbach](https://akyoto.dev). The only reason this package exists is to keep a local copy of it for myself, and to handle stylistic differences.
## Features
- Simple API; few brain cells required
- Tiny codebase; little overhead
- Zero dependencies; self-contained, built-in Go code. It's predictable!
## Installation
```shell
go get git.sharkk.net/Go/Assert
```
## Usage
```go
assert.Nil(t, nil)
assert.True(t, true)
assert.Equal(t, "Hello", "Hello")
assert.DeepEqual(t, "Hello", "Hello")
assert.Contains(t, "Hello", "ello")
```
## Tests
```
PASS: TestContains
PASS: TestNotContains
PASS: TestFailContains
PASS: TestFailNotContains
PASS: TestEqual
PASS: TestNotEqual
PASS: TestDeepEqual
PASS: TestFailEqual
PASS: TestFailNotEqual
PASS: TestFailDeepEqual
PASS: TestNil
PASS: TestNotNil
PASS: TestFailNil
PASS: TestFailNotNil
PASS: TestTrue
coverage: 100.0% of statements
```

86
contains.go Normal file
View File

@ -0,0 +1,86 @@
package assert
import (
"reflect"
"strings"
)
// Contains asserts that a contains b
func Contains(t test, a any, b any) {
if contains(a, b) {
return
}
t.Errorf(twoValues, file(), "Contains", a, b)
t.FailNow()
}
// NotContains asserts that a doesn't contain b
func NotContains(t test, a any, b any) {
if !contains(a, b) {
return
}
t.Errorf(twoValues, file(), "NotContains", a, b)
t.FailNow()
}
// contains returns whether container contains the given the element
// It works with strings, maps and slices
func contains(container any, element any) bool {
containerValue := reflect.ValueOf(container)
switch containerValue.Kind() {
case reflect.String:
elementValue := reflect.ValueOf(element)
return strings.Contains(containerValue.String(), elementValue.String())
case reflect.Map:
keys := containerValue.MapKeys()
for _, key := range keys {
if key.Interface() == element {
return true
}
}
case reflect.Slice:
elementValue := reflect.ValueOf(element)
if elementValue.Kind() == reflect.Slice {
elementLength := elementValue.Len()
if elementLength == 0 {
return true
}
if elementLength > containerValue.Len() {
return false
}
matchingElements := 0
for i := 0; i < containerValue.Len(); i++ {
if containerValue.Index(i).Interface() == elementValue.Index(matchingElements).Interface() {
matchingElements++
} else {
matchingElements = 0
}
if matchingElements == elementLength {
return true
}
}
return false
}
for i := 0; i < containerValue.Len(); i++ {
if containerValue.Index(i).Interface() == element {
return true
}
}
}
return false
}

33
equal.go Normal file
View File

@ -0,0 +1,33 @@
package assert
import "reflect"
// Equal asserts that the two given values are equal
func Equal[T comparable](t test, a T, b T) {
if a == b {
return
}
t.Errorf(twoValues, file(), "Equal", a, b)
t.FailNow()
}
// NotEqual asserts that the two given values are not equal
func NotEqual[T comparable](t test, a T, b T) {
if a != b {
return
}
t.Errorf(twoValues, file(), "NotEqual", a, b)
t.FailNow()
}
// DeepEqual asserts that the two given values are deeply equal
func DeepEqual[T any](t test, a T, b T) {
if reflect.DeepEqual(a, b) {
return
}
t.Errorf(twoValues, file(), "DeepEqual", a, b)
t.FailNow()
}

41
errors.go Normal file
View File

@ -0,0 +1,41 @@
package assert
import (
"runtime/debug"
"strings"
)
const oneValue = `
%s
assert.%s
%v
`
const twoValues = `
%s
assert.%s
%v
%v
`
// file returns the first line containing "_test.go" in the debug stack
func file() string {
stack := string(debug.Stack())
lines := strings.Split(stack, "\n")
name := ""
for _, line := range lines {
if strings.Contains(line, "_test.go") {
space := strings.LastIndex(line, " ")
if space != -1 {
line = line[:space]
}
name = strings.TrimSpace(line)
break
}
}
return name
}

3
go.mod Normal file
View File

@ -0,0 +1,3 @@
module git.sharkk.net/Go/Assert
go 1.23.5

39
nil.go Normal file
View File

@ -0,0 +1,39 @@
package assert
import "reflect"
// Nil asserts that the given value equals nil
func Nil(t test, a any) {
if isNil(a) {
return
}
t.Errorf(oneValue, file(), "Nil", a)
t.FailNow()
}
// NotNil asserts that the given value does not equal nil
func NotNil(t test, a any) {
if !isNil(a) {
return
}
t.Errorf(oneValue, file(), "NotNil", a)
t.FailNow()
}
// isNil returns true if the object is nil.
func isNil(object any) bool {
if object == nil {
return true
}
value := reflect.ValueOf(object)
switch value.Kind() {
case reflect.Chan, reflect.Func, reflect.Map, reflect.Ptr, reflect.UnsafePointer, reflect.Interface, reflect.Slice:
return value.IsNil()
}
return false
}

7
test.go Normal file
View File

@ -0,0 +1,7 @@
package assert
// test is the interface used for tests
type test interface {
Errorf(format string, args ...any)
FailNow()
}

128
test_test.go Normal file
View File

@ -0,0 +1,128 @@
package assert_test
import (
"testing"
assert "git.sharkk.net/Go/Assert"
)
type T struct{ A int }
// noop implements the Test interface with noop functions so you can test the failure cases
type noop struct{}
func (t *noop) Errorf(format string, args ...any) {}
func (t *noop) FailNow() {}
// fail creates a new test that is expected to fail
func fail(_ *testing.T) *noop {
return &noop{}
}
func TestEqual(t *testing.T) {
assert.Equal(t, 0, 0)
assert.Equal(t, "Hello", "Hello")
assert.Equal(t, T{A: 10}, T{A: 10})
}
func TestNotEqual(t *testing.T) {
assert.NotEqual(t, 0, 1)
assert.NotEqual(t, "Hello", "World")
assert.NotEqual(t, &T{A: 10}, &T{A: 10})
assert.NotEqual(t, T{A: 10}, T{A: 20})
}
func TestDeepEqual(t *testing.T) {
assert.DeepEqual(t, 0, 0)
assert.DeepEqual(t, "Hello", "Hello")
assert.DeepEqual(t, []byte("Hello"), []byte("Hello"))
assert.DeepEqual(t, T{A: 10}, T{A: 10})
assert.DeepEqual(t, &T{A: 10}, &T{A: 10})
}
func TestFailEqual(t *testing.T) {
assert.Equal(fail(t), 0, 1)
}
func TestFailNotEqual(t *testing.T) {
assert.NotEqual(fail(t), 0, 0)
}
func TestFailDeepEqual(t *testing.T) {
assert.DeepEqual(fail(t), "Hello", "World")
}
func TestTrue(t *testing.T) {
assert.True(t, true)
assert.False(t, false)
}
func TestContains(t *testing.T) {
assert.Contains(t, "Hello", "H")
assert.Contains(t, "Hello", "Hello")
assert.Contains(t, []string{"Hello", "World"}, "Hello")
assert.Contains(t, []int{1, 2, 3}, 2)
assert.Contains(t, []int{1, 2, 3}, []int{})
assert.Contains(t, []int{1, 2, 3}, []int{1, 2})
assert.Contains(t, []byte{'H', 'e', 'l', 'l', 'o'}, byte('e'))
assert.Contains(t, []byte{'H', 'e', 'l', 'l', 'o'}, []byte{'e', 'l'})
assert.Contains(t, map[string]int{"Hello": 1, "World": 2}, "Hello")
}
func TestNotContains(t *testing.T) {
assert.NotContains(t, "Hello", "h")
assert.NotContains(t, "Hello", "hello")
assert.NotContains(t, []string{"Hello", "World"}, "hello")
assert.NotContains(t, []int{1, 2, 3}, 4)
assert.NotContains(t, []int{1, 2, 3}, []int{2, 1})
assert.NotContains(t, []int{1, 2, 3}, []int{1, 2, 3, 4})
assert.NotContains(t, []byte{'H', 'e', 'l', 'l', 'o'}, byte('a'))
assert.NotContains(t, []byte{'H', 'e', 'l', 'l', 'o'}, []byte{'l', 'e'})
assert.NotContains(t, map[string]int{"Hello": 1, "World": 2}, "hello")
}
func TestFailContains(t *testing.T) {
assert.Contains(fail(t), "Hello", "h")
}
func TestFailNotContains(t *testing.T) {
assert.NotContains(fail(t), "Hello", "H")
}
func TestNil(t *testing.T) {
var (
nilPointer *T
nilInterface any
nilSlice []byte
nilMap map[byte]byte
nilChannel chan byte
nilFunction func()
)
assert.Nil(t, nil)
assert.Nil(t, nilPointer)
assert.Nil(t, nilInterface)
assert.Nil(t, nilSlice)
assert.Nil(t, nilMap)
assert.Nil(t, nilChannel)
assert.Nil(t, nilFunction)
}
func TestNotNil(t *testing.T) {
assert.NotNil(t, 0)
assert.NotNil(t, "Hello")
assert.NotNil(t, T{})
assert.NotNil(t, &T{})
assert.NotNil(t, make([]byte, 0))
assert.NotNil(t, make(map[byte]byte))
assert.NotNil(t, make(chan byte))
assert.NotNil(t, TestNotNil)
}
func TestFailNil(t *testing.T) {
assert.Nil(fail(t), 0)
}
func TestFailNotNil(t *testing.T) {
assert.NotNil(fail(t), nil)
}

11
true.go Normal file
View File

@ -0,0 +1,11 @@
package assert
// True asserts that the given value is true
func True(t test, a bool) {
Equal(t, a, true)
}
// False asserts that the given value is false
func False(t test, a bool) {
Equal(t, a, false)
}