1[![GoDoc](https://godoc.org/github.com/frankban/quicktest?status.svg)](https://godoc.org/github.com/frankban/quicktest) 2[![Build Status](https://github.com/frankban/quicktest/actions/workflows/ci.yaml/badge.svg)](https://github.com/frankban/quicktest/actions/workflows/ci.yaml) 3 4[//]: # (Generated with: godocdown -template=.godocdown.template -o README.md) 5 6# quicktest 7 8`go get github.com/frankban/quicktest` 9 10Package quicktest provides a collection of Go helpers for writing tests. 11 12Quicktest helpers can be easily integrated inside regular Go tests, for 13instance: 14 15 import qt "github.com/frankban/quicktest" 16 17 func TestFoo(t *testing.T) { 18 t.Run("numbers", func(t *testing.T) { 19 c := qt.New(t) 20 numbers, err := somepackage.Numbers() 21 c.Assert(numbers, qt.DeepEquals, []int{42, 47}) 22 c.Assert(err, qt.ErrorMatches, "bad wolf") 23 }) 24 t.Run("nil", func(t *testing.T) { 25 c := qt.New(t) 26 got := somepackage.MaybeNil() 27 c.Assert(got, qt.IsNil, qt.Commentf("value: %v", somepackage.Value)) 28 }) 29 } 30 31 32### Assertions 33 34An assertion looks like this, where qt.Equals could be replaced by any available 35checker. If the assertion fails, the underlying Fatal method is called to 36describe the error and abort the test. 37 38 c := qt.New(t) 39 c.Assert(someValue, qt.Equals, wantValue) 40 41If you don’t want to abort on failure, use Check instead, which calls Error 42instead of Fatal: 43 44 c.Check(someValue, qt.Equals, wantValue) 45 46For really short tests, the extra line for instantiating *qt.C can be avoided: 47 48 qt.Assert(t, someValue, qt.Equals, wantValue) 49 qt.Check(t, someValue, qt.Equals, wantValue) 50 51The library provides some base checkers like Equals, DeepEquals, Matches, 52ErrorMatches, IsNil and others. More can be added by implementing the Checker 53interface. Below, we list the checkers implemented by the package in 54alphabetical order. 55 56 57### All 58 59All returns a Checker that uses the given checker to check elements of slice or 60array or the values of a map. It succeeds if all elements pass the check. On 61failure it prints the error from the first index that failed. 62 63For example: 64 65 c.Assert([]int{3, 5, 8}, qt.All(qt.Not(qt.Equals)), 0) 66 c.Assert([][]string{{"a", "b"}, {"a", "b"}}, qt.All(qt.DeepEquals), []string{"c", "d"}) 67 68See also Any and Contains. 69 70 71### Any 72 73Any returns a Checker that uses the given checker to check elements of a slice 74or array or the values from a map. It succeeds if any element passes the check. 75 76For example: 77 78 c.Assert([]int{3,5,7,99}, qt.Any(qt.Equals), 7) 79 c.Assert([][]string{{"a", "b"}, {"c", "d"}}, qt.Any(qt.DeepEquals), []string{"c", "d"}) 80 81See also All and Contains. 82 83 84### CmpEquals 85 86CmpEquals checks equality of two arbitrary values according to the provided 87compare options. DeepEquals is more commonly used when no compare options are 88required. 89 90Example calls: 91 92 c.Assert(list, qt.CmpEquals(cmpopts.SortSlices), []int{42, 47}) 93 c.Assert(got, qt.CmpEquals(), []int{42, 47}) // Same as qt.DeepEquals. 94 95 96### CodecEquals 97 98CodecEquals returns a checker that checks for codec value equivalence. 99 100 func CodecEquals( 101 marshal func(interface{}) ([]byte, error), 102 unmarshal func([]byte, interface{}) error, 103 opts ...cmp.Option, 104 ) Checker 105 106It expects two arguments: a byte slice or a string containing some 107codec-marshaled data, and a Go value. 108 109It uses unmarshal to unmarshal the data into an interface{} value. It marshals 110the Go value using marshal, then unmarshals the result into an interface{} 111value. 112 113It then checks that the two interface{} values are deep-equal to one another, 114using CmpEquals(opts) to perform the check. 115 116See JSONEquals for an example of this in use. 117 118 119### Contains 120 121Contains checks that a map, slice, array or string contains a value. It's the 122same as using Any(Equals), except that it has a special case for strings - if 123the first argument is a string, the second argument must also be a string and 124strings.Contains will be used. 125 126For example: 127 128 c.Assert("hello world", qt.Contains, "world") 129 c.Assert([]int{3,5,7,99}, qt.Contains, 7) 130 131 132### ContentEquals 133 134ContentEquals is is like DeepEquals but any slices in the compared values will 135be sorted before being compared. 136 137For example: 138 139 c.Assert([]string{"c", "a", "b"}, qt.ContentEquals, []string{"a", "b", "c"}) 140 141 142### DeepEquals 143 144DeepEquals checks that two arbitrary values are deeply equal. The comparison is 145done using the github.com/google/go-cmp/cmp package. When comparing structs, by 146default no exported fields are allowed. If a more sophisticated comparison is 147required, use CmpEquals (see below). 148 149Example call: 150 151 c.Assert(got, qt.DeepEquals, []int{42, 47}) 152 153 154### Equals 155 156Equals checks that two values are equal, as compared with Go's == operator. 157 158For instance: 159 160 c.Assert(answer, qt.Equals, 42) 161 162Note that the following will fail: 163 164 c.Assert((*sometype)(nil), qt.Equals, nil) 165 166Use the IsNil checker below for this kind of nil check. 167 168 169### ErrorAs 170 171ErrorAs checks that the error is or wraps a specific error type. If so, it 172assigns it to the provided pointer. This is analogous to calling errors.As. 173 174For instance: 175 176 // Checking for a specific error type 177 c.Assert(err, qt.ErrorAs, new(*os.PathError)) 178 179 // Checking fields on a specific error type 180 var pathError *os.PathError 181 if c.Check(err, qt.ErrorAs, &pathError) { 182 c.Assert(pathError.Path, Equals, "some_path") 183 } 184 185 186### ErrorIs 187 188ErrorIs checks that the error is or wraps a specific error value. This is 189analogous to calling errors.Is. 190 191For instance: 192 193 c.Assert(err, qt.ErrorIs, os.ErrNotExist) 194 195 196### ErrorMatches 197 198ErrorMatches checks that the provided value is an error whose message matches 199the provided regular expression. 200 201For instance: 202 203 c.Assert(err, qt.ErrorMatches, `bad wolf .*`) 204 205 206### HasLen 207 208HasLen checks that the provided value has the given length. 209 210For instance: 211 212 c.Assert([]int{42, 47}, qt.HasLen, 2) 213 c.Assert(myMap, qt.HasLen, 42) 214 215 216### Implements 217 218Implements checks that the provided value implements an interface. The interface 219is specified with a pointer to an interface variable. 220 221For instance: 222 223 var rc io.ReadCloser 224 c.Assert(myReader, qt.Implements, &rc) 225 226 227### IsFalse 228 229IsFalse checks that the provided value is false. The value must have a boolean 230underlying type. 231 232For instance: 233 234 c.Assert(false, qt.IsFalse) 235 c.Assert(IsValid(), qt.IsFalse) 236 237 238### IsNil 239 240IsNil checks that the provided value is nil. 241 242For instance: 243 244 c.Assert(got, qt.IsNil) 245 246As a special case, if the value is nil but implements the error interface, it is 247still considered to be non-nil. This means that IsNil will fail on an error 248value that happens to have an underlying nil value, because that's invariably a 249mistake. See https://golang.org/doc/faq#nil_error. 250 251So it's just fine to check an error like this: 252 253 c.Assert(err, qt.IsNil) 254 255 256### IsNotNil 257 258IsNotNil is a Checker checking that the provided value is not nil. IsNotNil is 259the equivalent of qt.Not(qt.IsNil) 260 261For instance: 262 263 c.Assert(got, qt.IsNotNil) 264 265 266### IsTrue 267 268IsTrue checks that the provided value is true. The value must have a boolean 269underlying type. 270 271For instance: 272 273 c.Assert(true, qt.IsTrue) 274 c.Assert(myBoolean(false), qt.IsTrue) 275 276 277### JSONEquals 278 279JSONEquals checks whether a byte slice or string is JSON-equivalent to a Go 280value. See CodecEquals for more information. 281 282It uses DeepEquals to do the comparison. If a more sophisticated comparison is 283required, use CodecEquals directly. 284 285For instance: 286 287 c.Assert(`{"First": 47.11}`, qt.JSONEquals, &MyStruct{First: 47.11}) 288 289 290### Matches 291 292Matches checks that a string or result of calling the String method (if the 293value implements fmt.Stringer) matches the provided regular expression. 294 295For instance: 296 297 c.Assert("these are the voyages", qt.Matches, `these are .*`) 298 c.Assert(net.ParseIP("1.2.3.4"), qt.Matches, `1.*`) 299 300 301### Not 302 303Not returns a Checker negating the given Checker. 304 305For instance: 306 307 c.Assert(got, qt.Not(qt.IsNil)) 308 c.Assert(answer, qt.Not(qt.Equals), 42) 309 310 311### PanicMatches 312 313PanicMatches checks that the provided function panics with a message matching 314the provided regular expression. 315 316For instance: 317 318 c.Assert(func() {panic("bad wolf ...")}, qt.PanicMatches, `bad wolf .*`) 319 320 321### Satisfies 322 323Satisfies checks that the provided value, when used as argument of the provided 324predicate function, causes the function to return true. The function must be of 325type func(T) bool, having got assignable to T. 326 327For instance: 328 329 // Check that an error from os.Open satisfies os.IsNotExist. 330 c.Assert(err, qt.Satisfies, os.IsNotExist) 331 332 // Check that a floating point number is a not-a-number. 333 c.Assert(f, qt.Satisfies, math.IsNaN) 334 335 336### Deferred Execution 337 338The testing.TB.Cleanup helper provides the ability to defer the execution of 339functions that will be run when the test completes. This is often useful for 340creating OS-level resources such as temporary directories (see c.Mkdir). 341 342When targeting Go versions that don't have Cleanup (< 1.14), the same can be 343achieved using c.Defer. In this case, to trigger the deferred behavior, calling 344c.Done is required. For instance, if you create a *C instance at the top level, 345you’ll have to add a defer to trigger the cleanups at the end of the test: 346 347 defer c.Done() 348 349However, if you use quicktest to create a subtest, Done will be called 350automatically at the end of that subtest. For example: 351 352 func TestFoo(t *testing.T) { 353 c := qt.New(t) 354 c.Run("subtest", func(c *qt.C) { 355 c.Setenv("HOME", c.Mkdir()) 356 // Here $HOME is set the path to a newly created directory. 357 // At the end of the test the directory will be removed 358 // and HOME set back to its original value. 359 }) 360 } 361 362The c.Patch, c.Setenv, c.Unsetenv and c.Mkdir helpers use t.Cleanup for cleaning 363up resources when available, and fall back to Defer otherwise. 364 365For a complete API reference, see the 366[package documentation](https://pkg.go.dev/github.com/frankban/quicktest). 367