version 1
This commit is contained in:
parent
f948ec8174
commit
e80b22c45a
46
README.md
46
README.md
|
@ -1,3 +1,47 @@
|
||||||
# Assert
|
# 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
86
contains.go
Normal 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
33
equal.go
Normal 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
41
errors.go
Normal 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
|
||||||
|
}
|
39
nil.go
Normal file
39
nil.go
Normal 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
7
test.go
Normal 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
128
test_test.go
Normal 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)
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user