version 1
This commit is contained in:
parent
f948ec8174
commit
e80b22c45a
46
README.md
46
README.md
|
@ -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
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