├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── assert.go ├── assert_test.go ├── doc.go └── go.mod /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files, Static and Dynamic libs (Shared Objects) 2 | *.o 3 | *.a 4 | *.so 5 | 6 | # Folders 7 | _obj 8 | _test 9 | 10 | # Architecture specific extensions/prefixes 11 | *.[568vq] 12 | [568vq].out 13 | 14 | *.cgo1.go 15 | *.cgo2.c 16 | _cgo_defun.c 17 | _cgo_gotypes.go 18 | _cgo_export.* 19 | 20 | _testmain.go 21 | 22 | *.exe 23 | *.test 24 | *.prof 25 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | go: 3 | - 1.13.1 4 | - tip 5 | matrix: 6 | allow_failures: 7 | - go: tip 8 | 9 | notifications: 10 | email: 11 | recipients: dean.karn@gmail.com 12 | on_success: change 13 | on_failure: always 14 | 15 | # Only clone the most recent commit. 16 | git: 17 | depth: 1 18 | 19 | script: 20 | - go test -v -race ./... 21 | 22 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Dean Karn 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Package assert 2 | ============== 3 | 4 | [![Build Status](https://travis-ci.org/go-playground/assert.svg?branch=master)](https://travis-ci.org/go-playground/assert) 5 | [![GoDoc](https://godoc.org/github.com/go-playground/assert?status.svg)](https://godoc.org/gopkg.in/go-playground/assert.v1) 6 | 7 | Package assert is a Basic Assertion library used along side native go testing 8 | 9 | Installation 10 | ------------ 11 | 12 | Use go get. 13 | 14 | go get github.com/go-playground/assert 15 | 16 | Then import the assert package into your own code. 17 | 18 | import . "github.com/go-playground/assert/v2" 19 | 20 | Usage and documentation 21 | ------ 22 | 23 | Please see http://godoc.org/github.com/go-playground/assert for detailed usage docs. 24 | 25 | ##### Example: 26 | ```go 27 | package whatever 28 | 29 | import ( 30 | "errors" 31 | "testing" 32 | . "github.com/go-playground/assert/v2" 33 | ) 34 | 35 | func AssertCustomErrorHandler(t testing.TB, errs map[string]string, key, expected string) { 36 | val, ok := errs[key] 37 | 38 | // using EqualSkip and NotEqualSkip as building blocks for my custom Assert function 39 | EqualSkip(t, 2, ok, true) 40 | NotEqualSkip(t, 2, val, nil) 41 | EqualSkip(t, 2, val, expected) 42 | } 43 | 44 | func TestEqual(t *testing.T) { 45 | 46 | // error comes from your package/library 47 | err := errors.New("my error") 48 | NotEqual(t, err, nil) 49 | Equal(t, err.Error(), "my error") 50 | 51 | err = nil 52 | Equal(t, err, nil) 53 | 54 | fn := func() { 55 | panic("omg omg omg!") 56 | } 57 | 58 | PanicMatches(t, func() { fn() }, "omg omg omg!") 59 | PanicMatches(t, func() { panic("omg omg omg!") }, "omg omg omg!") 60 | 61 | // errs would have come from your package/library 62 | errs := map[string]string{} 63 | errs["Name"] = "User Name Invalid" 64 | errs["Email"] = "User Email Invalid" 65 | 66 | AssertCustomErrorHandler(t, errs, "Name", "User Name Invalid") 67 | AssertCustomErrorHandler(t, errs, "Email", "User Email Invalid") 68 | } 69 | ``` 70 | 71 | How to Contribute 72 | ------ 73 | Make a PR. 74 | 75 | I strongly encourage everyone whom creates a usefull custom assertion function to contribute them and 76 | help make this package even better. 77 | 78 | License 79 | ------ 80 | Distributed under MIT License, please see license file in code for more details. 81 | -------------------------------------------------------------------------------- /assert.go: -------------------------------------------------------------------------------- 1 | package assert 2 | 3 | import ( 4 | "fmt" 5 | "path" 6 | "reflect" 7 | "regexp" 8 | "runtime" 9 | "testing" 10 | ) 11 | 12 | // IsEqual returns whether val1 is equal to val2 taking into account Pointers, Interfaces and their underlying types 13 | func IsEqual(val1, val2 interface{}) bool { 14 | v1 := reflect.ValueOf(val1) 15 | v2 := reflect.ValueOf(val2) 16 | 17 | if v1.Kind() == reflect.Ptr { 18 | v1 = v1.Elem() 19 | } 20 | 21 | if v2.Kind() == reflect.Ptr { 22 | v2 = v2.Elem() 23 | } 24 | 25 | if !v1.IsValid() && !v2.IsValid() { 26 | return true 27 | } 28 | 29 | switch v1.Kind() { 30 | case reflect.Chan, reflect.Func, reflect.Interface, reflect.Map, reflect.Ptr, reflect.Slice: 31 | if v1.IsNil() { 32 | v1 = reflect.ValueOf(nil) 33 | } 34 | } 35 | 36 | switch v2.Kind() { 37 | case reflect.Chan, reflect.Func, reflect.Interface, reflect.Map, reflect.Ptr, reflect.Slice: 38 | if v2.IsNil() { 39 | v2 = reflect.ValueOf(nil) 40 | } 41 | } 42 | 43 | v1Underlying := reflect.Zero(reflect.TypeOf(v1)).Interface() 44 | v2Underlying := reflect.Zero(reflect.TypeOf(v2)).Interface() 45 | 46 | if v1 == v1Underlying { 47 | if v2 == v2Underlying { 48 | goto CASE4 49 | } else { 50 | goto CASE3 51 | } 52 | } else { 53 | if v2 == v2Underlying { 54 | goto CASE2 55 | } else { 56 | goto CASE1 57 | } 58 | } 59 | 60 | CASE1: 61 | return reflect.DeepEqual(v1.Interface(), v2.Interface()) 62 | CASE2: 63 | return reflect.DeepEqual(v1.Interface(), v2) 64 | CASE3: 65 | return reflect.DeepEqual(v1, v2.Interface()) 66 | CASE4: 67 | return reflect.DeepEqual(v1, v2) 68 | } 69 | 70 | // NotMatchRegex validates that value matches the regex, either string or *regex 71 | // and throws an error with line number 72 | func NotMatchRegex(t *testing.T, value string, regex interface{}) { 73 | NotMatchRegexSkip(t, 2, value, regex) 74 | } 75 | 76 | // NotMatchRegexSkip validates that value matches the regex, either string or *regex 77 | // and throws an error with line number 78 | // but the skip variable tells NotMatchRegexSkip how far back on the stack to report the error. 79 | // This is a building block to creating your own more complex validation functions. 80 | func NotMatchRegexSkip(t *testing.T, skip int, value string, regex interface{}) { 81 | 82 | if r, ok, err := regexMatches(regex, value); ok || err != nil { 83 | _, file, line, _ := runtime.Caller(skip) 84 | 85 | if err != nil { 86 | fmt.Printf("%s:%d %v error compiling regex %v\n", path.Base(file), line, value, r.String()) 87 | } else { 88 | fmt.Printf("%s:%d %v matches regex %v\n", path.Base(file), line, value, r.String()) 89 | } 90 | 91 | t.FailNow() 92 | } 93 | } 94 | 95 | // MatchRegex validates that value matches the regex, either string or *regex 96 | // and throws an error with line number 97 | func MatchRegex(t *testing.T, value string, regex interface{}) { 98 | MatchRegexSkip(t, 2, value, regex) 99 | } 100 | 101 | // MatchRegexSkip validates that value matches the regex, either string or *regex 102 | // and throws an error with line number 103 | // but the skip variable tells MatchRegexSkip how far back on the stack to report the error. 104 | // This is a building block to creating your own more complex validation functions. 105 | func MatchRegexSkip(t *testing.T, skip int, value string, regex interface{}) { 106 | 107 | if r, ok, err := regexMatches(regex, value); !ok { 108 | _, file, line, _ := runtime.Caller(skip) 109 | 110 | if err != nil { 111 | fmt.Printf("%s:%d %v error compiling regex %v\n", path.Base(file), line, value, r.String()) 112 | } else { 113 | fmt.Printf("%s:%d %v does not match regex %v\n", path.Base(file), line, value, r.String()) 114 | } 115 | 116 | t.FailNow() 117 | } 118 | } 119 | 120 | func regexMatches(regex interface{}, value string) (*regexp.Regexp, bool, error) { 121 | 122 | var err error 123 | 124 | r, ok := regex.(*regexp.Regexp) 125 | 126 | // must be a string 127 | if !ok { 128 | if r, err = regexp.Compile(regex.(string)); err != nil { 129 | return r, false, err 130 | } 131 | } 132 | 133 | return r, r.MatchString(value), err 134 | } 135 | 136 | // Equal validates that val1 is equal to val2 and throws an error with line number 137 | func Equal(t testing.TB, val1, val2 interface{}) { 138 | EqualSkip(t, 2, val1, val2) 139 | } 140 | 141 | // EqualSkip validates that val1 is equal to val2 and throws an error with line number 142 | // but the skip variable tells EqualSkip how far back on the stack to report the error. 143 | // This is a building block to creating your own more complex validation functions. 144 | func EqualSkip(t testing.TB, skip int, val1, val2 interface{}) { 145 | 146 | if !IsEqual(val1, val2) { 147 | _, file, line, _ := runtime.Caller(skip) 148 | fmt.Printf("%s:%d %v does not equal %v\n", path.Base(file), line, val1, val2) 149 | t.FailNow() 150 | } 151 | } 152 | 153 | // NotEqual validates that val1 is not equal val2 and throws an error with line number 154 | func NotEqual(t testing.TB, val1, val2 interface{}) { 155 | NotEqualSkip(t, 2, val1, val2) 156 | } 157 | 158 | // NotEqualSkip validates that val1 is not equal to val2 and throws an error with line number 159 | // but the skip variable tells NotEqualSkip how far back on the stack to report the error. 160 | // This is a building block to creating your own more complex validation functions. 161 | func NotEqualSkip(t testing.TB, skip int, val1, val2 interface{}) { 162 | 163 | if IsEqual(val1, val2) { 164 | _, file, line, _ := runtime.Caller(skip) 165 | fmt.Printf("%s:%d %v should not be equal %v\n", path.Base(file), line, val1, val2) 166 | t.FailNow() 167 | } 168 | } 169 | 170 | // PanicMatches validates that the panic output of running fn matches the supplied string 171 | func PanicMatches(t testing.TB, fn func(), matches string) { 172 | PanicMatchesSkip(t, 2, fn, matches) 173 | } 174 | 175 | // PanicMatchesSkip validates that the panic output of running fn matches the supplied string 176 | // but the skip variable tells PanicMatchesSkip how far back on the stack to report the error. 177 | // This is a building block to creating your own more complex validation functions. 178 | func PanicMatchesSkip(t testing.TB, skip int, fn func(), matches string) { 179 | 180 | _, file, line, _ := runtime.Caller(skip) 181 | 182 | defer func() { 183 | if r := recover(); r != nil { 184 | err := fmt.Sprintf("%s", r) 185 | 186 | if err != matches { 187 | fmt.Printf("%s:%d Panic... expected [%s] received [%s]", path.Base(file), line, matches, err) 188 | t.FailNow() 189 | } 190 | } else { 191 | fmt.Printf("%s:%d Panic Expected, none found... expected [%s]", path.Base(file), line, matches) 192 | t.FailNow() 193 | } 194 | }() 195 | 196 | fn() 197 | } 198 | -------------------------------------------------------------------------------- /assert_test.go: -------------------------------------------------------------------------------- 1 | package assert 2 | 3 | import ( 4 | "errors" 5 | "testing" 6 | ) 7 | 8 | // NOTES: 9 | // - Run "go test" to run tests 10 | // - Run "gocov test | gocov report" to report on test converage by file 11 | // - Run "gocov test | gocov annotate -" to report on all code and functions, those ,marked with "MISS" were never called 12 | // 13 | // or 14 | // 15 | // -- may be a good idea to change to output path to somewherelike /tmp 16 | // go test -coverprofile cover.out && go tool cover -html=cover.out -o cover.html 17 | // 18 | 19 | func MyCustomErrorHandler(t testing.TB, errs map[string]string, key, expected string) { 20 | val, ok := errs[key] 21 | EqualSkip(t, 2, ok, true) 22 | NotEqualSkip(t, 2, val, nil) 23 | EqualSkip(t, 2, val, expected) 24 | } 25 | 26 | func TestRegexMatchAndNotMatch(t *testing.T) { 27 | goodRegex := "^(.*/vendor/)?github.com/go-playground/assert$" 28 | 29 | MatchRegex(t, "github.com/go-playground/assert", goodRegex) 30 | MatchRegex(t, "/vendor/github.com/go-playground/assert", goodRegex) 31 | 32 | NotMatchRegex(t, "/vendor/github.com/go-playground/test", goodRegex) 33 | } 34 | 35 | func TestBasicAllGood(t *testing.T) { 36 | 37 | err := errors.New("my error") 38 | NotEqual(t, err, nil) 39 | Equal(t, err.Error(), "my error") 40 | 41 | err = nil 42 | Equal(t, err, nil) 43 | 44 | fn := func() { 45 | panic("omg omg omg!") 46 | } 47 | 48 | PanicMatches(t, func() { fn() }, "omg omg omg!") 49 | PanicMatches(t, func() { panic("omg omg omg!") }, "omg omg omg!") 50 | 51 | /* if you uncomment creates hard fail, that is expected 52 | // you cant really do this, but it is here for the sake of completeness 53 | fun := func() {} 54 | PanicMatches(t, func() { fun() }, "omg omg omg!") 55 | */ 56 | errs := map[string]string{} 57 | errs["Name"] = "User Name Invalid" 58 | errs["Email"] = "User Email Invalid" 59 | 60 | MyCustomErrorHandler(t, errs, "Name", "User Name Invalid") 61 | MyCustomErrorHandler(t, errs, "Email", "User Email Invalid") 62 | } 63 | 64 | func TestEquals(t *testing.T) { 65 | 66 | type Test struct { 67 | Name string 68 | } 69 | 70 | tst := &Test{ 71 | Name: "joeybloggs", 72 | } 73 | 74 | Equal(t, tst, tst) 75 | 76 | NotEqual(t, tst, nil) 77 | NotEqual(t, nil, tst) 78 | 79 | type TestMap map[string]string 80 | 81 | var tm TestMap 82 | 83 | Equal(t, tm, nil) 84 | Equal(t, nil, tm) 85 | 86 | var iface interface{} 87 | var iface2 interface{} 88 | 89 | iface = 1 90 | Equal(t, iface, 1) 91 | NotEqual(t, iface, iface2) 92 | } 93 | -------------------------------------------------------------------------------- /doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | Package assert provides some basic assertion functions for testing and 3 | also provides the building blocks for creating your own more complex 4 | validations. 5 | 6 | package whatever 7 | 8 | import ( 9 | "errors" 10 | "testing" 11 | . "github.com/go-playground/assert.v1" 12 | ) 13 | 14 | func AssertCustomErrorHandler(t testing.TB, errs map[string]string, key, expected string) { 15 | val, ok := errs[key] 16 | 17 | // using EqualSkip and NotEqualSkip as building blocks for my custom Assert function 18 | EqualSkip(t, 2, ok, true) 19 | NotEqualSkip(t, 2, val, nil) 20 | EqualSkip(t, 2, val, expected) 21 | } 22 | 23 | func TestEqual(t *testing.T) { 24 | 25 | // error comes from your package/library 26 | err := errors.New("my error") 27 | NotEqual(t, err, nil) 28 | Equal(t, err.Error(), "my error") 29 | 30 | err = nil 31 | Equal(t, err, nil) 32 | 33 | fn := func() { 34 | panic("omg omg omg!") 35 | } 36 | 37 | PanicMatches(t, func() { fn() }, "omg omg omg!") 38 | PanicMatches(t, func() { panic("omg omg omg!") }, "omg omg omg!") 39 | 40 | // errs would have come from your package/library 41 | errs := map[string]string{} 42 | errs["Name"] = "User Name Invalid" 43 | errs["Email"] = "User Email Invalid" 44 | 45 | AssertCustomErrorHandler(t, errs, "Name", "User Name Invalid") 46 | AssertCustomErrorHandler(t, errs, "Email", "User Email Invalid") 47 | } 48 | */ 49 | package assert 50 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/go-playground/assert/v2 2 | 3 | go 1.13 4 | --------------------------------------------------------------------------------