Documentation
¶
Overview ¶
Package td (from go-testdeep) allows extremely flexible deep comparison, it is built for testing.
It is a go rewrite and adaptation of wonderful Test::Deep perl module.
In golang, comparing data structure is usually done using reflect.DeepEqual or using a package that uses this function behind the scene.
This function works very well, but it is not flexible. Both compared structures must match exactly.
The purpose of td package is to do its best to introduce this missing flexibility using "operators" when the expected value (or one of its component) cannot be matched exactly.
See go-testdeep for details.
For easy HTTP API testing, see the tdhttp helper.
For tests suites also just as easy, see tdsuite helper.
Example of use ¶
Imagine a function returning a struct containing a newly created database record. The Id and the CreatedAt fields are set by the database layer:
type Record struct { Id uint64 Name string Age int CreatedAt time.Time } func CreateRecord(name string, age int) (*Record, error) { // Do INSERT INTO … and return newly created record or error if it failed }
Using standard testing package ¶
To check the freshly created record contents using standard testing package, we have to do something like that:
import ( "testing" "time" ) func TestCreateRecord(t *testing.T) { before := time.Now().Truncate(time.Second) record, err := CreateRecord() if err != nil { t.Errorf("An error occurred: %s", err) } else { expected := Record{Name: "Bob", Age: 23} if record.Id == 0 { t.Error("Id probably not initialized") } if before.After(record.CreatedAt) || time.Now().Before(record.CreatedAt) { t.Errorf("CreatedAt field not expected: %s", record.CreatedAt) } if record.Name != expected.Name { t.Errorf("Name field differs, got=%s, expected=%s", record.Name, expected.Name) } if record.Age != expected.Age { t.Errorf("Age field differs, got=%s, expected=%s", record.Age, expected.Age) } } }
Using basic go-testdeep approach ¶
td package, via its Cmp* functions, handles the tests and all the error message boiler plate. Let's do it:
import ( "testing" "time" "github.com/maxatome/go-testdeep/td" ) func TestCreateRecord(t *testing.T) { before := time.Now().Truncate(time.Second) record, err := CreateRecord() if td.CmpNoError(t, err) { td.Cmp(t, record.Id, td.NotZero(), "Id initialized") td.Cmp(t, record.Name, "Bob") td.Cmp(t, record.Age, 23) td.Cmp(t, record.CreatedAt, td.Between(before, time.Now())) } }
As we cannot guess the Id field value before its creation, we use the NotZero operator to check it is set by CreateRecord() call. The same it true for the creation date field CreatedAt. Thanks to the Between operator we can check it is set with a value included between the date before CreateRecord() call and the date just after.
Note that if Id and CreateAt could be known in advance, we could simply do:
import ( "testing" "time" "github.com/maxatome/go-testdeep/td" ) func TestCreateRecord(t *testing.T) { before := time.Now().Truncate(time.Second) record, err := CreateRecord() if td.CmpNoError(t, err) { td.Cmp(t, record, &Record{ Id: 1234, Name: "Bob", Age: 23, CreatedAt: time.Date(2019, time.May, 1, 12, 13, 14, 0, time.UTC), }) } }
But unfortunately, it is common to not know exactly the value of some fields…
Using advanced go-testdeep technique ¶
Of course we can test struct fields one by one, but with go-testdeep, the whole struct can be compared with one Cmp call.
import ( "testing" "time" "github.com/maxatome/go-testdeep/td" ) func TestCreateRecord(t *testing.T) { before := time.Now().Truncate(time.Second) record, err := CreateRecord() if td.CmpNoError(t, err) { td.Cmp(t, record, td.Struct( &Record{ Name: "Bob", Age: 23, }, td.StructFields{ "Id": td.NotZero(), "CreatedAt": td.Between(before, time.Now()), }), "Newly created record") } }
See the use of the Struct operator. It is needed here to overcome the go static typing system and so use other go-testdeep operators for some fields, here NotZero and Between.
Not only structs can be compared. A lot of "operators" can be found below to cover most (all?) needed tests. See TestDeep.
Using go-testdeep Cmp shortcuts ¶
The Cmp function is the keystone of this package, but to make the writing of tests even easier, the family of Cmp* functions are provided and act as shortcuts. Using CmpStruct function, the previous example can be written as:
import ( "testing" "time" "github.com/maxatome/go-testdeep/td" ) func TestCreateRecord(t *testing.T) { before := time.Now().Truncate(time.Second) record, err := CreateRecord() if td.CmpNoError(t, err) { td.CmpStruct(t, record, &Record{ Name: "Bob", Age: 23, }, td.StructFields{ "Id": td.NotZero(), "CreatedAt": td.Between(before, time.Now()), }, "Newly created record") } }
Using T type ¶
testing.T can be encapsulated in T type, simplifying again the test:
import ( "testing" "time" "github.com/maxatome/go-testdeep/td" ) func TestCreateRecord(tt *testing.T) { t := td.NewT(tt) before := time.Now().Truncate(time.Second) record, err := CreateRecord() if t.CmpNoError(err) { t.RootName("RECORD").Struct(record, &Record{ Name: "Bob", Age: 23, }, td.StructFields{ "Id": td.NotZero(), "CreatedAt": td.Between(before, time.Now()), }, "Newly created record") } }
Note the use of T.RootName method, it allows to name what we are going to test, instead of the default "DATA".
A step further with operator anchoring ¶
Overcome the go static typing system using the Struct operator is sometimes heavy. Especially when structs are nested, as the Struct operator needs to be used for each level surrounding the level in which an operator is involved. Operator anchoring feature has been designed to avoid this heaviness:
import ( "testing" "time" "github.com/maxatome/go-testdeep/td" ) func TestCreateRecord(tt *testing.T) { before := time.Now().Truncate(time.Second) record, err := CreateRecord() t := td.NewT(tt) // operator anchoring needs a *td.T instance if t.CmpNoError(err) { t.Cmp(record, &Record{ Name: "Bob", Age: 23, ID: t.A(td.NotZero(), uint64(0)).(uint64), CreatedAt: t.A(td.Between(before, time.Now())).(time.Time), }, "Newly created record") } }
See the T.A method (or its full name alias T.Anchor) documentation for details.
Example ¶
package main import ( "fmt" "testing" "time" "github.com/maxatome/go-testdeep/td" ) func main() { t := &testing.T{} dateToTime := func(str string) time.Time { t, err := time.Parse(time.RFC3339, str) if err != nil { panic(err) } return t } type PetFamily uint8 const ( Canidae PetFamily = 1 Felidae PetFamily = 2 ) type Pet struct { Name string Birthday time.Time Family PetFamily } type Master struct { Name string AnnualIncome int Pets []*Pet } // Imagine a function returning a Master slice... masters := []Master{ { Name: "Bob Smith", AnnualIncome: 25000, Pets: []*Pet{ { Name: "Quizz", Birthday: dateToTime("2010-11-05T10:00:00Z"), Family: Canidae, }, { Name: "Charlie", Birthday: dateToTime("2013-05-11T08:00:00Z"), Family: Canidae, }, }, }, { Name: "John Doe", AnnualIncome: 38000, Pets: []*Pet{ { Name: "Coco", Birthday: dateToTime("2015-08-05T18:00:00Z"), Family: Felidae, }, { Name: "Lucky", Birthday: dateToTime("2014-04-17T07:00:00Z"), Family: Canidae, }, }, }, } // Let's check masters slice contents ok := td.Cmp(t, masters, td.All( td.Len(td.Gt(0)), // len(masters) should be > 0 td.ArrayEach( // For each Master td.Struct(Master{}, td.StructFields{ // Master Name should be composed of 2 words, with 1st letter uppercased "Name": td.Re(`^[A-Z][a-z]+ [A-Z][a-z]+\z`), // Annual income should be greater than $10000 "AnnualIncome": td.Gt(10000), "Pets": td.ArrayEach( // For each Pet td.Struct(&Pet{}, td.StructFields{ // Pet Name should be composed of 1 word, with 1st letter uppercased "Name": td.Re(`^[A-Z][a-z]+\z`), "Birthday": td.All( // Pet should be born after 2010, January 1st, but before now! td.Between(dateToTime("2010-01-01T00:00:00Z"), time.Now()), // AND minutes, seconds and nanoseconds should be 0 td.Code(func(t time.Time) bool { return t.Minute() == 0 && t.Second() == 0 && t.Nanosecond() == 0 }), ), // Only dogs and cats allowed "Family": td.Any(Canidae, Felidae), }), ), }), ), )) fmt.Println(ok) }
Output: true
Index ¶
- Variables
- func A(t *T, operator TestDeep) X
- func AddAnchorableStructType(fn any)
- func Anchor(t *T, operator TestDeep) X
- func Cmp(t TestingT, got, expected any, args ...any) bool
- func CmpAll(t TestingT, got any, expectedValues []any, args ...any) bool
- func CmpAny(t TestingT, got any, expectedValues []any, args ...any) bool
- func CmpArray(t TestingT, got, model any, expectedEntries ArrayEntries, args ...any) bool
- func CmpArrayEach(t TestingT, got, expectedValue any, args ...any) bool
- func CmpBag(t TestingT, got any, expectedItems []any, args ...any) bool
- func CmpBetween(t TestingT, got, from, to any, bounds BoundsKind, args ...any) bool
- func CmpCap(t TestingT, got, expectedCap any, args ...any) bool
- func CmpCode(t TestingT, got, fn any, args ...any) bool
- func CmpContains(t TestingT, got, expectedValue any, args ...any) bool
- func CmpContainsKey(t TestingT, got, expectedValue any, args ...any) bool
- func CmpDeeply(t TestingT, got, expected any, args ...any) bool
- func CmpEmpty(t TestingT, got any, args ...any) bool
- func CmpError(t TestingT, got error, args ...any) bool
- func CmpErrorIs(t TestingT, got any, expected error, args ...any) bool
- func CmpFalse(t TestingT, got bool, args ...any) bool
- func CmpFirst(t TestingT, got, filter, expectedValue any, args ...any) bool
- func CmpGrep(t TestingT, got, filter, expectedValue any, args ...any) bool
- func CmpGt(t TestingT, got, minExpectedValue any, args ...any) bool
- func CmpGte(t TestingT, got, minExpectedValue any, args ...any) bool
- func CmpHasPrefix(t TestingT, got any, expected string, args ...any) bool
- func CmpHasSuffix(t TestingT, got any, expected string, args ...any) bool
- func CmpIsa(t TestingT, got, model any, args ...any) bool
- func CmpJSON(t TestingT, got, expectedJSON any, params []any, args ...any) bool
- func CmpJSONPointer(t TestingT, got any, ptr string, expectedValue any, args ...any) bool
- func CmpKeys(t TestingT, got, val any, args ...any) bool
- func CmpLast(t TestingT, got, filter, expectedValue any, args ...any) bool
- func CmpLax(t TestingT, got, expectedValue any, args ...any) bool
- func CmpLen(t TestingT, got, expectedLen any, args ...any) bool
- func CmpLt(t TestingT, got, maxExpectedValue any, args ...any) bool
- func CmpLte(t TestingT, got, maxExpectedValue any, args ...any) bool
- func CmpMap(t TestingT, got, model any, expectedEntries MapEntries, args ...any) bool
- func CmpMapEach(t TestingT, got, expectedValue any, args ...any) bool
- func CmpN(t TestingT, got, num, tolerance any, args ...any) bool
- func CmpNaN(t TestingT, got any, args ...any) bool
- func CmpNil(t TestingT, got any, args ...any) bool
- func CmpNoError(t TestingT, got error, args ...any) bool
- func CmpNone(t TestingT, got any, notExpectedValues []any, args ...any) bool
- func CmpNot(t TestingT, got, notExpected any, args ...any) bool
- func CmpNotAny(t TestingT, got any, notExpectedItems []any, args ...any) bool
- func CmpNotEmpty(t TestingT, got any, args ...any) bool
- func CmpNotNaN(t TestingT, got any, args ...any) bool
- func CmpNotNil(t TestingT, got any, args ...any) bool
- func CmpNotPanic(t TestingT, fn func(), args ...any) bool
- func CmpNotZero(t TestingT, got any, args ...any) bool
- func CmpPPtr(t TestingT, got, val any, args ...any) bool
- func CmpPanic(t TestingT, fn func(), expectedPanic any, args ...any) bool
- func CmpPtr(t TestingT, got, val any, args ...any) bool
- func CmpRe(t TestingT, got, reg, capture any, args ...any) bool
- func CmpReAll(t TestingT, got, reg, capture any, args ...any) bool
- func CmpRecv(t TestingT, got, expectedValue any, timeout time.Duration, args ...any) bool
- func CmpSStruct(t TestingT, got, model any, expectedFields StructFields, args ...any) bool
- func CmpSet(t TestingT, got any, expectedItems []any, args ...any) bool
- func CmpShallow(t TestingT, got, expectedPtr any, args ...any) bool
- func CmpSlice(t TestingT, got, model any, expectedEntries ArrayEntries, args ...any) bool
- func CmpSmuggle(t TestingT, got, fn, expectedValue any, args ...any) bool
- func CmpString(t TestingT, got any, expected string, args ...any) bool
- func CmpStruct(t TestingT, got, model any, expectedFields StructFields, args ...any) bool
- func CmpSubBagOf(t TestingT, got any, expectedItems []any, args ...any) bool
- func CmpSubJSONOf(t TestingT, got, expectedJSON any, params []any, args ...any) bool
- func CmpSubMapOf(t TestingT, got, model any, expectedEntries MapEntries, args ...any) bool
- func CmpSubSetOf(t TestingT, got any, expectedItems []any, args ...any) bool
- func CmpSuperBagOf(t TestingT, got any, expectedItems []any, args ...any) bool
- func CmpSuperJSONOf(t TestingT, got, expectedJSON any, params []any, args ...any) bool
- func CmpSuperMapOf(t TestingT, got, model any, expectedEntries MapEntries, args ...any) bool
- func CmpSuperSetOf(t TestingT, got any, expectedItems []any, args ...any) bool
- func CmpSuperSliceOf(t TestingT, got, model any, expectedEntries ArrayEntries, args ...any) bool
- func CmpTrue(t TestingT, got bool, args ...any) bool
- func CmpTruncTime(t TestingT, got, expectedTime any, trunc time.Duration, args ...any) bool
- func CmpValues(t TestingT, got, val any, args ...any) bool
- func CmpZero(t TestingT, got any, args ...any) bool
- func EqDeeply(got, expected any) bool
- func EqDeeplyError(got, expected any) error
- func Flatten(sliceOrMap any, fn ...any) flat.Slice
- func S(args ...any) string
- type ArrayEntries
- type BoundsKind
- type ContextConfig
- type MapEntries
- type RecvKind
- type SmuggledGot
- type StructFields
- type T
- func (t *T) A(operator TestDeep, model ...any) any
- func (t *T) All(got any, expectedValues []any, args ...any) bool
- func (t *T) Anchor(operator TestDeep, model ...any) any
- func (t *T) AnchorsPersistTemporarily() func()
- func (t *T) Any(got any, expectedValues []any, args ...any) bool
- func (t *T) Array(got, model any, expectedEntries ArrayEntries, args ...any) bool
- func (t *T) ArrayEach(got, expectedValue any, args ...any) bool
- func (t *T) Assert() *T
- func (t *T) Bag(got any, expectedItems []any, args ...any) bool
- func (t *T) BeLax(enable ...bool) *T
- func (t *T) Between(got, from, to any, bounds BoundsKind, args ...any) bool
- func (t *T) Cap(got, expectedCap any, args ...any) bool
- func (t *T) Cmp(got, expected any, args ...any) bool
- func (t *T) CmpDeeply(got, expected any, args ...any) bool
- func (t *T) CmpError(got error, args ...any) bool
- func (t *T) CmpErrorIs(got any, expected error, args ...any) bool
- func (t *T) CmpLax(got, expectedValue any, args ...any) bool
- func (t *T) CmpNoError(got error, args ...any) bool
- func (t *T) CmpNotPanic(fn func(), args ...any) bool
- func (t *T) CmpPanic(fn func(), expected any, args ...any) bool
- func (t *T) Code(got, fn any, args ...any) bool
- func (t *T) Contains(got, expectedValue any, args ...any) bool
- func (t *T) ContainsKey(got, expectedValue any, args ...any) bool
- func (t *T) DoAnchorsPersist() bool
- func (t *T) Empty(got any, args ...any) bool
- func (t *T) ErrorTrace(args ...any)
- func (t *T) FailureIsFatal(enable ...bool) *T
- func (t *T) False(got any, args ...any) bool
- func (t *T) FatalTrace(args ...any)
- func (t *T) First(got, filter, expectedValue any, args ...any) bool
- func (t *T) Grep(got, filter, expectedValue any, args ...any) bool
- func (t *T) Gt(got, minExpectedValue any, args ...any) bool
- func (t *T) Gte(got, minExpectedValue any, args ...any) bool
- func (t *T) HasPrefix(got any, expected string, args ...any) bool
- func (t *T) HasSuffix(got any, expected string, args ...any) bool
- func (t *T) IgnoreUnexported(types ...any) *T
- func (t *T) Isa(got, model any, args ...any) bool
- func (t *T) JSON(got, expectedJSON any, params []any, args ...any) bool
- func (t *T) JSONPointer(got any, ptr string, expectedValue any, args ...any) bool
- func (t *T) Keys(got, val any, args ...any) bool
- func (t *T) Last(got, filter, expectedValue any, args ...any) bool
- func (t *T) Len(got, expectedLen any, args ...any) bool
- func (t *T) LogTrace(args ...any)
- func (t *T) Lt(got, maxExpectedValue any, args ...any) bool
- func (t *T) Lte(got, maxExpectedValue any, args ...any) bool
- func (t *T) Map(got, model any, expectedEntries MapEntries, args ...any) bool
- func (t *T) MapEach(got, expectedValue any, args ...any) bool
- func (t *T) N(got, num, tolerance any, args ...any) bool
- func (t *T) NaN(got any, args ...any) bool
- func (t *T) Nil(got any, args ...any) bool
- func (t *T) None(got any, notExpectedValues []any, args ...any) bool
- func (t *T) Not(got, notExpected any, args ...any) bool
- func (t *T) NotAny(got any, notExpectedItems []any, args ...any) bool
- func (t *T) NotEmpty(got any, args ...any) bool
- func (t *T) NotNaN(got any, args ...any) bool
- func (t *T) NotNil(got any, args ...any) bool
- func (t *T) NotZero(got any, args ...any) bool
- func (t *T) PPtr(got, val any, args ...any) bool
- func (t *T) Parallel()
- func (t *T) Ptr(got, val any, args ...any) bool
- func (t *T) Re(got, reg, capture any, args ...any) bool
- func (t *T) ReAll(got, reg, capture any, args ...any) bool
- func (t *T) Recv(got, expectedValue any, timeout time.Duration, args ...any) bool
- func (t *T) Require() *T
- func (t *T) ResetAnchors()
- func (t *T) RootName(rootName string) *T
- func (t *T) Run(name string, f func(t *T)) bool
- func (t *T) RunAssertRequire(name string, f func(assert, require *T)) bool
- func (t *T) RunT(name string, f func(t *T)) booldeprecated
- func (t *T) SStruct(got, model any, expectedFields StructFields, args ...any) bool
- func (t *T) Set(got any, expectedItems []any, args ...any) bool
- func (t *T) SetAnchorsPersist(persist bool)
- func (t *T) Shallow(got, expectedPtr any, args ...any) bool
- func (t *T) Slice(got, model any, expectedEntries ArrayEntries, args ...any) bool
- func (t *T) Smuggle(got, fn, expectedValue any, args ...any) bool
- func (t *T) String(got any, expected string, args ...any) bool
- func (t *T) Struct(got, model any, expectedFields StructFields, args ...any) bool
- func (t *T) SubBagOf(got any, expectedItems []any, args ...any) bool
- func (t *T) SubJSONOf(got, expectedJSON any, params []any, args ...any) bool
- func (t *T) SubMapOf(got, model any, expectedEntries MapEntries, args ...any) bool
- func (t *T) SubSetOf(got any, expectedItems []any, args ...any) bool
- func (t *T) SuperBagOf(got any, expectedItems []any, args ...any) bool
- func (t *T) SuperJSONOf(got, expectedJSON any, params []any, args ...any) bool
- func (t *T) SuperMapOf(got, model any, expectedEntries MapEntries, args ...any) bool
- func (t *T) SuperSetOf(got any, expectedItems []any, args ...any) bool
- func (t *T) SuperSliceOf(got, model any, expectedEntries ArrayEntries, args ...any) bool
- func (t *T) TestDeepInGotOK(enable ...bool) *T
- func (t *T) True(got any, args ...any) bool
- func (t *T) TruncTime(got, expectedTime any, trunc time.Duration, args ...any) bool
- func (t *T) UseEqual(types ...any) *T
- func (t *T) Values(got, val any, args ...any) bool
- func (t *T) WithCmpHooks(fns ...any) *T
- func (t *T) WithSmuggleHooks(fns ...any) *T
- func (t *T) Zero(got any, args ...any) bool
- type TestDeep
- func All(expectedValues ...any) TestDeep
- func Any(expectedValues ...any) TestDeep
- func Array(model any, expectedEntries ArrayEntries) TestDeep
- func ArrayEach(expectedValue any) TestDeep
- func Bag(expectedItems ...any) TestDeep
- func Between(from, to any, bounds ...BoundsKind) TestDeep
- func Cap(expectedCap any) TestDeep
- func Catch(target, expectedValue any) TestDeep
- func Code(fn any) TestDeep
- func Contains(expectedValue any) TestDeep
- func ContainsKey(expectedValue any) TestDeep
- func Delay(delayed func() TestDeep) TestDeep
- func Empty() TestDeep
- func ErrorIs(expected error) TestDeep
- func First(filter, expectedValue any) TestDeep
- func Grep(filter, expectedValue any) TestDeep
- func Gt(minExpectedValue any) TestDeep
- func Gte(minExpectedValue any) TestDeep
- func HasPrefix(expected string) TestDeep
- func HasSuffix(expected string) TestDeep
- func Ignore() TestDeep
- func Isa(model any) TestDeep
- func JSON(expectedJSON any, params ...any) TestDeep
- func JSONPointer(ptr string, expectedValue any) TestDeep
- func Keys(val any) TestDeep
- func Last(filter, expectedValue any) TestDeep
- func Lax(expectedValue any) TestDeep
- func Len(expectedLen any) TestDeep
- func Lt(maxExpectedValue any) TestDeep
- func Lte(maxExpectedValue any) TestDeep
- func Map(model any, expectedEntries MapEntries) TestDeep
- func MapEach(expectedValue any) TestDeep
- func N(num any, tolerance ...any) TestDeep
- func NaN() TestDeep
- func Nil() TestDeep
- func None(notExpectedValues ...any) TestDeep
- func Not(notExpected any) TestDeep
- func NotAny(notExpectedItems ...any) TestDeep
- func NotEmpty() TestDeep
- func NotNaN() TestDeep
- func NotNil() TestDeep
- func NotZero() TestDeep
- func PPtr(val any) TestDeep
- func Ptr(val any) TestDeep
- func Re(reg any, capture ...any) TestDeep
- func ReAll(reg, capture any) TestDeep
- func Recv(expectedValue any, timeout ...time.Duration) TestDeep
- func SStruct(model any, expectedFields ...StructFields) TestDeep
- func Set(expectedItems ...any) TestDeep
- func Shallow(expectedPtr any) TestDeep
- func Slice(model any, expectedEntries ArrayEntries) TestDeep
- func Smuggle(fn, expectedValue any) TestDeep
- func String(expected string) TestDeep
- func Struct(model any, expectedFields ...StructFields) TestDeep
- func SubBagOf(expectedItems ...any) TestDeep
- func SubJSONOf(expectedJSON any, params ...any) TestDeep
- func SubMapOf(model any, expectedEntries MapEntries) TestDeep
- func SubSetOf(expectedItems ...any) TestDeep
- func SuperBagOf(expectedItems ...any) TestDeep
- func SuperJSONOf(expectedJSON any, params ...any) TestDeep
- func SuperMapOf(model any, expectedEntries MapEntries) TestDeep
- func SuperSetOf(expectedItems ...any) TestDeep
- func SuperSliceOf(model any, expectedEntries ArrayEntries) TestDeep
- func Tag(tag string, expectedValue any) TestDeep
- func TruncTime(expectedTime any, trunc ...time.Duration) TestDeep
- func Values(val any) TestDeep
- func Zero() TestDeep
- type TestingFT
- type TestingT
- type Tuple
Examples ¶
- Package
- All
- Any
- Array (Array)
- Array (TypedArray)
- ArrayEach (Array)
- ArrayEach (Slice)
- ArrayEach (TypedArray)
- ArrayEach (TypedSlice)
- Bag
- Between (Int)
- Between (String)
- Between (Time)
- Cap
- Cap (Operator)
- Catch
- CmpAll
- CmpAny
- CmpArray (Array)
- CmpArray (TypedArray)
- CmpArrayEach (Array)
- CmpArrayEach (Slice)
- CmpArrayEach (TypedArray)
- CmpArrayEach (TypedSlice)
- CmpBag
- CmpBetween (Int)
- CmpBetween (String)
- CmpBetween (Time)
- CmpCap
- CmpCap (Operator)
- CmpCode
- CmpCode (Custom)
- CmpContains (ArraySlice)
- CmpContains (Error)
- CmpContains (Map)
- CmpContains (Nil)
- CmpContains (String)
- CmpContains (Stringer)
- CmpContainsKey
- CmpContainsKey (Nil)
- CmpEmpty
- CmpEmpty (Pointers)
- CmpError
- CmpErrorIs
- CmpFalse
- CmpFirst (Classic)
- CmpFirst (Empty)
- CmpFirst (Struct)
- CmpGrep (Classic)
- CmpGrep (Nil)
- CmpGrep (Struct)
- CmpGt (Int)
- CmpGt (String)
- CmpGte (Int)
- CmpGte (String)
- CmpHasPrefix
- CmpHasPrefix (Error)
- CmpHasPrefix (Stringer)
- CmpHasSuffix
- CmpHasSuffix (Error)
- CmpHasSuffix (Stringer)
- CmpIsa
- CmpIsa (Interface)
- CmpJSON (Basic)
- CmpJSON (Embedding)
- CmpJSON (File)
- CmpJSON (Placeholders)
- CmpJSON (RawStrings)
- CmpJSONPointer (Has_hasnt)
- CmpJSONPointer (Rfc6901)
- CmpJSONPointer (Struct)
- CmpKeys
- CmpLast (Classic)
- CmpLast (Empty)
- CmpLast (Struct)
- CmpLax
- CmpLen (Map)
- CmpLen (OperatorMap)
- CmpLen (OperatorSlice)
- CmpLen (Slice)
- CmpLt (Int)
- CmpLt (String)
- CmpLte (Int)
- CmpLte (String)
- CmpMap (Map)
- CmpMap (TypedMap)
- CmpMapEach (Map)
- CmpMapEach (TypedMap)
- CmpN
- CmpNaN (Float32)
- CmpNaN (Float64)
- CmpNil
- CmpNoError
- CmpNone
- CmpNot
- CmpNotAny
- CmpNotEmpty
- CmpNotEmpty (Pointers)
- CmpNotNaN (Float32)
- CmpNotNaN (Float64)
- CmpNotNil
- CmpNotPanic
- CmpNotZero
- CmpPPtr
- CmpPanic
- CmpPtr
- CmpRe
- CmpRe (Capture)
- CmpRe (Compiled)
- CmpRe (CompiledCapture)
- CmpRe (CompiledError)
- CmpRe (CompiledStringer)
- CmpRe (Error)
- CmpRe (Stringer)
- CmpReAll (Capture)
- CmpReAll (CaptureComplex)
- CmpReAll (CompiledCapture)
- CmpReAll (CompiledCaptureComplex)
- CmpRecv (Basic)
- CmpRecv (ChannelPointer)
- CmpRecv (NilChannel)
- CmpRecv (WithTimeout)
- CmpSStruct
- CmpSStruct (Overwrite_model)
- CmpSStruct (Patterns)
- CmpSet
- CmpShallow
- CmpShallow (Slice)
- CmpShallow (String)
- CmpSlice (Slice)
- CmpSlice (TypedSlice)
- CmpSmuggle (Auto_unmarshal)
- CmpSmuggle (Cast)
- CmpSmuggle (Complex)
- CmpSmuggle (Convert)
- CmpSmuggle (Field_path)
- CmpSmuggle (Interface)
- CmpSmuggle (Lax)
- CmpString
- CmpString (Error)
- CmpString (Stringer)
- CmpStruct
- CmpStruct (Overwrite_model)
- CmpStruct (Patterns)
- CmpSubBagOf
- CmpSubJSONOf (Basic)
- CmpSubJSONOf (File)
- CmpSubJSONOf (Placeholders)
- CmpSubMapOf (Map)
- CmpSubMapOf (TypedMap)
- CmpSubSetOf
- CmpSuperBagOf
- CmpSuperJSONOf (Basic)
- CmpSuperJSONOf (File)
- CmpSuperJSONOf (Placeholders)
- CmpSuperMapOf (Map)
- CmpSuperMapOf (TypedMap)
- CmpSuperSetOf
- CmpSuperSliceOf (Array)
- CmpSuperSliceOf (Slice)
- CmpSuperSliceOf (TypedArray)
- CmpSuperSliceOf (TypedSlice)
- CmpTrue
- CmpTruncTime
- CmpValues
- CmpZero
- Code
- Code (Custom)
- Contains (ArraySlice)
- Contains (Error)
- Contains (Map)
- Contains (Nil)
- Contains (String)
- Contains (Stringer)
- ContainsKey
- ContainsKey (Nil)
- Delay
- Empty
- Empty (Pointers)
- EqDeeply
- EqDeeplyError
- ErrorIs
- First (Classic)
- First (Empty)
- First (Json)
- First (Struct)
- Grep (Classic)
- Grep (Json)
- Grep (Nil)
- Grep (Struct)
- Gt (Int)
- Gt (String)
- Gte (Int)
- Gte (String)
- HasPrefix
- HasPrefix (Error)
- HasPrefix (Stringer)
- HasSuffix
- HasSuffix (Error)
- HasSuffix (Stringer)
- Ignore
- Isa
- Isa (Interface)
- JSON (Basic)
- JSON (Embedding)
- JSON (File)
- JSON (Placeholders)
- JSON (RawStrings)
- JSONPointer (Has_hasnt)
- JSONPointer (Rfc6901)
- JSONPointer (Struct)
- Keys
- Last (Classic)
- Last (Empty)
- Last (Json)
- Last (Struct)
- Lax
- Len (Map)
- Len (OperatorMap)
- Len (OperatorSlice)
- Len (Slice)
- Lt (Int)
- Lt (String)
- Lte (Int)
- Lte (String)
- Map (Map)
- Map (TypedMap)
- MapEach (Map)
- MapEach (TypedMap)
- N
- NaN (Float32)
- NaN (Float64)
- Nil
- None
- Not
- NotAny
- NotEmpty
- NotEmpty (Pointers)
- NotNaN (Float32)
- NotNaN (Float64)
- NotNil
- NotZero
- PPtr
- Ptr
- Re
- Re (Capture)
- Re (Compiled)
- Re (CompiledCapture)
- Re (CompiledError)
- Re (CompiledStringer)
- Re (Error)
- Re (Stringer)
- ReAll (Capture)
- ReAll (CaptureComplex)
- ReAll (CompiledCapture)
- ReAll (CompiledCaptureComplex)
- Recv (Basic)
- Recv (ChannelPointer)
- Recv (NilChannel)
- Recv (WithTimeout)
- SStruct
- SStruct (Overwrite_model)
- SStruct (Patterns)
- SStruct (Struct_fields)
- Set
- Shallow
- Shallow (Slice)
- Shallow (String)
- Slice (Slice)
- Slice (TypedSlice)
- Smuggle (Auto_unmarshal)
- Smuggle (Cast)
- Smuggle (Complex)
- Smuggle (Convert)
- Smuggle (Field_path)
- Smuggle (Interface)
- Smuggle (Lax)
- String
- String (Error)
- String (Stringer)
- Struct
- Struct (Overwrite_model)
- Struct (Patterns)
- Struct (Struct_fields)
- SubBagOf
- SubJSONOf (Basic)
- SubJSONOf (File)
- SubJSONOf (Placeholders)
- SubMapOf (Map)
- SubMapOf (TypedMap)
- SubSetOf
- SuperBagOf
- SuperJSONOf (Basic)
- SuperJSONOf (File)
- SuperJSONOf (Placeholders)
- SuperMapOf (Map)
- SuperMapOf (TypedMap)
- SuperSetOf
- SuperSliceOf (Array)
- SuperSliceOf (Slice)
- SuperSliceOf (TypedArray)
- SuperSliceOf (TypedSlice)
- T.All
- T.Any
- T.Array (Array)
- T.Array (TypedArray)
- T.ArrayEach (Array)
- T.ArrayEach (Slice)
- T.ArrayEach (TypedArray)
- T.ArrayEach (TypedSlice)
- T.Bag
- T.Between (Int)
- T.Between (String)
- T.Between (Time)
- T.Cap
- T.Cap (Operator)
- T.CmpError
- T.CmpErrorIs
- T.CmpLax
- T.CmpNoError
- T.CmpNotPanic
- T.CmpPanic
- T.Code
- T.Code (Custom)
- T.Contains (ArraySlice)
- T.Contains (Error)
- T.Contains (Map)
- T.Contains (Nil)
- T.Contains (String)
- T.Contains (Stringer)
- T.ContainsKey
- T.ContainsKey (Nil)
- T.Empty
- T.Empty (Pointers)
- T.False
- T.First (Classic)
- T.First (Empty)
- T.First (Struct)
- T.Grep (Classic)
- T.Grep (Nil)
- T.Grep (Struct)
- T.Gt (Int)
- T.Gt (String)
- T.Gte (Int)
- T.Gte (String)
- T.HasPrefix
- T.HasPrefix (Error)
- T.HasPrefix (Stringer)
- T.HasSuffix
- T.HasSuffix (Error)
- T.HasSuffix (Stringer)
- T.Isa
- T.Isa (Interface)
- T.JSON (Basic)
- T.JSON (Embedding)
- T.JSON (File)
- T.JSON (Placeholders)
- T.JSON (RawStrings)
- T.JSONPointer (Has_hasnt)
- T.JSONPointer (Rfc6901)
- T.JSONPointer (Struct)
- T.Keys
- T.Last (Classic)
- T.Last (Empty)
- T.Last (Struct)
- T.Len (Map)
- T.Len (OperatorMap)
- T.Len (OperatorSlice)
- T.Len (Slice)
- T.Lt (Int)
- T.Lt (String)
- T.Lte (Int)
- T.Lte (String)
- T.Map (Map)
- T.Map (TypedMap)
- T.MapEach (Map)
- T.MapEach (TypedMap)
- T.N
- T.NaN (Float32)
- T.NaN (Float64)
- T.Nil
- T.None
- T.Not
- T.NotAny
- T.NotEmpty
- T.NotEmpty (Pointers)
- T.NotNaN (Float32)
- T.NotNaN (Float64)
- T.NotNil
- T.NotZero
- T.PPtr
- T.Ptr
- T.Re
- T.Re (Capture)
- T.Re (Compiled)
- T.Re (CompiledCapture)
- T.Re (CompiledError)
- T.Re (CompiledStringer)
- T.Re (Error)
- T.Re (Stringer)
- T.ReAll (Capture)
- T.ReAll (CaptureComplex)
- T.ReAll (CompiledCapture)
- T.ReAll (CompiledCaptureComplex)
- T.Recv (Basic)
- T.Recv (ChannelPointer)
- T.Recv (NilChannel)
- T.Recv (WithTimeout)
- T.SStruct
- T.SStruct (Overwrite_model)
- T.SStruct (Patterns)
- T.Set
- T.Shallow
- T.Shallow (Slice)
- T.Shallow (String)
- T.Slice (Slice)
- T.Slice (TypedSlice)
- T.Smuggle (Auto_unmarshal)
- T.Smuggle (Cast)
- T.Smuggle (Complex)
- T.Smuggle (Convert)
- T.Smuggle (Field_path)
- T.Smuggle (Interface)
- T.Smuggle (Lax)
- T.String
- T.String (Error)
- T.String (Stringer)
- T.Struct
- T.Struct (Overwrite_model)
- T.Struct (Patterns)
- T.SubBagOf
- T.SubJSONOf (Basic)
- T.SubJSONOf (File)
- T.SubJSONOf (Placeholders)
- T.SubMapOf (Map)
- T.SubMapOf (TypedMap)
- T.SubSetOf
- T.SuperBagOf
- T.SuperJSONOf (Basic)
- T.SuperJSONOf (File)
- T.SuperJSONOf (Placeholders)
- T.SuperMapOf (Map)
- T.SuperMapOf (TypedMap)
- T.SuperSetOf
- T.SuperSliceOf (Array)
- T.SuperSliceOf (Slice)
- T.SuperSliceOf (TypedArray)
- T.SuperSliceOf (TypedSlice)
- T.True
- T.TruncTime
- T.Values
- T.Zero
- TruncTime
- Values
- Zero
Constants ¶
This section is empty.
Variables ¶
var DefaultContextConfig = ContextConfig{ RootName: contextDefaultRootName, MaxErrors: getMaxErrorsFromEnv(), FailureIsFatal: false, UseEqual: false, BeLax: false, IgnoreUnexported: false, TestDeepInGotOK: false, }
DefaultContextConfig is the default configuration used to render tests failures. If overridden, new settings will impact all Cmp* functions and *T methods (if not specifically configured.)
Functions ¶
func AddAnchorableStructType ¶
func AddAnchorableStructType(fn any)
AddAnchorableStructType declares a struct type as anchorable. fn is a function allowing to return a unique and identifiable instance of the struct type.
fn has to have the following signature:
func (nextAnchor int) TYPE
TYPE is the struct type to make anchorable and nextAnchor is an index to allow to differentiate several instances of the same type.
For example, the time.Time type which is anchorable by default, could be declared as:
AddAnchorableStructType(func (nextAnchor int) time.Time { return time.Unix(int64(math.MaxInt64-1000424443-nextAnchor), 42) })
Just as a note, the 1000424443 constant allows to avoid to flirt with the math.MaxInt64 extreme limit and so avoid possible collision with real world values.
It panics if the provided fn is not a function or if it has not the expected signature (see above).
See also T.Anchor, T.AnchorsPersistTemporarily, T.DoAnchorsPersist, T.ResetAnchors and T.SetAnchorsPersist.
func Cmp ¶
Cmp returns true if got matches expected. expected can be the same type as got is, or contains some TestDeep operators. If got does not match expected, it returns false and the reason of failure is logged with the help of t Error() method.
got := "foobar" td.Cmp(t, got, "foobar") // succeeds td.Cmp(t, got, td.HasPrefix("foo")) // succeeds
If t is a *T then its Config is inherited, so:
td.Cmp(td.Require(t), got, 42)
is the same as:
td.Require(t).Cmp(got, 42)
args... are optional and allow to name the test. This name is used in case of failure to qualify the test. If len(args) > 1 and the first item of args is a string and contains a '%' rune then fmt.Fprintf is used to compose the name, else args are passed to fmt.Fprint. Do not forget it is the name of the test, not the reason of a potential failure.
func CmpAll ¶
CmpAll is a shortcut for:
td.Cmp(t, got, td.All(expectedValues...), args...)
See All for details.
Returns true if the test is OK, false if it fails.
If t is a *T then its Config field is inherited.
args... are optional and allow to name the test. This name is used in case of failure to qualify the test. If len(args) > 1 and the first item of args is a string and contains a '%' rune then fmt.Fprintf is used to compose the name, else args are passed to fmt.Fprint. Do not forget it is the name of the test, not the reason of a potential failure.
Example ¶
package main import ( "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := &testing.T{} got := "foo/bar" // Checks got string against: // "o/b" regexp *AND* "bar" suffix *AND* exact "foo/bar" string ok := td.CmpAll(t, got, []any{td.Re("o/b"), td.HasSuffix("bar"), "foo/bar"}, "checks value %s", got) fmt.Println(ok) // Checks got string against: // "o/b" regexp *AND* "bar" suffix *AND* exact "fooX/Ybar" string ok = td.CmpAll(t, got, []any{td.Re("o/b"), td.HasSuffix("bar"), "fooX/Ybar"}, "checks value %s", got) fmt.Println(ok) // When some operators or values have to be reused and mixed between // several calls, Flatten can be used to avoid boring and // inefficient []any copies: regOps := td.Flatten([]td.TestDeep{td.Re("o/b"), td.Re(`^fo`), td.Re(`ar$`)}) ok = td.CmpAll(t, got, []any{td.HasPrefix("foo"), regOps, td.HasSuffix("bar")}, "checks all operators against value %s", got) fmt.Println(ok) }
Output: true false true
func CmpAny ¶
CmpAny is a shortcut for:
td.Cmp(t, got, td.Any(expectedValues...), args...)
See Any for details.
Returns true if the test is OK, false if it fails.
If t is a *T then its Config field is inherited.
args... are optional and allow to name the test. This name is used in case of failure to qualify the test. If len(args) > 1 and the first item of args is a string and contains a '%' rune then fmt.Fprintf is used to compose the name, else args are passed to fmt.Fprint. Do not forget it is the name of the test, not the reason of a potential failure.
Example ¶
package main import ( "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := &testing.T{} got := "foo/bar" // Checks got string against: // "zip" regexp *OR* "bar" suffix ok := td.CmpAny(t, got, []any{td.Re("zip"), td.HasSuffix("bar")}, "checks value %s", got) fmt.Println(ok) // Checks got string against: // "zip" regexp *OR* "foo" suffix ok = td.CmpAny(t, got, []any{td.Re("zip"), td.HasSuffix("foo")}, "checks value %s", got) fmt.Println(ok) // When some operators or values have to be reused and mixed between // several calls, Flatten can be used to avoid boring and // inefficient []any copies: regOps := td.Flatten([]td.TestDeep{td.Re("a/c"), td.Re(`^xx`), td.Re(`ar$`)}) ok = td.CmpAny(t, got, []any{td.HasPrefix("xxx"), regOps, td.HasSuffix("zip")}, "check at least one operator matches value %s", got) fmt.Println(ok) }
Output: true false true
func CmpArray ¶
func CmpArray(t TestingT, got, model any, expectedEntries ArrayEntries, args ...any) bool
CmpArray is a shortcut for:
td.Cmp(t, got, td.Array(model, expectedEntries), args...)
See Array for details.
Returns true if the test is OK, false if it fails.
If t is a *T then its Config field is inherited.
args... are optional and allow to name the test. This name is used in case of failure to qualify the test. If len(args) > 1 and the first item of args is a string and contains a '%' rune then fmt.Fprintf is used to compose the name, else args are passed to fmt.Fprint. Do not forget it is the name of the test, not the reason of a potential failure.
Example (Array) ¶
package main import ( "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := &testing.T{} got := [3]int{42, 58, 26} ok := td.CmpArray(t, got, [3]int{42}, td.ArrayEntries{1: 58, 2: td.Ignore()}, "checks array %v", got) fmt.Println("Simple array:", ok) ok = td.CmpArray(t, &got, &[3]int{42}, td.ArrayEntries{1: 58, 2: td.Ignore()}, "checks array %v", got) fmt.Println("Array pointer:", ok) ok = td.CmpArray(t, &got, (*[3]int)(nil), td.ArrayEntries{0: 42, 1: 58, 2: td.Ignore()}, "checks array %v", got) fmt.Println("Array pointer, nil model:", ok) }
Output: Simple array: true Array pointer: true Array pointer, nil model: true
Example (TypedArray) ¶
package main import ( "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := &testing.T{} type MyArray [3]int got := MyArray{42, 58, 26} ok := td.CmpArray(t, got, MyArray{42}, td.ArrayEntries{1: 58, 2: td.Ignore()}, "checks typed array %v", got) fmt.Println("Typed array:", ok) ok = td.CmpArray(t, &got, &MyArray{42}, td.ArrayEntries{1: 58, 2: td.Ignore()}, "checks pointer on typed array %v", got) fmt.Println("Pointer on a typed array:", ok) ok = td.CmpArray(t, &got, &MyArray{}, td.ArrayEntries{0: 42, 1: 58, 2: td.Ignore()}, "checks pointer on typed array %v", got) fmt.Println("Pointer on a typed array, empty model:", ok) ok = td.CmpArray(t, &got, (*MyArray)(nil), td.ArrayEntries{0: 42, 1: 58, 2: td.Ignore()}, "checks pointer on typed array %v", got) fmt.Println("Pointer on a typed array, nil model:", ok) }
Output: Typed array: true Pointer on a typed array: true Pointer on a typed array, empty model: true Pointer on a typed array, nil model: true
func CmpArrayEach ¶
CmpArrayEach is a shortcut for:
td.Cmp(t, got, td.ArrayEach(expectedValue), args...)
See ArrayEach for details.
Returns true if the test is OK, false if it fails.
If t is a *T then its Config field is inherited.
args... are optional and allow to name the test. This name is used in case of failure to qualify the test. If len(args) > 1 and the first item of args is a string and contains a '%' rune then fmt.Fprintf is used to compose the name, else args are passed to fmt.Fprint. Do not forget it is the name of the test, not the reason of a potential failure.
Example (Array) ¶
package main import ( "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := &testing.T{} got := [3]int{42, 58, 26} ok := td.CmpArrayEach(t, got, td.Between(25, 60), "checks each item of array %v is in [25 .. 60]", got) fmt.Println(ok) }
Output: true
Example (Slice) ¶
package main import ( "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := &testing.T{} got := []int{42, 58, 26} ok := td.CmpArrayEach(t, got, td.Between(25, 60), "checks each item of slice %v is in [25 .. 60]", got) fmt.Println(ok) }
Output: true
Example (TypedArray) ¶
package main import ( "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := &testing.T{} type MyArray [3]int got := MyArray{42, 58, 26} ok := td.CmpArrayEach(t, got, td.Between(25, 60), "checks each item of typed array %v is in [25 .. 60]", got) fmt.Println(ok) ok = td.CmpArrayEach(t, &got, td.Between(25, 60), "checks each item of typed array pointer %v is in [25 .. 60]", got) fmt.Println(ok) }
Output: true true
Example (TypedSlice) ¶
package main import ( "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := &testing.T{} type MySlice []int got := MySlice{42, 58, 26} ok := td.CmpArrayEach(t, got, td.Between(25, 60), "checks each item of typed slice %v is in [25 .. 60]", got) fmt.Println(ok) ok = td.CmpArrayEach(t, &got, td.Between(25, 60), "checks each item of typed slice pointer %v is in [25 .. 60]", got) fmt.Println(ok) }
Output: true true
func CmpBag ¶
CmpBag is a shortcut for:
td.Cmp(t, got, td.Bag(expectedItems...), args...)
See Bag for details.
Returns true if the test is OK, false if it fails.
If t is a *T then its Config field is inherited.
args... are optional and allow to name the test. This name is used in case of failure to qualify the test. If len(args) > 1 and the first item of args is a string and contains a '%' rune then fmt.Fprintf is used to compose the name, else args are passed to fmt.Fprint. Do not forget it is the name of the test, not the reason of a potential failure.
Example ¶
package main import ( "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := &testing.T{} got := []int{1, 3, 5, 8, 8, 1, 2} // Matches as all items are present ok := td.CmpBag(t, got, []any{1, 1, 2, 3, 5, 8, 8}, "checks all items are present, in any order") fmt.Println(ok) // Does not match as got contains 2 times 1 and 8, and these // duplicates are not expected ok = td.CmpBag(t, got, []any{1, 2, 3, 5, 8}, "checks all items are present, in any order") fmt.Println(ok) got = []int{1, 3, 5, 8, 2} // Duplicates of 1 and 8 are expected but not present in got ok = td.CmpBag(t, got, []any{1, 1, 2, 3, 5, 8, 8}, "checks all items are present, in any order") fmt.Println(ok) // Matches as all items are present ok = td.CmpBag(t, got, []any{1, 2, 3, 5, td.Gt(7)}, "checks all items are present, in any order") fmt.Println(ok) // When expected is already a non-[]any slice, it cannot be // flattened directly using expected... without copying it to a new // []any slice, then use td.Flatten! expected := []int{1, 2, 3, 5} ok = td.CmpBag(t, got, []any{td.Flatten(expected), td.Gt(7)}, "checks all expected items are present, in any order") fmt.Println(ok) }
Output: true false false true true
func CmpBetween ¶
func CmpBetween(t TestingT, got, from, to any, bounds BoundsKind, args ...any) bool
CmpBetween is a shortcut for:
td.Cmp(t, got, td.Between(from, to, bounds), args...)
See Between for details.
Between optional parameter bounds is here mandatory. BoundsInIn value should be passed to mimic its absence in original Between call.
Returns true if the test is OK, false if it fails.
If t is a *T then its Config field is inherited.
args... are optional and allow to name the test. This name is used in case of failure to qualify the test. If len(args) > 1 and the first item of args is a string and contains a '%' rune then fmt.Fprintf is used to compose the name, else args are passed to fmt.Fprint. Do not forget it is the name of the test, not the reason of a potential failure.
Example (Int) ¶
package main import ( "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := &testing.T{} got := 156 ok := td.CmpBetween(t, got, 154, 156, td.BoundsInIn, "checks %v is in [154 .. 156]", got) fmt.Println(ok) // BoundsInIn is implicit ok = td.CmpBetween(t, got, 154, 156, td.BoundsInIn, "checks %v is in [154 .. 156]", got) fmt.Println(ok) ok = td.CmpBetween(t, got, 154, 156, td.BoundsInOut, "checks %v is in [154 .. 156[", got) fmt.Println(ok) ok = td.CmpBetween(t, got, 154, 156, td.BoundsOutIn, "checks %v is in ]154 .. 156]", got) fmt.Println(ok) ok = td.CmpBetween(t, got, 154, 156, td.BoundsOutOut, "checks %v is in ]154 .. 156[", got) fmt.Println(ok) }
Output: true true false true false
Example (String) ¶
package main import ( "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := &testing.T{} got := "abc" ok := td.CmpBetween(t, got, "aaa", "abc", td.BoundsInIn, `checks "%v" is in ["aaa" .. "abc"]`, got) fmt.Println(ok) // BoundsInIn is implicit ok = td.CmpBetween(t, got, "aaa", "abc", td.BoundsInIn, `checks "%v" is in ["aaa" .. "abc"]`, got) fmt.Println(ok) ok = td.CmpBetween(t, got, "aaa", "abc", td.BoundsInOut, `checks "%v" is in ["aaa" .. "abc"[`, got) fmt.Println(ok) ok = td.CmpBetween(t, got, "aaa", "abc", td.BoundsOutIn, `checks "%v" is in ]"aaa" .. "abc"]`, got) fmt.Println(ok) ok = td.CmpBetween(t, got, "aaa", "abc", td.BoundsOutOut, `checks "%v" is in ]"aaa" .. "abc"[`, got) fmt.Println(ok) }
Output: true true false true false
Example (Time) ¶
package main import ( "fmt" "testing" "time" "github.com/maxatome/go-testdeep/td" ) func main() { t := &testing.T{} before := time.Now() occurredAt := time.Now() after := time.Now() ok := td.CmpBetween(t, occurredAt, before, after, td.BoundsInIn) fmt.Println("It occurred between before and after:", ok) type MyTime time.Time ok = td.CmpBetween(t, MyTime(occurredAt), MyTime(before), MyTime(after), td.BoundsInIn) fmt.Println("Same for convertible MyTime type:", ok) ok = td.CmpBetween(t, MyTime(occurredAt), before, after, td.BoundsInIn) fmt.Println("MyTime vs time.Time:", ok) ok = td.CmpBetween(t, occurredAt, before, 10*time.Second, td.BoundsInIn) fmt.Println("Using a time.Duration as TO:", ok) ok = td.CmpBetween(t, MyTime(occurredAt), MyTime(before), 10*time.Second, td.BoundsInIn) fmt.Println("Using MyTime as FROM and time.Duration as TO:", ok) }
Output: It occurred between before and after: true Same for convertible MyTime type: true MyTime vs time.Time: false Using a time.Duration as TO: true Using MyTime as FROM and time.Duration as TO: true
func CmpCap ¶
CmpCap is a shortcut for:
td.Cmp(t, got, td.Cap(expectedCap), args...)
See Cap for details.
Returns true if the test is OK, false if it fails.
If t is a *T then its Config field is inherited.
args... are optional and allow to name the test. This name is used in case of failure to qualify the test. If len(args) > 1 and the first item of args is a string and contains a '%' rune then fmt.Fprintf is used to compose the name, else args are passed to fmt.Fprint. Do not forget it is the name of the test, not the reason of a potential failure.
Example ¶
package main import ( "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := &testing.T{} got := make([]int, 0, 12) ok := td.CmpCap(t, got, 12, "checks %v capacity is 12", got) fmt.Println(ok) ok = td.CmpCap(t, got, 0, "checks %v capacity is 0", got) fmt.Println(ok) got = nil ok = td.CmpCap(t, got, 0, "checks %v capacity is 0", got) fmt.Println(ok) }
Output: true false true
Example (Operator) ¶
package main import ( "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := &testing.T{} got := make([]int, 0, 12) ok := td.CmpCap(t, got, td.Between(10, 12), "checks %v capacity is in [10 .. 12]", got) fmt.Println(ok) ok = td.CmpCap(t, got, td.Gt(10), "checks %v capacity is in [10 .. 12]", got) fmt.Println(ok) }
Output: true true
func CmpCode ¶
CmpCode is a shortcut for:
td.Cmp(t, got, td.Code(fn), args...)
See Code for details.
Returns true if the test is OK, false if it fails.
If t is a *T then its Config field is inherited.
args... are optional and allow to name the test. This name is used in case of failure to qualify the test. If len(args) > 1 and the first item of args is a string and contains a '%' rune then fmt.Fprintf is used to compose the name, else args are passed to fmt.Fprint. Do not forget it is the name of the test, not the reason of a potential failure.
Example ¶
package main import ( "fmt" "strconv" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := &testing.T{} got := "12" ok := td.CmpCode(t, got, func(num string) bool { n, err := strconv.Atoi(num) return err == nil && n > 10 && n < 100 }, "checks string `%s` contains a number and this number is in ]10 .. 100[", got) fmt.Println(ok) // Same with failure reason ok = td.CmpCode(t, got, func(num string) (bool, string) { n, err := strconv.Atoi(num) if err != nil { return false, "not a number" } if n > 10 && n < 100 { return true, "" } return false, "not in ]10 .. 100[" }, "checks string `%s` contains a number and this number is in ]10 .. 100[", got) fmt.Println(ok) // Same with failure reason thanks to error ok = td.CmpCode(t, got, func(num string) error { n, err := strconv.Atoi(num) if err != nil { return err } if n > 10 && n < 100 { return nil } return fmt.Errorf("%d not in ]10 .. 100[", n) }, "checks string `%s` contains a number and this number is in ]10 .. 100[", got) fmt.Println(ok) }
Output: true true true
Example (Custom) ¶
package main import ( "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := &testing.T{} got := 123 ok := td.CmpCode(t, got, func(t *td.T, num int) { t.Cmp(num, 123) }) fmt.Println("with one *td.T:", ok) ok = td.CmpCode(t, got, func(assert, require *td.T, num int) { assert.Cmp(num, 123) require.Cmp(num, 123) }) fmt.Println("with assert & require *td.T:", ok) }
Output: with one *td.T: true with assert & require *td.T: true
func CmpContains ¶
CmpContains is a shortcut for:
td.Cmp(t, got, td.Contains(expectedValue), args...)
See Contains for details.
Returns true if the test is OK, false if it fails.
If t is a *T then its Config field is inherited.
args... are optional and allow to name the test. This name is used in case of failure to qualify the test. If len(args) > 1 and the first item of args is a string and contains a '%' rune then fmt.Fprintf is used to compose the name, else args are passed to fmt.Fprint. Do not forget it is the name of the test, not the reason of a potential failure.
Example (ArraySlice) ¶
package main import ( "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := &testing.T{} ok := td.CmpContains(t, [...]int{11, 22, 33, 44}, 22) fmt.Println("array contains 22:", ok) ok = td.CmpContains(t, [...]int{11, 22, 33, 44}, td.Between(20, 25)) fmt.Println("array contains at least one item in [20 .. 25]:", ok) ok = td.CmpContains(t, []int{11, 22, 33, 44}, 22) fmt.Println("slice contains 22:", ok) ok = td.CmpContains(t, []int{11, 22, 33, 44}, td.Between(20, 25)) fmt.Println("slice contains at least one item in [20 .. 25]:", ok) ok = td.CmpContains(t, []int{11, 22, 33, 44}, []int{22, 33}) fmt.Println("slice contains the sub-slice [22, 33]:", ok) }
Output: array contains 22: true array contains at least one item in [20 .. 25]: true slice contains 22: true slice contains at least one item in [20 .. 25]: true slice contains the sub-slice [22, 33]: true
Example (Error) ¶
package main import ( "errors" "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := &testing.T{} got := errors.New("foobar") ok := td.CmpContains(t, got, "oob", "checks %s", got) fmt.Println("contains `oob` string:", ok) ok = td.CmpContains(t, got, 'b', "checks %s", got) fmt.Println("contains 'b' rune:", ok) ok = td.CmpContains(t, got, byte('a'), "checks %s", got) fmt.Println("contains 'a' byte:", ok) ok = td.CmpContains(t, got, td.Between('n', 'p'), "checks %s", got) fmt.Println("contains at least one character ['n' .. 'p']:", ok) }
Output: contains `oob` string: true contains 'b' rune: true contains 'a' byte: true contains at least one character ['n' .. 'p']: true
Example (Map) ¶
package main import ( "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := &testing.T{} ok := td.CmpContains(t, map[string]int{"foo": 11, "bar": 22, "zip": 33}, 22) fmt.Println("map contains value 22:", ok) ok = td.CmpContains(t, map[string]int{"foo": 11, "bar": 22, "zip": 33}, td.Between(20, 25)) fmt.Println("map contains at least one value in [20 .. 25]:", ok) }
Output: map contains value 22: true map contains at least one value in [20 .. 25]: true
Example (Nil) ¶
package main import ( "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := &testing.T{} num := 123 got := [...]*int{&num, nil} ok := td.CmpContains(t, got, nil) fmt.Println("array contains untyped nil:", ok) ok = td.CmpContains(t, got, (*int)(nil)) fmt.Println("array contains *int nil:", ok) ok = td.CmpContains(t, got, td.Nil()) fmt.Println("array contains Nil():", ok) ok = td.CmpContains(t, got, (*byte)(nil)) fmt.Println("array contains *byte nil:", ok) // types differ: *byte ≠ *int }
Output: array contains untyped nil: true array contains *int nil: true array contains Nil(): true array contains *byte nil: false
Example (String) ¶
package main import ( "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := &testing.T{} got := "foobar" ok := td.CmpContains(t, got, "oob", "checks %s", got) fmt.Println("contains `oob` string:", ok) ok = td.CmpContains(t, got, []byte("oob"), "checks %s", got) fmt.Println("contains `oob` []byte:", ok) ok = td.CmpContains(t, got, 'b', "checks %s", got) fmt.Println("contains 'b' rune:", ok) ok = td.CmpContains(t, got, byte('a'), "checks %s", got) fmt.Println("contains 'a' byte:", ok) ok = td.CmpContains(t, got, td.Between('n', 'p'), "checks %s", got) fmt.Println("contains at least one character ['n' .. 'p']:", ok) }
Output: contains `oob` string: true contains `oob` []byte: true contains 'b' rune: true contains 'a' byte: true contains at least one character ['n' .. 'p']: true
Example (Stringer) ¶
package main import ( "bytes" "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := &testing.T{} // bytes.Buffer implements fmt.Stringer got := bytes.NewBufferString("foobar") ok := td.CmpContains(t, got, "oob", "checks %s", got) fmt.Println("contains `oob` string:", ok) ok = td.CmpContains(t, got, 'b', "checks %s", got) fmt.Println("contains 'b' rune:", ok) ok = td.CmpContains(t, got, byte('a'), "checks %s", got) fmt.Println("contains 'a' byte:", ok) ok = td.CmpContains(t, got, td.Between('n', 'p'), "checks %s", got) fmt.Println("contains at least one character ['n' .. 'p']:", ok) }
Output: contains `oob` string: true contains 'b' rune: true contains 'a' byte: true contains at least one character ['n' .. 'p']: true
func CmpContainsKey ¶
CmpContainsKey is a shortcut for:
td.Cmp(t, got, td.ContainsKey(expectedValue), args...)
See ContainsKey for details.
Returns true if the test is OK, false if it fails.
If t is a *T then its Config field is inherited.
args... are optional and allow to name the test. This name is used in case of failure to qualify the test. If len(args) > 1 and the first item of args is a string and contains a '%' rune then fmt.Fprintf is used to compose the name, else args are passed to fmt.Fprint. Do not forget it is the name of the test, not the reason of a potential failure.
Example ¶
package main import ( "fmt" "strings" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := &testing.T{} ok := td.CmpContainsKey(t, map[string]int{"foo": 11, "bar": 22, "zip": 33}, "foo") fmt.Println(`map contains key "foo":`, ok) ok = td.CmpContainsKey(t, map[int]bool{12: true, 24: false, 42: true, 51: false}, td.Between(40, 50)) fmt.Println("map contains at least a key in [40 .. 50]:", ok) ok = td.CmpContainsKey(t, map[string]int{"FOO": 11, "bar": 22, "zip": 33}, td.Smuggle(strings.ToLower, "foo")) fmt.Println(`map contains key "foo" without taking case into account:`, ok) }
Output: map contains key "foo": true map contains at least a key in [40 .. 50]: true map contains key "foo" without taking case into account: true
Example (Nil) ¶
package main import ( "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := &testing.T{} num := 1234 got := map[*int]bool{&num: false, nil: true} ok := td.CmpContainsKey(t, got, nil) fmt.Println("map contains untyped nil key:", ok) ok = td.CmpContainsKey(t, got, (*int)(nil)) fmt.Println("map contains *int nil key:", ok) ok = td.CmpContainsKey(t, got, td.Nil()) fmt.Println("map contains Nil() key:", ok) ok = td.CmpContainsKey(t, got, (*byte)(nil)) fmt.Println("map contains *byte nil key:", ok) // types differ: *byte ≠ *int }
Output: map contains untyped nil key: true map contains *int nil key: true map contains Nil() key: true map contains *byte nil key: false
func CmpDeeply ¶
CmpDeeply works the same as Cmp and is still available for compatibility purpose. Use shorter Cmp in new code.
got := "foobar" td.CmpDeeply(t, got, "foobar") // succeeds td.CmpDeeply(t, got, td.HasPrefix("foo")) // succeeds
func CmpEmpty ¶
CmpEmpty is a shortcut for:
td.Cmp(t, got, td.Empty(), args...)
See Empty for details.
Returns true if the test is OK, false if it fails.
If t is a *T then its Config field is inherited.
args... are optional and allow to name the test. This name is used in case of failure to qualify the test. If len(args) > 1 and the first item of args is a string and contains a '%' rune then fmt.Fprintf is used to compose the name, else args are passed to fmt.Fprint. Do not forget it is the name of the test, not the reason of a potential failure.
Example ¶
package main import ( "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := &testing.T{} ok := td.CmpEmpty(t, nil) // special case: nil is considered empty fmt.Println(ok) // fails, typed nil is not empty (expect for channel, map, slice or // pointers on array, channel, map slice and strings) ok = td.CmpEmpty(t, (*int)(nil)) fmt.Println(ok) ok = td.CmpEmpty(t, "") fmt.Println(ok) // Fails as 0 is a number, so not empty. Use Zero() instead ok = td.CmpEmpty(t, 0) fmt.Println(ok) ok = td.CmpEmpty(t, (map[string]int)(nil)) fmt.Println(ok) ok = td.CmpEmpty(t, map[string]int{}) fmt.Println(ok) ok = td.CmpEmpty(t, ([]int)(nil)) fmt.Println(ok) ok = td.CmpEmpty(t, []int{}) fmt.Println(ok) ok = td.CmpEmpty(t, []int{3}) // fails, as not empty fmt.Println(ok) ok = td.CmpEmpty(t, [3]int{}) // fails, Empty() is not Zero()! fmt.Println(ok) }
Output: true false true false true true true true false false
Example (Pointers) ¶
package main import ( "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := &testing.T{} type MySlice []int ok := td.CmpEmpty(t, MySlice{}) // Ptr() not needed fmt.Println(ok) ok = td.CmpEmpty(t, &MySlice{}) fmt.Println(ok) l1 := &MySlice{} l2 := &l1 l3 := &l2 ok = td.CmpEmpty(t, &l3) fmt.Println(ok) // Works the same for array, map, channel and string // But not for others types as: type MyStruct struct { Value int } ok = td.CmpEmpty(t, &MyStruct{}) // fails, use Zero() instead fmt.Println(ok) }
Output: true true true false
func CmpError ¶
CmpError checks that got is non-nil error.
_, err := MyFunction(1, 2, 3) td.CmpError(t, err, "MyFunction(1, 2, 3) should return an error")
args... are optional and allow to name the test. This name is used in case of failure to qualify the test. If len(args) > 1 and the first item of args is a string and contains a '%' rune then fmt.Fprintf is used to compose the name, else args are passed to fmt.Fprint. Do not forget it is the name of the test, not the reason of a potential failure.
Example ¶
package main import ( "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := &testing.T{} got := fmt.Errorf("Error #%d", 42) ok := td.CmpError(t, got, "An error occurred") fmt.Println(ok) got = nil ok = td.CmpError(t, got, "An error occurred") // fails fmt.Println(ok) }
Output: true false
func CmpErrorIs ¶ added in v1.13.0
CmpErrorIs is a shortcut for:
td.Cmp(t, got, td.ErrorIs(expected), args...)
See ErrorIs for details.
Returns true if the test is OK, false if it fails.
If t is a *T then its Config field is inherited.
args... are optional and allow to name the test. This name is used in case of failure to qualify the test. If len(args) > 1 and the first item of args is a string and contains a '%' rune then fmt.Fprintf is used to compose the name, else args are passed to fmt.Fprint. Do not forget it is the name of the test, not the reason of a potential failure.
Example ¶
package main import ( "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := &testing.T{} err1 := fmt.Errorf("failure1") err2 := fmt.Errorf("failure2: %w", err1) err3 := fmt.Errorf("failure3: %w", err2) err := fmt.Errorf("failure4: %w", err3) ok := td.CmpErrorIs(t, err, err) fmt.Println("error is itself:", ok) ok = td.CmpErrorIs(t, err, err1) fmt.Println("error is also err1:", ok) ok = td.CmpErrorIs(t, err1, err) fmt.Println("err1 is err:", ok) }
Output: error is itself: true error is also err1: true err1 is err: false
func CmpFalse ¶
CmpFalse is a shortcut for:
td.Cmp(t, got, false, args...)
Returns true if the test is OK, false if it fails.
td.CmpFalse(t, IsAvailable(x), "x should not be available")
args... are optional and allow to name the test. This name is used in case of failure to qualify the test. If len(args) > 1 and the first item of args is a string and contains a '%' rune then fmt.Fprintf is used to compose the name, else args are passed to fmt.Fprint. Do not forget it is the name of the test, not the reason of a potential failure.
Example ¶
package main import ( "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := &testing.T{} got := false ok := td.CmpFalse(t, got, "check that got is false!") fmt.Println(ok) got = true ok = td.CmpFalse(t, got, "check that got is false!") fmt.Println(ok) }
Output: true false
func CmpFirst ¶ added in v1.13.0
CmpFirst is a shortcut for:
td.Cmp(t, got, td.First(filter, expectedValue), args...)
See First for details.
Returns true if the test is OK, false if it fails.
If t is a *T then its Config field is inherited.
args... are optional and allow to name the test. This name is used in case of failure to qualify the test. If len(args) > 1 and the first item of args is a string and contains a '%' rune then fmt.Fprintf is used to compose the name, else args are passed to fmt.Fprint. Do not forget it is the name of the test, not the reason of a potential failure.
Example (Classic) ¶
package main import ( "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := &testing.T{} got := []int{-3, -2, -1, 0, 1, 2, 3} ok := td.CmpFirst(t, got, td.Gt(0), 1) fmt.Println("first positive number is 1:", ok) isEven := func(x int) bool { return x%2 == 0 } ok = td.CmpFirst(t, got, isEven, -2) fmt.Println("first even number is -2:", ok) ok = td.CmpFirst(t, got, isEven, td.Lt(0)) fmt.Println("first even number is < 0:", ok) ok = td.CmpFirst(t, got, isEven, td.Code(isEven)) fmt.Println("first even number is well even:", ok) }
Output: first positive number is 1: true first even number is -2: true first even number is < 0: true first even number is well even: true
Example (Empty) ¶
package main import ( "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := &testing.T{} ok := td.CmpFirst(t, ([]int)(nil), td.Gt(0), td.Gt(0)) fmt.Println("first in nil slice:", ok) ok = td.CmpFirst(t, []int{}, td.Gt(0), td.Gt(0)) fmt.Println("first in empty slice:", ok) ok = td.CmpFirst(t, &[]int{}, td.Gt(0), td.Gt(0)) fmt.Println("first in empty pointed slice:", ok) ok = td.CmpFirst(t, [0]int{}, td.Gt(0), td.Gt(0)) fmt.Println("first in empty array:", ok) }
Output: first in nil slice: false first in empty slice: false first in empty pointed slice: false first in empty array: false
Example (Struct) ¶
package main import ( "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := &testing.T{} type Person struct { Fullname string `json:"fullname"` Age int `json:"age"` } got := []*Person{ { Fullname: "Bob Foobar", Age: 42, }, { Fullname: "Alice Bingo", Age: 37, }, } ok := td.CmpFirst(t, got, td.Smuggle("Age", td.Gt(30)), td.Smuggle("Fullname", "Bob Foobar")) fmt.Println("first person.Age > 30 → Bob:", ok) ok = td.CmpFirst(t, got, td.JSONPointer("/age", td.Gt(30)), td.SuperJSONOf(`{"fullname":"Bob Foobar"}`)) fmt.Println("first person.Age > 30 → Bob, using JSON:", ok) ok = td.CmpFirst(t, got, td.JSONPointer("/age", td.Gt(30)), td.JSONPointer("/fullname", td.HasPrefix("Bob"))) fmt.Println("first person.Age > 30 → Bob, using JSONPointer:", ok) }
Output: first person.Age > 30 → Bob: true first person.Age > 30 → Bob, using JSON: true first person.Age > 30 → Bob, using JSONPointer: true
func CmpGrep ¶ added in v1.13.0
CmpGrep is a shortcut for:
td.Cmp(t, got, td.Grep(filter, expectedValue), args...)
See Grep for details.
Returns true if the test is OK, false if it fails.
If t is a *T then its Config field is inherited.
args... are optional and allow to name the test. This name is used in case of failure to qualify the test. If len(args) > 1 and the first item of args is a string and contains a '%' rune then fmt.Fprintf is used to compose the name, else args are passed to fmt.Fprint. Do not forget it is the name of the test, not the reason of a potential failure.
Example (Classic) ¶
package main import ( "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := &testing.T{} got := []int{-3, -2, -1, 0, 1, 2, 3} ok := td.CmpGrep(t, got, td.Gt(0), []int{1, 2, 3}) fmt.Println("check positive numbers:", ok) isEven := func(x int) bool { return x%2 == 0 } ok = td.CmpGrep(t, got, isEven, []int{-2, 0, 2}) fmt.Println("even numbers are -2, 0 and 2:", ok) ok = td.CmpGrep(t, got, isEven, td.Set(0, 2, -2)) fmt.Println("even numbers are also 0, 2 and -2:", ok) ok = td.CmpGrep(t, got, isEven, td.ArrayEach(td.Code(isEven))) fmt.Println("even numbers are each even:", ok) }
Output: check positive numbers: true even numbers are -2, 0 and 2: true even numbers are also 0, 2 and -2: true even numbers are each even: true
Example (Nil) ¶
package main import ( "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := &testing.T{} var got []int ok := td.CmpGrep(t, got, td.Gt(0), ([]int)(nil)) fmt.Println("typed []int nil:", ok) ok = td.CmpGrep(t, got, td.Gt(0), ([]string)(nil)) fmt.Println("typed []string nil:", ok) ok = td.CmpGrep(t, got, td.Gt(0), td.Nil()) fmt.Println("td.Nil:", ok) ok = td.CmpGrep(t, got, td.Gt(0), []int{}) fmt.Println("empty non-nil slice:", ok) }
Output: typed []int nil: true typed []string nil: false td.Nil: true empty non-nil slice: false
Example (Struct) ¶
package main import ( "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := &testing.T{} type Person struct { Fullname string `json:"fullname"` Age int `json:"age"` } got := []*Person{ { Fullname: "Bob Foobar", Age: 42, }, { Fullname: "Alice Bingo", Age: 27, }, } ok := td.CmpGrep(t, got, td.Smuggle("Age", td.Gt(30)), td.All( td.Len(1), td.ArrayEach(td.Smuggle("Fullname", "Bob Foobar")), )) fmt.Println("person.Age > 30 → only Bob:", ok) ok = td.CmpGrep(t, got, td.JSONPointer("/age", td.Gt(30)), td.JSON(`[ SuperMapOf({"fullname":"Bob Foobar"}) ]`)) fmt.Println("person.Age > 30 → only Bob, using JSON:", ok) }
Output: person.Age > 30 → only Bob: true person.Age > 30 → only Bob, using JSON: true
func CmpGt ¶
CmpGt is a shortcut for:
td.Cmp(t, got, td.Gt(minExpectedValue), args...)
See Gt for details.
Returns true if the test is OK, false if it fails.
If t is a *T then its Config field is inherited.
args... are optional and allow to name the test. This name is used in case of failure to qualify the test. If len(args) > 1 and the first item of args is a string and contains a '%' rune then fmt.Fprintf is used to compose the name, else args are passed to fmt.Fprint. Do not forget it is the name of the test, not the reason of a potential failure.
Example (Int) ¶
package main import ( "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := &testing.T{} got := 156 ok := td.CmpGt(t, got, 155, "checks %v is > 155", got) fmt.Println(ok) ok = td.CmpGt(t, got, 156, "checks %v is > 156", got) fmt.Println(ok) }
Output: true false
Example (String) ¶
package main import ( "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := &testing.T{} got := "abc" ok := td.CmpGt(t, got, "abb", `checks "%v" is > "abb"`, got) fmt.Println(ok) ok = td.CmpGt(t, got, "abc", `checks "%v" is > "abc"`, got) fmt.Println(ok) }
Output: true false
func CmpGte ¶
CmpGte is a shortcut for:
td.Cmp(t, got, td.Gte(minExpectedValue), args...)
See Gte for details.
Returns true if the test is OK, false if it fails.
If t is a *T then its Config field is inherited.
args... are optional and allow to name the test. This name is used in case of failure to qualify the test. If len(args) > 1 and the first item of args is a string and contains a '%' rune then fmt.Fprintf is used to compose the name, else args are passed to fmt.Fprint. Do not forget it is the name of the test, not the reason of a potential failure.
Example (Int) ¶
package main import ( "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := &testing.T{} got := 156 ok := td.CmpGte(t, got, 156, "checks %v is ≥ 156", got) fmt.Println(ok) ok = td.CmpGte(t, got, 155, "checks %v is ≥ 155", got) fmt.Println(ok) ok = td.CmpGte(t, got, 157, "checks %v is ≥ 157", got) fmt.Println(ok) }
Output: true true false
Example (String) ¶
package main import ( "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := &testing.T{} got := "abc" ok := td.CmpGte(t, got, "abc", `checks "%v" is ≥ "abc"`, got) fmt.Println(ok) ok = td.CmpGte(t, got, "abb", `checks "%v" is ≥ "abb"`, got) fmt.Println(ok) ok = td.CmpGte(t, got, "abd", `checks "%v" is ≥ "abd"`, got) fmt.Println(ok) }
Output: true true false
func CmpHasPrefix ¶
CmpHasPrefix is a shortcut for:
td.Cmp(t, got, td.HasPrefix(expected), args...)
See HasPrefix for details.
Returns true if the test is OK, false if it fails.
If t is a *T then its Config field is inherited.
args... are optional and allow to name the test. This name is used in case of failure to qualify the test. If len(args) > 1 and the first item of args is a string and contains a '%' rune then fmt.Fprintf is used to compose the name, else args are passed to fmt.Fprint. Do not forget it is the name of the test, not the reason of a potential failure.
Example ¶
package main import ( "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := &testing.T{} got := "foobar" ok := td.CmpHasPrefix(t, got, "foo", "checks %s", got) fmt.Println("using string:", ok) ok = td.Cmp(t, []byte(got), td.HasPrefix("foo"), "checks %s", got) fmt.Println("using []byte:", ok) }
Output: using string: true using []byte: true
Example (Error) ¶
package main import ( "errors" "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := &testing.T{} got := errors.New("foobar") ok := td.CmpHasPrefix(t, got, "foo", "checks %s", got) fmt.Println(ok) }
Output: true
Example (Stringer) ¶
package main import ( "bytes" "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := &testing.T{} // bytes.Buffer implements fmt.Stringer got := bytes.NewBufferString("foobar") ok := td.CmpHasPrefix(t, got, "foo", "checks %s", got) fmt.Println(ok) }
Output: true
func CmpHasSuffix ¶
CmpHasSuffix is a shortcut for:
td.Cmp(t, got, td.HasSuffix(expected), args...)
See HasSuffix for details.
Returns true if the test is OK, false if it fails.
If t is a *T then its Config field is inherited.
args... are optional and allow to name the test. This name is used in case of failure to qualify the test. If len(args) > 1 and the first item of args is a string and contains a '%' rune then fmt.Fprintf is used to compose the name, else args are passed to fmt.Fprint. Do not forget it is the name of the test, not the reason of a potential failure.
Example ¶
package main import ( "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := &testing.T{} got := "foobar" ok := td.CmpHasSuffix(t, got, "bar", "checks %s", got) fmt.Println("using string:", ok) ok = td.Cmp(t, []byte(got), td.HasSuffix("bar"), "checks %s", got) fmt.Println("using []byte:", ok) }
Output: using string: true using []byte: true
Example (Error) ¶
package main import ( "errors" "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := &testing.T{} got := errors.New("foobar") ok := td.CmpHasSuffix(t, got, "bar", "checks %s", got) fmt.Println(ok) }
Output: true
Example (Stringer) ¶
package main import ( "bytes" "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := &testing.T{} // bytes.Buffer implements fmt.Stringer got := bytes.NewBufferString("foobar") ok := td.CmpHasSuffix(t, got, "bar", "checks %s", got) fmt.Println(ok) }
Output: true
func CmpIsa ¶
CmpIsa is a shortcut for:
td.Cmp(t, got, td.Isa(model), args...)
See Isa for details.
Returns true if the test is OK, false if it fails.
If t is a *T then its Config field is inherited.
args... are optional and allow to name the test. This name is used in case of failure to qualify the test. If len(args) > 1 and the first item of args is a string and contains a '%' rune then fmt.Fprintf is used to compose the name, else args are passed to fmt.Fprint. Do not forget it is the name of the test, not the reason of a potential failure.
Example ¶
package main import ( "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := &testing.T{} type TstStruct struct { Field int } got := TstStruct{Field: 1} ok := td.CmpIsa(t, got, TstStruct{}, "checks got is a TstStruct") fmt.Println(ok) ok = td.CmpIsa(t, got, &TstStruct{}, "checks got is a pointer on a TstStruct") fmt.Println(ok) ok = td.CmpIsa(t, &got, &TstStruct{}, "checks &got is a pointer on a TstStruct") fmt.Println(ok) }
Output: true false true
Example (Interface) ¶
package main import ( "bytes" "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := &testing.T{} got := bytes.NewBufferString("foobar") ok := td.CmpIsa(t, got, (*fmt.Stringer)(nil), "checks got implements fmt.Stringer interface") fmt.Println(ok) errGot := fmt.Errorf("An error #%d occurred", 123) ok = td.CmpIsa(t, errGot, (*error)(nil), "checks errGot is a *error or implements error interface") fmt.Println(ok) // As nil, is passed below, it is not an interface but nil… So it // does not match errGot = nil ok = td.CmpIsa(t, errGot, (*error)(nil), "checks errGot is a *error or implements error interface") fmt.Println(ok) // BUT if its address is passed, now it is OK as the types match ok = td.CmpIsa(t, &errGot, (*error)(nil), "checks &errGot is a *error or implements error interface") fmt.Println(ok) }
Output: true true false true
func CmpJSON ¶
CmpJSON is a shortcut for:
td.Cmp(t, got, td.JSON(expectedJSON, params...), args...)
See JSON for details.
Returns true if the test is OK, false if it fails.
If t is a *T then its Config field is inherited.
args... are optional and allow to name the test. This name is used in case of failure to qualify the test. If len(args) > 1 and the first item of args is a string and contains a '%' rune then fmt.Fprintf is used to compose the name, else args are passed to fmt.Fprint. Do not forget it is the name of the test, not the reason of a potential failure.
Example (Basic) ¶
package main import ( "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := &testing.T{} got := &struct { Fullname string `json:"fullname"` Age int `json:"age"` }{ Fullname: "Bob", Age: 42, } ok := td.CmpJSON(t, got, `{"age":42,"fullname":"Bob"}`, nil) fmt.Println("check got with age then fullname:", ok) ok = td.CmpJSON(t, got, `{"fullname":"Bob","age":42}`, nil) fmt.Println("check got with fullname then age:", ok) ok = td.CmpJSON(t, got, ` // This should be the JSON representation of a struct { // A person: "fullname": "Bob", // The name of this person "age": 42 /* The age of this person: - 42 of course - to demonstrate a multi-lines comment */ }`, nil) fmt.Println("check got with nicely formatted and commented JSON:", ok) ok = td.CmpJSON(t, got, `{"fullname":"Bob","age":42,"gender":"male"}`, nil) fmt.Println("check got with gender field:", ok) ok = td.CmpJSON(t, got, `{"fullname":"Bob"}`, nil) fmt.Println("check got with fullname only:", ok) ok = td.CmpJSON(t, true, `true`, nil) fmt.Println("check boolean got is true:", ok) ok = td.CmpJSON(t, 42, `42`, nil) fmt.Println("check numeric got is 42:", ok) got = nil ok = td.CmpJSON(t, got, `null`, nil) fmt.Println("check nil got is null:", ok) }
Output: check got with age then fullname: true check got with fullname then age: true check got with nicely formatted and commented JSON: true check got with gender field: false check got with fullname only: false check boolean got is true: true check numeric got is 42: true check nil got is null: true
Example (Embedding) ¶
package main import ( "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := &testing.T{} got := &struct { Fullname string `json:"fullname"` Age int `json:"age"` }{ Fullname: "Bob Foobar", Age: 42, } ok := td.CmpJSON(t, got, `{"age": NotZero(), "fullname": NotEmpty()}`, nil) fmt.Println("check got with simple operators:", ok) ok = td.CmpJSON(t, got, `{"age": $^NotZero, "fullname": $^NotEmpty}`, nil) fmt.Println("check got with operator shortcuts:", ok) ok = td.CmpJSON(t, got, ` { "age": Between(40, 42, "]]"), // in ]40; 42] "fullname": All( HasPrefix("Bob"), HasSuffix("bar") // ← comma is optional here ) }`, nil) fmt.Println("check got with complex operators:", ok) ok = td.CmpJSON(t, got, ` { "age": Between(40, 42, "]["), // in ]40; 42[ → 42 excluded "fullname": All( HasPrefix("Bob"), HasSuffix("bar"), ) }`, nil) fmt.Println("check got with complex operators:", ok) ok = td.CmpJSON(t, got, ` { "age": Between($1, $2, $3), // in ]40; 42] "fullname": All( HasPrefix($4), HasSuffix("bar") // ← comma is optional here ) }`, []any{40, 42, td.BoundsOutIn, "Bob"}) fmt.Println("check got with complex operators, w/placeholder args:", ok) }
Output: check got with simple operators: true check got with operator shortcuts: true check got with complex operators: true check got with complex operators: false check got with complex operators, w/placeholder args: true
Example (File) ¶
package main import ( "fmt" "os" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := &testing.T{} got := &struct { Fullname string `json:"fullname"` Age int `json:"age"` Gender string `json:"gender"` }{ Fullname: "Bob Foobar", Age: 42, Gender: "male", } tmpDir, err := os.MkdirTemp("", "") if err != nil { t.Fatal(err) } defer os.RemoveAll(tmpDir) // clean up filename := tmpDir + "/test.json" if err = os.WriteFile(filename, []byte(` { "fullname": "$name", "age": "$age", "gender": "$gender" }`), 0644); err != nil { t.Fatal(err) } // OK let's test with this file ok := td.CmpJSON(t, got, filename, []any{td.Tag("name", td.HasPrefix("Bob")), td.Tag("age", td.Between(40, 45)), td.Tag("gender", td.Re(`^(male|female)\z`))}) fmt.Println("Full match from file name:", ok) // When the file is already open file, err := os.Open(filename) if err != nil { t.Fatal(err) } ok = td.CmpJSON(t, got, file, []any{td.Tag("name", td.HasPrefix("Bob")), td.Tag("age", td.Between(40, 45)), td.Tag("gender", td.Re(`^(male|female)\z`))}) fmt.Println("Full match from io.Reader:", ok) }
Output: Full match from file name: true Full match from io.Reader: true
Example (Placeholders) ¶
package main import ( "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := &testing.T{} type Person struct { Fullname string `json:"fullname"` Age int `json:"age"` Children []*Person `json:"children,omitempty"` } got := &Person{ Fullname: "Bob Foobar", Age: 42, } ok := td.CmpJSON(t, got, `{"age": $1, "fullname": $2}`, []any{42, "Bob Foobar"}) fmt.Println("check got with numeric placeholders without operators:", ok) ok = td.CmpJSON(t, got, `{"age": $1, "fullname": $2}`, []any{td.Between(40, 45), td.HasSuffix("Foobar")}) fmt.Println("check got with numeric placeholders:", ok) ok = td.CmpJSON(t, got, `{"age": "$1", "fullname": "$2"}`, []any{td.Between(40, 45), td.HasSuffix("Foobar")}) fmt.Println("check got with double-quoted numeric placeholders:", ok) ok = td.CmpJSON(t, got, `{"age": $age, "fullname": $name}`, []any{td.Tag("age", td.Between(40, 45)), td.Tag("name", td.HasSuffix("Foobar"))}) fmt.Println("check got with named placeholders:", ok) got.Children = []*Person{ {Fullname: "Alice", Age: 28}, {Fullname: "Brian", Age: 22}, } ok = td.CmpJSON(t, got, `{"age": $age, "fullname": $name, "children": $children}`, []any{td.Tag("age", td.Between(40, 45)), td.Tag("name", td.HasSuffix("Foobar")), td.Tag("children", td.Bag( &Person{Fullname: "Brian", Age: 22}, &Person{Fullname: "Alice", Age: 28}, ))}) fmt.Println("check got w/named placeholders, and children w/go structs:", ok) ok = td.CmpJSON(t, got, `{"age": Between($1, $2), "fullname": HasSuffix($suffix), "children": Len(2)}`, []any{40, 45, td.Tag("suffix", "Foobar")}) fmt.Println("check got w/num & named placeholders:", ok) }
Output: check got with numeric placeholders without operators: true check got with numeric placeholders: true check got with double-quoted numeric placeholders: true check got with named placeholders: true check got w/named placeholders, and children w/go structs: true check got w/num & named placeholders: true
Example (RawStrings) ¶
package main import ( "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := &testing.T{} type details struct { Address string `json:"address"` Car string `json:"car"` } got := &struct { Fullname string `json:"fullname"` Age int `json:"age"` Details details `json:"details"` }{ Fullname: "Foo Bar", Age: 42, Details: details{ Address: "something", Car: "Peugeot", }, } ok := td.CmpJSON(t, got, ` { "fullname": HasPrefix("Foo"), "age": Between(41, 43), "details": SuperMapOf({ "address": NotEmpty, // () are optional when no parameters "car": Any("Peugeot", "Tesla", "Jeep") // any of these }) }`, nil) fmt.Println("Original:", ok) ok = td.CmpJSON(t, got, ` { "fullname": "$^HasPrefix(\"Foo\")", "age": "$^Between(41, 43)", "details": "$^SuperMapOf({\n\"address\": NotEmpty,\n\"car\": Any(\"Peugeot\", \"Tesla\", \"Jeep\")\n})" }`, nil) fmt.Println("JSON compliant:", ok) ok = td.CmpJSON(t, got, ` { "fullname": "$^HasPrefix(\"Foo\")", "age": "$^Between(41, 43)", "details": "$^SuperMapOf({ \"address\": NotEmpty, // () are optional when no parameters \"car\": Any(\"Peugeot\", \"Tesla\", \"Jeep\") // any of these })" }`, nil) fmt.Println("JSON multilines strings:", ok) ok = td.CmpJSON(t, got, ` { "fullname": "$^HasPrefix(r<Foo>)", "age": "$^Between(41, 43)", "details": "$^SuperMapOf({ r<address>: NotEmpty, // () are optional when no parameters r<car>: Any(r<Peugeot>, r<Tesla>, r<Jeep>) // any of these })" }`, nil) fmt.Println("Raw strings:", ok) }
Output: Original: true JSON compliant: true JSON multilines strings: true Raw strings: true
func CmpJSONPointer ¶ added in v1.8.0
CmpJSONPointer is a shortcut for:
td.Cmp(t, got, td.JSONPointer(ptr, expectedValue), args...)
See JSONPointer for details.
Returns true if the test is OK, false if it fails.
If t is a *T then its Config field is inherited.
args... are optional and allow to name the test. This name is used in case of failure to qualify the test. If len(args) > 1 and the first item of args is a string and contains a '%' rune then fmt.Fprintf is used to compose the name, else args are passed to fmt.Fprint. Do not forget it is the name of the test, not the reason of a potential failure.
Example (Has_hasnt) ¶
package main import ( "encoding/json" "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := &testing.T{} got := json.RawMessage(` { "name": "Bob", "age": 42, "children": [ { "name": "Alice", "age": 16 }, { "name": "Britt", "age": 21, "children": [ { "name": "John", "age": 1 } ] } ] }`) // Has Bob some children? ok := td.CmpJSONPointer(t, got, "/children", td.Len(td.Gt(0))) fmt.Println("Bob has at least one child:", ok) // But checking "children" exists is enough here ok = td.CmpJSONPointer(t, got, "/children/0/children", td.Ignore()) fmt.Println("Alice has children:", ok) ok = td.CmpJSONPointer(t, got, "/children/1/children", td.Ignore()) fmt.Println("Britt has children:", ok) // The reverse can be checked too ok = td.Cmp(t, got, td.Not(td.JSONPointer("/children/0/children", td.Ignore()))) fmt.Println("Alice hasn't children:", ok) ok = td.Cmp(t, got, td.Not(td.JSONPointer("/children/1/children", td.Ignore()))) fmt.Println("Britt hasn't children:", ok) }
Output: Bob has at least one child: true Alice has children: false Britt has children: true Alice hasn't children: true Britt hasn't children: false
Example (Rfc6901) ¶
package main import ( "encoding/json" "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := &testing.T{} got := json.RawMessage(` { "foo": ["bar", "baz"], "": 0, "a/b": 1, "c%d": 2, "e^f": 3, "g|h": 4, "i\\j": 5, "k\"l": 6, " ": 7, "m~n": 8 }`) expected := map[string]any{ "foo": []any{"bar", "baz"}, "": 0, "a/b": 1, "c%d": 2, "e^f": 3, "g|h": 4, `i\j`: 5, `k"l`: 6, " ": 7, "m~n": 8, } ok := td.CmpJSONPointer(t, got, "", expected) fmt.Println("Empty JSON pointer means all:", ok) ok = td.CmpJSONPointer(t, got, `/foo`, []any{"bar", "baz"}) fmt.Println("Extract `foo` key:", ok) ok = td.CmpJSONPointer(t, got, `/foo/0`, "bar") fmt.Println("First item of `foo` key slice:", ok) ok = td.CmpJSONPointer(t, got, `/`, 0) fmt.Println("Empty key:", ok) ok = td.CmpJSONPointer(t, got, `/a~1b`, 1) fmt.Println("Slash has to be escaped using `~1`:", ok) ok = td.CmpJSONPointer(t, got, `/c%d`, 2) fmt.Println("% in key:", ok) ok = td.CmpJSONPointer(t, got, `/e^f`, 3) fmt.Println("^ in key:", ok) ok = td.CmpJSONPointer(t, got, `/g|h`, 4) fmt.Println("| in key:", ok) ok = td.CmpJSONPointer(t, got, `/i\j`, 5) fmt.Println("Backslash in key:", ok) ok = td.CmpJSONPointer(t, got, `/k"l`, 6) fmt.Println("Double-quote in key:", ok) ok = td.CmpJSONPointer(t, got, `/ `, 7) fmt.Println("Space key:", ok) ok = td.CmpJSONPointer(t, got, `/m~0n`, 8) fmt.Println("Tilde has to be escaped using `~0`:", ok) }
Output: Empty JSON pointer means all: true Extract `foo` key: true First item of `foo` key slice: true Empty key: true Slash has to be escaped using `~1`: true % in key: true ^ in key: true | in key: true Backslash in key: true Double-quote in key: true Space key: true Tilde has to be escaped using `~0`: true
Example (Struct) ¶
package main import ( "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := &testing.T{} // Without json tags, encoding/json uses public fields name type Item struct { Name string Value int64 Next *Item } got := Item{ Name: "first", Value: 1, Next: &Item{ Name: "second", Value: 2, Next: &Item{ Name: "third", Value: 3, }, }, } ok := td.CmpJSONPointer(t, got, "/Next/Next/Name", "third") fmt.Println("3rd item name is `third`:", ok) ok = td.CmpJSONPointer(t, got, "/Next/Next/Value", td.Gte(int64(3))) fmt.Println("3rd item value is greater or equal than 3:", ok) ok = td.CmpJSONPointer(t, got, "/Next", td.JSONPointer("/Next", td.JSONPointer("/Value", td.Gte(int64(3))))) fmt.Println("3rd item value is still greater or equal than 3:", ok) ok = td.CmpJSONPointer(t, got, "/Next/Next/Next/Name", td.Ignore()) fmt.Println("4th item exists and has a name:", ok) // Struct comparison work with or without pointer: &Item{…} works too ok = td.CmpJSONPointer(t, got, "/Next/Next", Item{ Name: "third", Value: 3, }) fmt.Println("3rd item full comparison:", ok) }
Output: 3rd item name is `third`: true 3rd item value is greater or equal than 3: true 3rd item value is still greater or equal than 3: true 4th item exists and has a name: false 3rd item full comparison: true
func CmpKeys ¶
CmpKeys is a shortcut for:
td.Cmp(t, got, td.Keys(val), args...)
See Keys for details.
Returns true if the test is OK, false if it fails.
If t is a *T then its Config field is inherited.
args... are optional and allow to name the test. This name is used in case of failure to qualify the test. If len(args) > 1 and the first item of args is a string and contains a '%' rune then fmt.Fprintf is used to compose the name, else args are passed to fmt.Fprint. Do not forget it is the name of the test, not the reason of a potential failure.
Example ¶
package main import ( "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := &testing.T{} got := map[string]int{"foo": 1, "bar": 2, "zip": 3} // Keys tests keys in an ordered manner ok := td.CmpKeys(t, got, []string{"bar", "foo", "zip"}) fmt.Println("All sorted keys are found:", ok) // If the expected keys are not ordered, it fails ok = td.CmpKeys(t, got, []string{"zip", "bar", "foo"}) fmt.Println("All unsorted keys are found:", ok) // To circumvent that, one can use Bag operator ok = td.CmpKeys(t, got, td.Bag("zip", "bar", "foo")) fmt.Println("All unsorted keys are found, with the help of Bag operator:", ok) // Check that each key is 3 bytes long ok = td.CmpKeys(t, got, td.ArrayEach(td.Len(3))) fmt.Println("Each key is 3 bytes long:", ok) }
Output: All sorted keys are found: true All unsorted keys are found: false All unsorted keys are found, with the help of Bag operator: true Each key is 3 bytes long: true
func CmpLast ¶ added in v1.13.0
CmpLast is a shortcut for:
td.Cmp(t, got, td.Last(filter, expectedValue), args...)
See Last for details.
Returns true if the test is OK, false if it fails.
If t is a *T then its Config field is inherited.
args... are optional and allow to name the test. This name is used in case of failure to qualify the test. If len(args) > 1 and the first item of args is a string and contains a '%' rune then fmt.Fprintf is used to compose the name, else args are passed to fmt.Fprint. Do not forget it is the name of the test, not the reason of a potential failure.
Example (Classic) ¶
package main import ( "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := &testing.T{} got := []int{-3, -2, -1, 0, 1, 2, 3} ok := td.CmpLast(t, got, td.Lt(0), -1) fmt.Println("last negative number is -1:", ok) isEven := func(x int) bool { return x%2 == 0 } ok = td.CmpLast(t, got, isEven, 2) fmt.Println("last even number is 2:", ok) ok = td.CmpLast(t, got, isEven, td.Gt(0)) fmt.Println("last even number is > 0:", ok) ok = td.CmpLast(t, got, isEven, td.Code(isEven)) fmt.Println("last even number is well even:", ok) }
Output: last negative number is -1: true last even number is 2: true last even number is > 0: true last even number is well even: true
Example (Empty) ¶
package main import ( "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := &testing.T{} ok := td.CmpLast(t, ([]int)(nil), td.Gt(0), td.Gt(0)) fmt.Println("last in nil slice:", ok) ok = td.CmpLast(t, []int{}, td.Gt(0), td.Gt(0)) fmt.Println("last in empty slice:", ok) ok = td.CmpLast(t, &[]int{}, td.Gt(0), td.Gt(0)) fmt.Println("last in empty pointed slice:", ok) ok = td.CmpLast(t, [0]int{}, td.Gt(0), td.Gt(0)) fmt.Println("last in empty array:", ok) }
Output: last in nil slice: false last in empty slice: false last in empty pointed slice: false last in empty array: false
Example (Struct) ¶
package main import ( "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := &testing.T{} type Person struct { Fullname string `json:"fullname"` Age int `json:"age"` } got := []*Person{ { Fullname: "Bob Foobar", Age: 42, }, { Fullname: "Alice Bingo", Age: 37, }, } ok := td.CmpLast(t, got, td.Smuggle("Age", td.Gt(30)), td.Smuggle("Fullname", "Alice Bingo")) fmt.Println("last person.Age > 30 → Alice:", ok) ok = td.CmpLast(t, got, td.JSONPointer("/age", td.Gt(30)), td.SuperJSONOf(`{"fullname":"Alice Bingo"}`)) fmt.Println("last person.Age > 30 → Alice, using JSON:", ok) ok = td.CmpLast(t, got, td.JSONPointer("/age", td.Gt(30)), td.JSONPointer("/fullname", td.HasPrefix("Alice"))) fmt.Println("first person.Age > 30 → Alice, using JSONPointer:", ok) }
Output: last person.Age > 30 → Alice: true last person.Age > 30 → Alice, using JSON: true first person.Age > 30 → Alice, using JSONPointer: true
func CmpLax ¶
CmpLax is a shortcut for:
td.Cmp(t, got, td.Lax(expectedValue), args...)
See Lax for details.
Returns true if the test is OK, false if it fails.
If t is a *T then its Config field is inherited.
args... are optional and allow to name the test. This name is used in case of failure to qualify the test. If len(args) > 1 and the first item of args is a string and contains a '%' rune then fmt.Fprintf is used to compose the name, else args are passed to fmt.Fprint. Do not forget it is the name of the test, not the reason of a potential failure.
Example ¶
package main import ( "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := &testing.T{} gotInt64 := int64(1234) gotInt32 := int32(1235) type myInt uint16 gotMyInt := myInt(1236) expected := td.Between(1230, 1240) // int type here ok := td.CmpLax(t, gotInt64, expected) fmt.Println("int64 got between ints [1230 .. 1240]:", ok) ok = td.CmpLax(t, gotInt32, expected) fmt.Println("int32 got between ints [1230 .. 1240]:", ok) ok = td.CmpLax(t, gotMyInt, expected) fmt.Println("myInt got between ints [1230 .. 1240]:", ok) }
Output: int64 got between ints [1230 .. 1240]: true int32 got between ints [1230 .. 1240]: true myInt got between ints [1230 .. 1240]: true
func CmpLen ¶
CmpLen is a shortcut for:
td.Cmp(t, got, td.Len(expectedLen), args...)
See Len for details.
Returns true if the test is OK, false if it fails.
If t is a *T then its Config field is inherited.
args... are optional and allow to name the test. This name is used in case of failure to qualify the test. If len(args) > 1 and the first item of args is a string and contains a '%' rune then fmt.Fprintf is used to compose the name, else args are passed to fmt.Fprint. Do not forget it is the name of the test, not the reason of a potential failure.
Example (Map) ¶
package main import ( "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := &testing.T{} got := map[int]bool{11: true, 22: false, 33: false} ok := td.CmpLen(t, got, 3, "checks %v len is 3", got) fmt.Println(ok) ok = td.CmpLen(t, got, 0, "checks %v len is 0", got) fmt.Println(ok) got = nil ok = td.CmpLen(t, got, 0, "checks %v len is 0", got) fmt.Println(ok) }
Output: true false true
Example (OperatorMap) ¶
package main import ( "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := &testing.T{} got := map[int]bool{11: true, 22: false, 33: false} ok := td.CmpLen(t, got, td.Between(3, 8), "checks %v len is in [3 .. 8]", got) fmt.Println(ok) ok = td.CmpLen(t, got, td.Gte(3), "checks %v len is ≥ 3", got) fmt.Println(ok) }
Output: true true
Example (OperatorSlice) ¶
package main import ( "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := &testing.T{} got := []int{11, 22, 33} ok := td.CmpLen(t, got, td.Between(3, 8), "checks %v len is in [3 .. 8]", got) fmt.Println(ok) ok = td.CmpLen(t, got, td.Lt(5), "checks %v len is < 5", got) fmt.Println(ok) }
Output: true true
Example (Slice) ¶
package main import ( "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := &testing.T{} got := []int{11, 22, 33} ok := td.CmpLen(t, got, 3, "checks %v len is 3", got) fmt.Println(ok) ok = td.CmpLen(t, got, 0, "checks %v len is 0", got) fmt.Println(ok) got = nil ok = td.CmpLen(t, got, 0, "checks %v len is 0", got) fmt.Println(ok) }
Output: true false true
func CmpLt ¶
CmpLt is a shortcut for:
td.Cmp(t, got, td.Lt(maxExpectedValue), args...)
See Lt for details.
Returns true if the test is OK, false if it fails.
If t is a *T then its Config field is inherited.
args... are optional and allow to name the test. This name is used in case of failure to qualify the test. If len(args) > 1 and the first item of args is a string and contains a '%' rune then fmt.Fprintf is used to compose the name, else args are passed to fmt.Fprint. Do not forget it is the name of the test, not the reason of a potential failure.
Example (Int) ¶
package main import ( "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := &testing.T{} got := 156 ok := td.CmpLt(t, got, 157, "checks %v is < 157", got) fmt.Println(ok) ok = td.CmpLt(t, got, 156, "checks %v is < 156", got) fmt.Println(ok) }
Output: true false
Example (String) ¶
package main import ( "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := &testing.T{} got := "abc" ok := td.CmpLt(t, got, "abd", `checks "%v" is < "abd"`, got) fmt.Println(ok) ok = td.CmpLt(t, got, "abc", `checks "%v" is < "abc"`, got) fmt.Println(ok) }
Output: true false
func CmpLte ¶
CmpLte is a shortcut for:
td.Cmp(t, got, td.Lte(maxExpectedValue), args...)
See Lte for details.
Returns true if the test is OK, false if it fails.
If t is a *T then its Config field is inherited.
args... are optional and allow to name the test. This name is used in case of failure to qualify the test. If len(args) > 1 and the first item of args is a string and contains a '%' rune then fmt.Fprintf is used to compose the name, else args are passed to fmt.Fprint. Do not forget it is the name of the test, not the reason of a potential failure.
Example (Int) ¶
package main import ( "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := &testing.T{} got := 156 ok := td.CmpLte(t, got, 156, "checks %v is ≤ 156", got) fmt.Println(ok) ok = td.CmpLte(t, got, 157, "checks %v is ≤ 157", got) fmt.Println(ok) ok = td.CmpLte(t, got, 155, "checks %v is ≤ 155", got) fmt.Println(ok) }
Output: true true false
Example (String) ¶
package main import ( "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := &testing.T{} got := "abc" ok := td.CmpLte(t, got, "abc", `checks "%v" is ≤ "abc"`, got) fmt.Println(ok) ok = td.CmpLte(t, got, "abd", `checks "%v" is ≤ "abd"`, got) fmt.Println(ok) ok = td.CmpLte(t, got, "abb", `checks "%v" is ≤ "abb"`, got) fmt.Println(ok) }
Output: true true false
func CmpMap ¶
func CmpMap(t TestingT, got, model any, expectedEntries MapEntries, args ...any) bool
CmpMap is a shortcut for:
td.Cmp(t, got, td.Map(model, expectedEntries), args...)
See Map for details.
Returns true if the test is OK, false if it fails.
If t is a *T then its Config field is inherited.
args... are optional and allow to name the test. This name is used in case of failure to qualify the test. If len(args) > 1 and the first item of args is a string and contains a '%' rune then fmt.Fprintf is used to compose the name, else args are passed to fmt.Fprint. Do not forget it is the name of the test, not the reason of a potential failure.
Example (Map) ¶
package main import ( "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := &testing.T{} got := map[string]int{"foo": 12, "bar": 42, "zip": 89} ok := td.CmpMap(t, got, map[string]int{"bar": 42}, td.MapEntries{"foo": td.Lt(15), "zip": td.Ignore()}, "checks map %v", got) fmt.Println(ok) ok = td.CmpMap(t, got, map[string]int{}, td.MapEntries{"bar": 42, "foo": td.Lt(15), "zip": td.Ignore()}, "checks map %v", got) fmt.Println(ok) ok = td.CmpMap(t, got, (map[string]int)(nil), td.MapEntries{"bar": 42, "foo": td.Lt(15), "zip": td.Ignore()}, "checks map %v", got) fmt.Println(ok) }
Output: true true true
Example (TypedMap) ¶
package main import ( "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := &testing.T{} type MyMap map[string]int got := MyMap{"foo": 12, "bar": 42, "zip": 89} ok := td.CmpMap(t, got, MyMap{"bar": 42}, td.MapEntries{"foo": td.Lt(15), "zip": td.Ignore()}, "checks typed map %v", got) fmt.Println(ok) ok = td.CmpMap(t, &got, &MyMap{"bar": 42}, td.MapEntries{"foo": td.Lt(15), "zip": td.Ignore()}, "checks pointer on typed map %v", got) fmt.Println(ok) ok = td.CmpMap(t, &got, &MyMap{}, td.MapEntries{"bar": 42, "foo": td.Lt(15), "zip": td.Ignore()}, "checks pointer on typed map %v", got) fmt.Println(ok) ok = td.CmpMap(t, &got, (*MyMap)(nil), td.MapEntries{"bar": 42, "foo": td.Lt(15), "zip": td.Ignore()}, "checks pointer on typed map %v", got) fmt.Println(ok) }
Output: true true true true
func CmpMapEach ¶
CmpMapEach is a shortcut for:
td.Cmp(t, got, td.MapEach(expectedValue), args...)
See MapEach for details.
Returns true if the test is OK, false if it fails.
If t is a *T then its Config field is inherited.
args... are optional and allow to name the test. This name is used in case of failure to qualify the test. If len(args) > 1 and the first item of args is a string and contains a '%' rune then fmt.Fprintf is used to compose the name, else args are passed to fmt.Fprint. Do not forget it is the name of the test, not the reason of a potential failure.
Example (Map) ¶
package main import ( "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := &testing.T{} got := map[string]int{"foo": 12, "bar": 42, "zip": 89} ok := td.CmpMapEach(t, got, td.Between(10, 90), "checks each value of map %v is in [10 .. 90]", got) fmt.Println(ok) }
Output: true
Example (TypedMap) ¶
package main import ( "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := &testing.T{} type MyMap map[string]int got := MyMap{"foo": 12, "bar": 42, "zip": 89} ok := td.CmpMapEach(t, got, td.Between(10, 90), "checks each value of typed map %v is in [10 .. 90]", got) fmt.Println(ok) ok = td.CmpMapEach(t, &got, td.Between(10, 90), "checks each value of typed map pointer %v is in [10 .. 90]", got) fmt.Println(ok) }
Output: true true
func CmpN ¶
CmpN is a shortcut for:
td.Cmp(t, got, td.N(num, tolerance), args...)
See N for details.
N optional parameter tolerance is here mandatory. 0 value should be passed to mimic its absence in original N call.
Returns true if the test is OK, false if it fails.
If t is a *T then its Config field is inherited.
args... are optional and allow to name the test. This name is used in case of failure to qualify the test. If len(args) > 1 and the first item of args is a string and contains a '%' rune then fmt.Fprintf is used to compose the name, else args are passed to fmt.Fprint. Do not forget it is the name of the test, not the reason of a potential failure.
Example ¶
package main import ( "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := &testing.T{} got := 1.12345 ok := td.CmpN(t, got, 1.1234, 0.00006, "checks %v = 1.1234 ± 0.00006", got) fmt.Println(ok) }
Output: true
func CmpNaN ¶
CmpNaN is a shortcut for:
td.Cmp(t, got, td.NaN(), args...)
See NaN for details.
Returns true if the test is OK, false if it fails.
If t is a *T then its Config field is inherited.
args... are optional and allow to name the test. This name is used in case of failure to qualify the test. If len(args) > 1 and the first item of args is a string and contains a '%' rune then fmt.Fprintf is used to compose the name, else args are passed to fmt.Fprint. Do not forget it is the name of the test, not the reason of a potential failure.
Example (Float32) ¶
package main import ( "fmt" "math" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := &testing.T{} got := float32(math.NaN()) ok := td.CmpNaN(t, got, "checks %v is not-a-number", got) fmt.Println("float32(math.NaN()) is float32 not-a-number:", ok) got = 12 ok = td.CmpNaN(t, got, "checks %v is not-a-number", got) fmt.Println("float32(12) is float32 not-a-number:", ok) }
Output: float32(math.NaN()) is float32 not-a-number: true float32(12) is float32 not-a-number: false
Example (Float64) ¶
package main import ( "fmt" "math" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := &testing.T{} got := math.NaN() ok := td.CmpNaN(t, got, "checks %v is not-a-number", got) fmt.Println("math.NaN() is not-a-number:", ok) got = 12 ok = td.CmpNaN(t, got, "checks %v is not-a-number", got) fmt.Println("float64(12) is not-a-number:", ok) // math.NaN() is not-a-number: true // float64(12) is not-a-number: false }
Output:
func CmpNil ¶
CmpNil is a shortcut for:
td.Cmp(t, got, td.Nil(), args...)
See Nil for details.
Returns true if the test is OK, false if it fails.
If t is a *T then its Config field is inherited.
args... are optional and allow to name the test. This name is used in case of failure to qualify the test. If len(args) > 1 and the first item of args is a string and contains a '%' rune then fmt.Fprintf is used to compose the name, else args are passed to fmt.Fprint. Do not forget it is the name of the test, not the reason of a potential failure.
Example ¶
package main import ( "bytes" "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := &testing.T{} var got fmt.Stringer // interface // nil value can be compared directly with nil, no need of Nil() here ok := td.Cmp(t, got, nil) fmt.Println(ok) // But it works with Nil() anyway ok = td.CmpNil(t, got) fmt.Println(ok) got = (*bytes.Buffer)(nil) // In the case of an interface containing a nil pointer, comparing // with nil fails, as the interface is not nil ok = td.Cmp(t, got, nil) fmt.Println(ok) // In this case Nil() succeed ok = td.CmpNil(t, got) fmt.Println(ok) }
Output: true true false true
func CmpNoError ¶
CmpNoError checks that got is nil error.
value, err := MyFunction(1, 2, 3) if td.CmpNoError(t, err) { // one can now check value... }
args... are optional and allow to name the test. This name is used in case of failure to qualify the test. If len(args) > 1 and the first item of args is a string and contains a '%' rune then fmt.Fprintf is used to compose the name, else args are passed to fmt.Fprint. Do not forget it is the name of the test, not the reason of a potential failure.
Example ¶
package main import ( "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := &testing.T{} got := fmt.Errorf("Error #%d", 42) ok := td.CmpNoError(t, got, "An error occurred") // fails fmt.Println(ok) got = nil ok = td.CmpNoError(t, got, "An error occurred") fmt.Println(ok) }
Output: false true
func CmpNone ¶
CmpNone is a shortcut for:
td.Cmp(t, got, td.None(notExpectedValues...), args...)
See None for details.
Returns true if the test is OK, false if it fails.
If t is a *T then its Config field is inherited.
args... are optional and allow to name the test. This name is used in case of failure to qualify the test. If len(args) > 1 and the first item of args is a string and contains a '%' rune then fmt.Fprintf is used to compose the name, else args are passed to fmt.Fprint. Do not forget it is the name of the test, not the reason of a potential failure.
Example ¶
package main import ( "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := &testing.T{} got := 18 ok := td.CmpNone(t, got, []any{0, 10, 20, 30, td.Between(100, 199)}, "checks %v is non-null, and ≠ 10, 20 & 30, and not in [100-199]", got) fmt.Println(ok) got = 20 ok = td.CmpNone(t, got, []any{0, 10, 20, 30, td.Between(100, 199)}, "checks %v is non-null, and ≠ 10, 20 & 30, and not in [100-199]", got) fmt.Println(ok) got = 142 ok = td.CmpNone(t, got, []any{0, 10, 20, 30, td.Between(100, 199)}, "checks %v is non-null, and ≠ 10, 20 & 30, and not in [100-199]", got) fmt.Println(ok) prime := td.Flatten([]int{1, 2, 3, 5, 7, 11, 13}) even := td.Flatten([]int{2, 4, 6, 8, 10, 12, 14}) for _, got := range [...]int{9, 3, 8, 15} { ok = td.CmpNone(t, got, []any{prime, even, td.Gt(14)}, "checks %v is not prime number, nor an even number and not > 14") fmt.Printf("%d → %t\n", got, ok) } }
Output: true false false 9 → true 3 → false 8 → false 15 → false
func CmpNot ¶
CmpNot is a shortcut for:
td.Cmp(t, got, td.Not(notExpected), args...)
See Not for details.
Returns true if the test is OK, false if it fails.
If t is a *T then its Config field is inherited.
args... are optional and allow to name the test. This name is used in case of failure to qualify the test. If len(args) > 1 and the first item of args is a string and contains a '%' rune then fmt.Fprintf is used to compose the name, else args are passed to fmt.Fprint. Do not forget it is the name of the test, not the reason of a potential failure.
Example ¶
package main import ( "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := &testing.T{} got := 42 ok := td.CmpNot(t, got, 0, "checks %v is non-null", got) fmt.Println(ok) ok = td.CmpNot(t, got, td.Between(10, 30), "checks %v is not in [10 .. 30]", got) fmt.Println(ok) got = 0 ok = td.CmpNot(t, got, 0, "checks %v is non-null", got) fmt.Println(ok) }
Output: true true false
func CmpNotAny ¶
CmpNotAny is a shortcut for:
td.Cmp(t, got, td.NotAny(notExpectedItems...), args...)
See NotAny for details.
Returns true if the test is OK, false if it fails.
If t is a *T then its Config field is inherited.
args... are optional and allow to name the test. This name is used in case of failure to qualify the test. If len(args) > 1 and the first item of args is a string and contains a '%' rune then fmt.Fprintf is used to compose the name, else args are passed to fmt.Fprint. Do not forget it is the name of the test, not the reason of a potential failure.
Example ¶
package main import ( "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := &testing.T{} got := []int{4, 5, 9, 42} ok := td.CmpNotAny(t, got, []any{3, 6, 8, 41, 43}, "checks %v contains no item listed in NotAny()", got) fmt.Println(ok) ok = td.CmpNotAny(t, got, []any{3, 6, 8, 42, 43}, "checks %v contains no item listed in NotAny()", got) fmt.Println(ok) // When expected is already a non-[]any slice, it cannot be // flattened directly using notExpected... without copying it to a new // []any slice, then use td.Flatten! notExpected := []int{3, 6, 8, 41, 43} ok = td.CmpNotAny(t, got, []any{td.Flatten(notExpected)}, "checks %v contains no item listed in notExpected", got) fmt.Println(ok) }
Output: true false true
func CmpNotEmpty ¶
CmpNotEmpty is a shortcut for:
td.Cmp(t, got, td.NotEmpty(), args...)
See NotEmpty for details.
Returns true if the test is OK, false if it fails.
If t is a *T then its Config field is inherited.
args... are optional and allow to name the test. This name is used in case of failure to qualify the test. If len(args) > 1 and the first item of args is a string and contains a '%' rune then fmt.Fprintf is used to compose the name, else args are passed to fmt.Fprint. Do not forget it is the name of the test, not the reason of a potential failure.
Example ¶
package main import ( "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := &testing.T{} ok := td.CmpNotEmpty(t, nil) // fails, as nil is considered empty fmt.Println(ok) ok = td.CmpNotEmpty(t, "foobar") fmt.Println(ok) // Fails as 0 is a number, so not empty. Use NotZero() instead ok = td.CmpNotEmpty(t, 0) fmt.Println(ok) ok = td.CmpNotEmpty(t, map[string]int{"foobar": 42}) fmt.Println(ok) ok = td.CmpNotEmpty(t, []int{1}) fmt.Println(ok) ok = td.CmpNotEmpty(t, [3]int{}) // succeeds, NotEmpty() is not NotZero()! fmt.Println(ok) }
Output: false true false true true true
Example (Pointers) ¶
package main import ( "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := &testing.T{} type MySlice []int ok := td.CmpNotEmpty(t, MySlice{12}) fmt.Println(ok) ok = td.CmpNotEmpty(t, &MySlice{12}) // Ptr() not needed fmt.Println(ok) l1 := &MySlice{12} l2 := &l1 l3 := &l2 ok = td.CmpNotEmpty(t, &l3) fmt.Println(ok) // Works the same for array, map, channel and string // But not for others types as: type MyStruct struct { Value int } ok = td.CmpNotEmpty(t, &MyStruct{}) // fails, use NotZero() instead fmt.Println(ok) }
Output: true true true false
func CmpNotNaN ¶
CmpNotNaN is a shortcut for:
td.Cmp(t, got, td.NotNaN(), args...)
See NotNaN for details.
Returns true if the test is OK, false if it fails.
If t is a *T then its Config field is inherited.
args... are optional and allow to name the test. This name is used in case of failure to qualify the test. If len(args) > 1 and the first item of args is a string and contains a '%' rune then fmt.Fprintf is used to compose the name, else args are passed to fmt.Fprint. Do not forget it is the name of the test, not the reason of a potential failure.
Example (Float32) ¶
package main import ( "fmt" "math" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := &testing.T{} got := float32(math.NaN()) ok := td.CmpNotNaN(t, got, "checks %v is not-a-number", got) fmt.Println("float32(math.NaN()) is NOT float32 not-a-number:", ok) got = 12 ok = td.CmpNotNaN(t, got, "checks %v is not-a-number", got) fmt.Println("float32(12) is NOT float32 not-a-number:", ok) }
Output: float32(math.NaN()) is NOT float32 not-a-number: false float32(12) is NOT float32 not-a-number: true
Example (Float64) ¶
package main import ( "fmt" "math" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := &testing.T{} got := math.NaN() ok := td.CmpNotNaN(t, got, "checks %v is not-a-number", got) fmt.Println("math.NaN() is not-a-number:", ok) got = 12 ok = td.CmpNotNaN(t, got, "checks %v is not-a-number", got) fmt.Println("float64(12) is not-a-number:", ok) // math.NaN() is NOT not-a-number: false // float64(12) is NOT not-a-number: true }
Output:
func CmpNotNil ¶
CmpNotNil is a shortcut for:
td.Cmp(t, got, td.NotNil(), args...)
See NotNil for details.
Returns true if the test is OK, false if it fails.
If t is a *T then its Config field is inherited.
args... are optional and allow to name the test. This name is used in case of failure to qualify the test. If len(args) > 1 and the first item of args is a string and contains a '%' rune then fmt.Fprintf is used to compose the name, else args are passed to fmt.Fprint. Do not forget it is the name of the test, not the reason of a potential failure.
Example ¶
package main import ( "bytes" "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := &testing.T{} var got fmt.Stringer = &bytes.Buffer{} // nil value can be compared directly with Not(nil), no need of NotNil() here ok := td.Cmp(t, got, td.Not(nil)) fmt.Println(ok) // But it works with NotNil() anyway ok = td.CmpNotNil(t, got) fmt.Println(ok) got = (*bytes.Buffer)(nil) // In the case of an interface containing a nil pointer, comparing // with Not(nil) succeeds, as the interface is not nil ok = td.Cmp(t, got, td.Not(nil)) fmt.Println(ok) // In this case NotNil() fails ok = td.CmpNotNil(t, got) fmt.Println(ok) }
Output: true true true false
func CmpNotPanic ¶
CmpNotPanic calls fn and checks no panic() occurred. If a panic() occurred false is returned then the panic() parameter and the stack trace appear in the test report.
Note that calling panic(nil) in fn body is detected as a panic.
td.CmpNotPanic(t, func() {}) // succeeds as function does not panic td.CmpNotPanic(t, func() { panic("I am panicking!") }) // fails td.CmpNotPanic(t, func() { panic(nil) }) // fails too
args... are optional and allow to name the test. This name is used in case of failure to qualify the test. If len(args) > 1 and the first item of args is a string and contains a '%' rune then fmt.Fprintf is used to compose the name, else args are passed to fmt.Fprint. Do not forget it is the name of the test, not the reason of a potential failure.
Example ¶
package main import ( "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := &testing.T{} ok := td.CmpNotPanic(t, func() {}) fmt.Println("checks a panic DID NOT occur:", ok) // Classic panic ok = td.CmpNotPanic(t, func() { panic("I am panicking!") }, "Hope it does not panic!") fmt.Println("still no panic?", ok) // Can detect panic(nil) ok = td.CmpNotPanic(t, func() { panic(nil) }, "Checks for panic(nil)") fmt.Println("last no panic?", ok) }
Output: checks a panic DID NOT occur: true still no panic? false last no panic? false
func CmpNotZero ¶
CmpNotZero is a shortcut for:
td.Cmp(t, got, td.NotZero(), args...)
See NotZero for details.
Returns true if the test is OK, false if it fails.
If t is a *T then its Config field is inherited.
args... are optional and allow to name the test. This name is used in case of failure to qualify the test. If len(args) > 1 and the first item of args is a string and contains a '%' rune then fmt.Fprintf is used to compose the name, else args are passed to fmt.Fprint. Do not forget it is the name of the test, not the reason of a potential failure.
Example ¶
package main import ( "bytes" "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := &testing.T{} ok := td.CmpNotZero(t, 0) // fails fmt.Println(ok) ok = td.CmpNotZero(t, float64(0)) // fails fmt.Println(ok) ok = td.CmpNotZero(t, 12) fmt.Println(ok) ok = td.CmpNotZero(t, (map[string]int)(nil)) // fails, as nil fmt.Println(ok) ok = td.CmpNotZero(t, map[string]int{}) // succeeds, as not nil fmt.Println(ok) ok = td.CmpNotZero(t, ([]int)(nil)) // fails, as nil fmt.Println(ok) ok = td.CmpNotZero(t, []int{}) // succeeds, as not nil fmt.Println(ok) ok = td.CmpNotZero(t, [3]int{}) // fails fmt.Println(ok) ok = td.CmpNotZero(t, [3]int{0, 1}) // succeeds, DATA[1] is not 0 fmt.Println(ok) ok = td.CmpNotZero(t, bytes.Buffer{}) // fails fmt.Println(ok) ok = td.CmpNotZero(t, &bytes.Buffer{}) // succeeds, as pointer not nil fmt.Println(ok) ok = td.Cmp(t, &bytes.Buffer{}, td.Ptr(td.NotZero())) // fails as deref by Ptr() fmt.Println(ok) }
Output: false false true false true false true false true false true false
func CmpPPtr ¶
CmpPPtr is a shortcut for:
td.Cmp(t, got, td.PPtr(val), args...)
See PPtr for details.
Returns true if the test is OK, false if it fails.
If t is a *T then its Config field is inherited.
args... are optional and allow to name the test. This name is used in case of failure to qualify the test. If len(args) > 1 and the first item of args is a string and contains a '%' rune then fmt.Fprintf is used to compose the name, else args are passed to fmt.Fprint. Do not forget it is the name of the test, not the reason of a potential failure.
Example ¶
package main import ( "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := &testing.T{} num := 12 got := &num ok := td.CmpPPtr(t, &got, 12) fmt.Println(ok) ok = td.CmpPPtr(t, &got, td.Between(4, 15)) fmt.Println(ok) }
Output: true true
func CmpPanic ¶
CmpPanic calls fn and checks a panic() occurred with the expectedPanic parameter. It returns true only if both conditions are fulfilled.
Note that calling panic(nil) in fn body is detected as a panic (in this case expectedPanic has to be nil).
td.CmpPanic(t, func() { panic("I am panicking!") }, "I am panicking!", "The function should panic with the right string") // succeeds td.CmpPanic(t, func() { panic("I am panicking!") }, Contains("panicking!"), "The function should panic with a string containing `panicking!`") // succeeds td.CmpPanic(t, func() { panic(nil) }, nil, "Checks for panic(nil)") // succeeds
args... are optional and allow to name the test. This name is used in case of failure to qualify the test. If len(args) > 1 and the first item of args is a string and contains a '%' rune then fmt.Fprintf is used to compose the name, else args are passed to fmt.Fprint. Do not forget it is the name of the test, not the reason of a potential failure.
Example ¶
package main import ( "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := &testing.T{} ok := td.CmpPanic(t, func() { panic("I am panicking!") }, "I am panicking!", "Checks for panic") fmt.Println("checks exact panic() string:", ok) // Can use TestDeep operator too ok = td.CmpPanic(t, func() { panic("I am panicking!") }, td.Contains("panicking!"), "Checks for panic") fmt.Println("checks panic() sub-string:", ok) // Can detect panic(nil) ok = td.CmpPanic(t, func() { panic(nil) }, nil, "Checks for panic(nil)") fmt.Println("checks for panic(nil):", ok) // As well as structured data panic type PanicStruct struct { Error string Code int } ok = td.CmpPanic(t, func() { panic(PanicStruct{Error: "Memory violation", Code: 11}) }, PanicStruct{ Error: "Memory violation", Code: 11, }) fmt.Println("checks exact panic() struct:", ok) // or combined with TestDeep operators too ok = td.CmpPanic(t, func() { panic(PanicStruct{Error: "Memory violation", Code: 11}) }, td.Struct(PanicStruct{}, td.StructFields{ "Code": td.Between(10, 20), })) fmt.Println("checks panic() struct against TestDeep operators:", ok) // Of course, do not panic = test failure, even for expected nil // panic parameter ok = td.CmpPanic(t, func() {}, nil) fmt.Println("checks a panic occurred:", ok) }
Output: checks exact panic() string: true checks panic() sub-string: true checks for panic(nil): true checks exact panic() struct: true checks panic() struct against TestDeep operators: true checks a panic occurred: false
func CmpPtr ¶
CmpPtr is a shortcut for:
td.Cmp(t, got, td.Ptr(val), args...)
See Ptr for details.
Returns true if the test is OK, false if it fails.
If t is a *T then its Config field is inherited.
args... are optional and allow to name the test. This name is used in case of failure to qualify the test. If len(args) > 1 and the first item of args is a string and contains a '%' rune then fmt.Fprintf is used to compose the name, else args are passed to fmt.Fprint. Do not forget it is the name of the test, not the reason of a potential failure.
Example ¶
package main import ( "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := &testing.T{} got := 12 ok := td.CmpPtr(t, &got, 12) fmt.Println(ok) ok = td.CmpPtr(t, &got, td.Between(4, 15)) fmt.Println(ok) }
Output: true true
func CmpRe ¶
CmpRe is a shortcut for:
td.Cmp(t, got, td.Re(reg, capture), args...)
See Re for details.
Re optional parameter capture is here mandatory. nil value should be passed to mimic its absence in original Re call.
Returns true if the test is OK, false if it fails.
If t is a *T then its Config field is inherited.
args... are optional and allow to name the test. This name is used in case of failure to qualify the test. If len(args) > 1 and the first item of args is a string and contains a '%' rune then fmt.Fprintf is used to compose the name, else args are passed to fmt.Fprint. Do not forget it is the name of the test, not the reason of a potential failure.
Example ¶
package main import ( "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := &testing.T{} got := "foo bar" ok := td.CmpRe(t, got, "(zip|bar)$", nil, "checks value %s", got) fmt.Println(ok) got = "bar foo" ok = td.CmpRe(t, got, "(zip|bar)$", nil, "checks value %s", got) fmt.Println(ok) }
Output: true false
Example (Capture) ¶
package main import ( "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := &testing.T{} got := "foo bar biz" ok := td.CmpRe(t, got, `^(\w+) (\w+) (\w+)$`, td.Set("biz", "foo", "bar"), "checks value %s", got) fmt.Println(ok) got = "foo bar! biz" ok = td.CmpRe(t, got, `^(\w+) (\w+) (\w+)$`, td.Set("biz", "foo", "bar"), "checks value %s", got) fmt.Println(ok) }
Output: true false
Example (Compiled) ¶
package main import ( "fmt" "regexp" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := &testing.T{} expected := regexp.MustCompile("(zip|bar)$") got := "foo bar" ok := td.CmpRe(t, got, expected, nil, "checks value %s", got) fmt.Println(ok) got = "bar foo" ok = td.CmpRe(t, got, expected, nil, "checks value %s", got) fmt.Println(ok) }
Output: true false
Example (CompiledCapture) ¶
package main import ( "fmt" "regexp" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := &testing.T{} expected := regexp.MustCompile(`^(\w+) (\w+) (\w+)$`) got := "foo bar biz" ok := td.CmpRe(t, got, expected, td.Set("biz", "foo", "bar"), "checks value %s", got) fmt.Println(ok) got = "foo bar! biz" ok = td.CmpRe(t, got, expected, td.Set("biz", "foo", "bar"), "checks value %s", got) fmt.Println(ok) }
Output: true false
Example (CompiledError) ¶
package main import ( "errors" "fmt" "regexp" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := &testing.T{} expected := regexp.MustCompile("(zip|bar)$") got := errors.New("foo bar") ok := td.CmpRe(t, got, expected, nil, "checks value %s", got) fmt.Println(ok) }
Output: true
Example (CompiledStringer) ¶
package main import ( "bytes" "fmt" "regexp" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := &testing.T{} expected := regexp.MustCompile("(zip|bar)$") // bytes.Buffer implements fmt.Stringer got := bytes.NewBufferString("foo bar") ok := td.CmpRe(t, got, expected, nil, "checks value %s", got) fmt.Println(ok) }
Output: true
Example (Error) ¶
package main import ( "errors" "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := &testing.T{} got := errors.New("foo bar") ok := td.CmpRe(t, got, "(zip|bar)$", nil, "checks value %s", got) fmt.Println(ok) }
Output: true
Example (Stringer) ¶
package main import ( "bytes" "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := &testing.T{} // bytes.Buffer implements fmt.Stringer got := bytes.NewBufferString("foo bar") ok := td.CmpRe(t, got, "(zip|bar)$", nil, "checks value %s", got) fmt.Println(ok) }
Output: true
func CmpReAll ¶
CmpReAll is a shortcut for:
td.Cmp(t, got, td.ReAll(reg, capture), args...)
See ReAll for details.
Returns true if the test is OK, false if it fails.
If t is a *T then its Config field is inherited.
args... are optional and allow to name the test. This name is used in case of failure to qualify the test. If len(args) > 1 and the first item of args is a string and contains a '%' rune then fmt.Fprintf is used to compose the name, else args are passed to fmt.Fprint. Do not forget it is the name of the test, not the reason of a potential failure.
Example (Capture) ¶
package main import ( "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := &testing.T{} got := "foo bar biz" ok := td.CmpReAll(t, got, `(\w+)`, td.Set("biz", "foo", "bar"), "checks value %s", got) fmt.Println(ok) // Matches, but all catured groups do not match Set got = "foo BAR biz" ok = td.CmpReAll(t, got, `(\w+)`, td.Set("biz", "foo", "bar"), "checks value %s", got) fmt.Println(ok) }
Output: true false
Example (CaptureComplex) ¶
package main import ( "fmt" "strconv" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := &testing.T{} got := "11 45 23 56 85 96" ok := td.CmpReAll(t, got, `(\d+)`, td.ArrayEach(td.Code(func(num string) bool { n, err := strconv.Atoi(num) return err == nil && n > 10 && n < 100 })), "checks value %s", got) fmt.Println(ok) // Matches, but 11 is not greater than 20 ok = td.CmpReAll(t, got, `(\d+)`, td.ArrayEach(td.Code(func(num string) bool { n, err := strconv.Atoi(num) return err == nil && n > 20 && n < 100 })), "checks value %s", got) fmt.Println(ok) }
Output: true false
Example (CompiledCapture) ¶
package main import ( "fmt" "regexp" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := &testing.T{} expected := regexp.MustCompile(`(\w+)`) got := "foo bar biz" ok := td.CmpReAll(t, got, expected, td.Set("biz", "foo", "bar"), "checks value %s", got) fmt.Println(ok) // Matches, but all catured groups do not match Set got = "foo BAR biz" ok = td.CmpReAll(t, got, expected, td.Set("biz", "foo", "bar"), "checks value %s", got) fmt.Println(ok) }
Output: true false
Example (CompiledCaptureComplex) ¶
package main import ( "fmt" "regexp" "strconv" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := &testing.T{} expected := regexp.MustCompile(`(\d+)`) got := "11 45 23 56 85 96" ok := td.CmpReAll(t, got, expected, td.ArrayEach(td.Code(func(num string) bool { n, err := strconv.Atoi(num) return err == nil && n > 10 && n < 100 })), "checks value %s", got) fmt.Println(ok) // Matches, but 11 is not greater than 20 ok = td.CmpReAll(t, got, expected, td.ArrayEach(td.Code(func(num string) bool { n, err := strconv.Atoi(num) return err == nil && n > 20 && n < 100 })), "checks value %s", got) fmt.Println(ok) }
Output: true false
func CmpRecv ¶ added in v1.13.0
CmpRecv is a shortcut for:
td.Cmp(t, got, td.Recv(expectedValue, timeout), args...)
See Recv for details.
Recv optional parameter timeout is here mandatory. 0 value should be passed to mimic its absence in original Recv call.
Returns true if the test is OK, false if it fails.
If t is a *T then its Config field is inherited.
args... are optional and allow to name the test. This name is used in case of failure to qualify the test. If len(args) > 1 and the first item of args is a string and contains a '%' rune then fmt.Fprintf is used to compose the name, else args are passed to fmt.Fprint. Do not forget it is the name of the test, not the reason of a potential failure.
Example (Basic) ¶
package main import ( "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := &testing.T{} got := make(chan int, 3) ok := td.CmpRecv(t, got, td.RecvNothing, 0) fmt.Println("nothing to receive:", ok) got <- 1 got <- 2 got <- 3 close(got) ok = td.CmpRecv(t, got, 1, 0) fmt.Println("1st receive is 1:", ok) ok = td.Cmp(t, got, td.All( td.Recv(2), td.Recv(td.Between(3, 4)), td.Recv(td.RecvClosed), )) fmt.Println("next receives are 2, 3 then closed:", ok) ok = td.CmpRecv(t, got, td.RecvNothing, 0) fmt.Println("nothing to receive:", ok) }
Output: nothing to receive: true 1st receive is 1: true next receives are 2, 3 then closed: true nothing to receive: false
Example (ChannelPointer) ¶
package main import ( "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := &testing.T{} got := make(chan int, 3) ok := td.CmpRecv(t, got, td.RecvNothing, 0) fmt.Println("nothing to receive:", ok) got <- 1 got <- 2 got <- 3 close(got) ok = td.CmpRecv(t, &got, 1, 0) fmt.Println("1st receive is 1:", ok) ok = td.Cmp(t, &got, td.All( td.Recv(2), td.Recv(td.Between(3, 4)), td.Recv(td.RecvClosed), )) fmt.Println("next receives are 2, 3 then closed:", ok) ok = td.CmpRecv(t, got, td.RecvNothing, 0) fmt.Println("nothing to receive:", ok) }
Output: nothing to receive: true 1st receive is 1: true next receives are 2, 3 then closed: true nothing to receive: false
Example (NilChannel) ¶
package main import ( "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := &testing.T{} var ch chan int ok := td.CmpRecv(t, ch, td.RecvNothing, 0) fmt.Println("nothing to receive from nil channel:", ok) ok = td.CmpRecv(t, ch, 42, 0) fmt.Println("something to receive from nil channel:", ok) ok = td.CmpRecv(t, ch, td.RecvClosed, 0) fmt.Println("is a nil channel closed:", ok) }
Output: nothing to receive from nil channel: true something to receive from nil channel: false is a nil channel closed: false
Example (WithTimeout) ¶
package main import ( "fmt" "testing" "time" "github.com/maxatome/go-testdeep/td" ) func main() { t := &testing.T{} got := make(chan int, 1) tick := make(chan struct{}) go func() { // ① <-tick time.Sleep(100 * time.Millisecond) got <- 0 // ② <-tick time.Sleep(100 * time.Millisecond) got <- 1 // ③ <-tick time.Sleep(100 * time.Millisecond) close(got) }() td.CmpRecv(t, got, td.RecvNothing, 0) // ① tick <- struct{}{} ok := td.CmpRecv(t, got, td.RecvNothing, 0) fmt.Println("① RecvNothing:", ok) ok = td.CmpRecv(t, got, 0, 150*time.Millisecond) fmt.Println("① receive 0 w/150ms timeout:", ok) ok = td.CmpRecv(t, got, td.RecvNothing, 0) fmt.Println("① RecvNothing:", ok) // ② tick <- struct{}{} ok = td.CmpRecv(t, got, td.RecvNothing, 0) fmt.Println("② RecvNothing:", ok) ok = td.CmpRecv(t, got, 1, 150*time.Millisecond) fmt.Println("② receive 1 w/150ms timeout:", ok) ok = td.CmpRecv(t, got, td.RecvNothing, 0) fmt.Println("② RecvNothing:", ok) // ③ tick <- struct{}{} ok = td.CmpRecv(t, got, td.RecvNothing, 0) fmt.Println("③ RecvNothing:", ok) ok = td.CmpRecv(t, got, td.RecvClosed, 150*time.Millisecond) fmt.Println("③ check closed w/150ms timeout:", ok) }
Output: ① RecvNothing: true ① receive 0 w/150ms timeout: true ① RecvNothing: true ② RecvNothing: true ② receive 1 w/150ms timeout: true ② RecvNothing: true ③ RecvNothing: true ③ check closed w/150ms timeout: true
func CmpSStruct ¶
func CmpSStruct(t TestingT, got, model any, expectedFields StructFields, args ...any) bool
CmpSStruct is a shortcut for:
td.Cmp(t, got, td.SStruct(model, expectedFields), args...)
See SStruct for details.
SStruct optional parameter expectedFields is here mandatory. nil value should be passed to mimic its absence in original SStruct call.
Returns true if the test is OK, false if it fails.
If t is a *T then its Config field is inherited.
args... are optional and allow to name the test. This name is used in case of failure to qualify the test. If len(args) > 1 and the first item of args is a string and contains a '%' rune then fmt.Fprintf is used to compose the name, else args are passed to fmt.Fprint. Do not forget it is the name of the test, not the reason of a potential failure.
Example ¶
package main import ( "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := &testing.T{} type Person struct { Name string Age int NumChildren int } got := Person{ Name: "Foobar", Age: 42, NumChildren: 0, } // NumChildren is not listed in expected fields so it must be zero ok := td.CmpSStruct(t, got, Person{Name: "Foobar"}, td.StructFields{ "Age": td.Between(40, 50), }, "checks %v is the right Person") fmt.Println("Foobar is between 40 & 50:", ok) // Model can be empty got.NumChildren = 3 ok = td.CmpSStruct(t, got, Person{}, td.StructFields{ "Name": "Foobar", "Age": td.Between(40, 50), "NumChildren": td.Not(0), }, "checks %v is the right Person") fmt.Println("Foobar has some children:", ok) // Works with pointers too ok = td.CmpSStruct(t, &got, &Person{}, td.StructFields{ "Name": "Foobar", "Age": td.Between(40, 50), "NumChildren": td.Not(0), }, "checks %v is the right Person") fmt.Println("Foobar has some children (using pointer):", ok) // Model does not need to be instanciated ok = td.CmpSStruct(t, &got, (*Person)(nil), td.StructFields{ "Name": "Foobar", "Age": td.Between(40, 50), "NumChildren": td.Not(0), }, "checks %v is the right Person") fmt.Println("Foobar has some children (using nil model):", ok) }
Output: Foobar is between 40 & 50: true Foobar has some children: true Foobar has some children (using pointer): true Foobar has some children (using nil model): true
Example (Overwrite_model) ¶
package main import ( "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := &testing.T{} type Person struct { Name string Age int NumChildren int } got := Person{ Name: "Foobar", Age: 42, NumChildren: 3, } ok := td.CmpSStruct(t, got, Person{ Name: "Foobar", Age: 53, }, td.StructFields{ ">Age": td.Between(40, 50), // ">" to overwrite Age:53 in model "NumChildren": td.Gt(2), }, "checks %v is the right Person") fmt.Println("Foobar is between 40 & 50:", ok) ok = td.CmpSStruct(t, got, Person{ Name: "Foobar", Age: 53, }, td.StructFields{ "> Age": td.Between(40, 50), // same, ">" can be followed by spaces "NumChildren": td.Gt(2), }, "checks %v is the right Person") fmt.Println("Foobar is between 40 & 50:", ok) }
Output: Foobar is between 40 & 50: true Foobar is between 40 & 50: true
Example (Patterns) ¶
package main import ( "fmt" "testing" "time" "github.com/maxatome/go-testdeep/td" ) func main() { t := &testing.T{} type Person struct { Firstname string Lastname string Surname string Nickname string CreatedAt time.Time UpdatedAt time.Time DeletedAt *time.Time id int64 secret string } now := time.Now() got := Person{ Firstname: "Maxime", Lastname: "Foo", Surname: "Max", Nickname: "max", CreatedAt: now, UpdatedAt: now, DeletedAt: nil, // not deleted yet id: 2345, secret: "5ecr3T", } ok := td.CmpSStruct(t, got, Person{Lastname: "Foo"}, td.StructFields{ `DeletedAt`: nil, `= *name`: td.Re(`^(?i)max`), // shell pattern, matches all names except Lastname as in model `=~ At\z`: td.Lte(time.Now()), // regexp, matches CreatedAt & UpdatedAt `! [A-Z]*`: td.Ignore(), // private fields }, "mix shell & regexp patterns") fmt.Println("Patterns match only remaining fields:", ok) ok = td.CmpSStruct(t, got, Person{Lastname: "Foo"}, td.StructFields{ `DeletedAt`: nil, `1 = *name`: td.Re(`^(?i)max`), // shell pattern, matches all names except Lastname as in model `2 =~ At\z`: td.Lte(time.Now()), // regexp, matches CreatedAt & UpdatedAt `3 !~ ^[A-Z]`: td.Ignore(), // private fields }, "ordered patterns") fmt.Println("Ordered patterns match only remaining fields:", ok) }
Output: Patterns match only remaining fields: true Ordered patterns match only remaining fields: true
func CmpSet ¶
CmpSet is a shortcut for:
td.Cmp(t, got, td.Set(expectedItems...), args...)
See Set for details.
Returns true if the test is OK, false if it fails.
If t is a *T then its Config field is inherited.
args... are optional and allow to name the test. This name is used in case of failure to qualify the test. If len(args) > 1 and the first item of args is a string and contains a '%' rune then fmt.Fprintf is used to compose the name, else args are passed to fmt.Fprint. Do not forget it is the name of the test, not the reason of a potential failure.
Example ¶
package main import ( "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := &testing.T{} got := []int{1, 3, 5, 8, 8, 1, 2} // Matches as all items are present, ignoring duplicates ok := td.CmpSet(t, got, []any{1, 2, 3, 5, 8}, "checks all items are present, in any order") fmt.Println(ok) // Duplicates are ignored in a Set ok = td.CmpSet(t, got, []any{1, 2, 2, 2, 2, 2, 3, 5, 8}, "checks all items are present, in any order") fmt.Println(ok) // Tries its best to not raise an error when a value can be matched // by several Set entries ok = td.CmpSet(t, got, []any{td.Between(1, 4), 3, td.Between(2, 10)}, "checks all items are present, in any order") fmt.Println(ok) // When expected is already a non-[]any slice, it cannot be // flattened directly using expected... without copying it to a new // []any slice, then use td.Flatten! expected := []int{1, 2, 3, 5, 8} ok = td.CmpSet(t, got, []any{td.Flatten(expected)}, "checks all expected items are present, in any order") fmt.Println(ok) }
Output: true true true true
func CmpShallow ¶
CmpShallow is a shortcut for:
td.Cmp(t, got, td.Shallow(expectedPtr), args...)
See Shallow for details.
Returns true if the test is OK, false if it fails.
If t is a *T then its Config field is inherited.
args... are optional and allow to name the test. This name is used in case of failure to qualify the test. If len(args) > 1 and the first item of args is a string and contains a '%' rune then fmt.Fprintf is used to compose the name, else args are passed to fmt.Fprint. Do not forget it is the name of the test, not the reason of a potential failure.
Example ¶
package main import ( "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := &testing.T{} type MyStruct struct { Value int } data := MyStruct{Value: 12} got := &data ok := td.CmpShallow(t, got, &data, "checks pointers only, not contents") fmt.Println(ok) // Same contents, but not same pointer ok = td.CmpShallow(t, got, &MyStruct{Value: 12}, "checks pointers only, not contents") fmt.Println(ok) }
Output: true false
Example (Slice) ¶
package main import ( "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := &testing.T{} back := []int{1, 2, 3, 1, 2, 3} a := back[:3] b := back[3:] ok := td.CmpShallow(t, a, back) fmt.Println("are ≠ but share the same area:", ok) ok = td.CmpShallow(t, b, back) fmt.Println("are = but do not point to same area:", ok) }
Output: are ≠ but share the same area: true are = but do not point to same area: false
Example (String) ¶
package main import ( "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := &testing.T{} back := "foobarfoobar" a := back[:6] b := back[6:] ok := td.CmpShallow(t, a, back) fmt.Println("are ≠ but share the same area:", ok) ok = td.CmpShallow(t, b, a) fmt.Println("are = but do not point to same area:", ok) }
Output: are ≠ but share the same area: true are = but do not point to same area: false
func CmpSlice ¶
func CmpSlice(t TestingT, got, model any, expectedEntries ArrayEntries, args ...any) bool
CmpSlice is a shortcut for:
td.Cmp(t, got, td.Slice(model, expectedEntries), args...)
See Slice for details.
Returns true if the test is OK, false if it fails.
If t is a *T then its Config field is inherited.
args... are optional and allow to name the test. This name is used in case of failure to qualify the test. If len(args) > 1 and the first item of args is a string and contains a '%' rune then fmt.Fprintf is used to compose the name, else args are passed to fmt.Fprint. Do not forget it is the name of the test, not the reason of a potential failure.
Example (Slice) ¶
package main import ( "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := &testing.T{} got := []int{42, 58, 26} ok := td.CmpSlice(t, got, []int{42}, td.ArrayEntries{1: 58, 2: td.Ignore()}, "checks slice %v", got) fmt.Println(ok) ok = td.CmpSlice(t, got, []int{}, td.ArrayEntries{0: 42, 1: 58, 2: td.Ignore()}, "checks slice %v", got) fmt.Println(ok) ok = td.CmpSlice(t, got, ([]int)(nil), td.ArrayEntries{0: 42, 1: 58, 2: td.Ignore()}, "checks slice %v", got) fmt.Println(ok) }
Output: true true true
Example (TypedSlice) ¶
package main import ( "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := &testing.T{} type MySlice []int got := MySlice{42, 58, 26} ok := td.CmpSlice(t, got, MySlice{42}, td.ArrayEntries{1: 58, 2: td.Ignore()}, "checks typed slice %v", got) fmt.Println(ok) ok = td.CmpSlice(t, &got, &MySlice{42}, td.ArrayEntries{1: 58, 2: td.Ignore()}, "checks pointer on typed slice %v", got) fmt.Println(ok) ok = td.CmpSlice(t, &got, &MySlice{}, td.ArrayEntries{0: 42, 1: 58, 2: td.Ignore()}, "checks pointer on typed slice %v", got) fmt.Println(ok) ok = td.CmpSlice(t, &got, (*MySlice)(nil), td.ArrayEntries{0: 42, 1: 58, 2: td.Ignore()}, "checks pointer on typed slice %v", got) fmt.Println(ok) }
Output: true true true true
func CmpSmuggle ¶
CmpSmuggle is a shortcut for:
td.Cmp(t, got, td.Smuggle(fn, expectedValue), args...)
See Smuggle for details.
Returns true if the test is OK, false if it fails.
If t is a *T then its Config field is inherited.
args... are optional and allow to name the test. This name is used in case of failure to qualify the test. If len(args) > 1 and the first item of args is a string and contains a '%' rune then fmt.Fprintf is used to compose the name, else args are passed to fmt.Fprint. Do not forget it is the name of the test, not the reason of a potential failure.
Example (Auto_unmarshal) ¶
package main import ( "encoding/json" "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := &testing.T{} // Automatically json.Unmarshal to compare got := []byte(`{"a":1,"b":2}`) ok := td.CmpSmuggle(t, got, func(b json.RawMessage) (r map[string]int, err error) { err = json.Unmarshal(b, &r) return }, map[string]int{ "a": 1, "b": 2, }) fmt.Println("JSON contents is OK:", ok) }
Output: JSON contents is OK: true
Example (Cast) ¶
package main import ( "bytes" "encoding/json" "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := &testing.T{} // A string containing JSON got := `{ "foo": 123 }` // Automatically cast a string to a json.RawMessage so td.JSON can operate ok := td.CmpSmuggle(t, got, json.RawMessage{}, td.JSON(`{"foo":123}`)) fmt.Println("JSON contents in string is OK:", ok) // Automatically read from io.Reader to a json.RawMessage ok = td.CmpSmuggle(t, bytes.NewReader([]byte(got)), json.RawMessage{}, td.JSON(`{"foo":123}`)) fmt.Println("JSON contents just read is OK:", ok) }
Output: JSON contents in string is OK: true JSON contents just read is OK: true
Example (Complex) ¶
package main import ( "fmt" "testing" "time" "github.com/maxatome/go-testdeep/td" ) func main() { t := &testing.T{} // No end date but a start date and a duration type StartDuration struct { StartDate time.Time Duration time.Duration } // Checks that end date is between 17th and 19th February both at 0h // for each of these durations in hours for _, duration := range []time.Duration{48 * time.Hour, 72 * time.Hour, 96 * time.Hour} { got := StartDuration{ StartDate: time.Date(2018, time.February, 14, 12, 13, 14, 0, time.UTC), Duration: duration, } // Simplest way, but in case of Between() failure, error will be bound // to DATA<smuggled>, not very clear... ok := td.CmpSmuggle(t, got, func(sd StartDuration) time.Time { return sd.StartDate.Add(sd.Duration) }, td.Between( time.Date(2018, time.February, 17, 0, 0, 0, 0, time.UTC), time.Date(2018, time.February, 19, 0, 0, 0, 0, time.UTC))) fmt.Println(ok) // Name the computed value "ComputedEndDate" to render a Between() failure // more understandable, so error will be bound to DATA.ComputedEndDate ok = td.CmpSmuggle(t, got, func(sd StartDuration) td.SmuggledGot { return td.SmuggledGot{ Name: "ComputedEndDate", Got: sd.StartDate.Add(sd.Duration), } }, td.Between( time.Date(2018, time.February, 17, 0, 0, 0, 0, time.UTC), time.Date(2018, time.February, 19, 0, 0, 0, 0, time.UTC))) fmt.Println(ok) } }
Output: false false true true true true
Example (Convert) ¶
package main import ( "fmt" "strconv" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := &testing.T{} got := int64(123) ok := td.CmpSmuggle(t, got, func(n int64) int { return int(n) }, 123, "checks int64 got against an int value") fmt.Println(ok) ok = td.CmpSmuggle(t, "123", func(numStr string) (int, bool) { n, err := strconv.Atoi(numStr) return n, err == nil }, td.Between(120, 130), "checks that number in %#v is in [120 .. 130]") fmt.Println(ok) ok = td.CmpSmuggle(t, "123", func(numStr string) (int, bool, string) { n, err := strconv.Atoi(numStr) if err != nil { return 0, false, "string must contain a number" } return n, true, "" }, td.Between(120, 130), "checks that number in %#v is in [120 .. 130]") fmt.Println(ok) ok = td.CmpSmuggle(t, "123", func(numStr string) (int, error) { //nolint: gocritic return strconv.Atoi(numStr) }, td.Between(120, 130), "checks that number in %#v is in [120 .. 130]") fmt.Println(ok) // Short version :) ok = td.CmpSmuggle(t, "123", strconv.Atoi, td.Between(120, 130), "checks that number in %#v is in [120 .. 130]") fmt.Println(ok) }
Output: true true true true true
Example (Field_path) ¶
package main import ( "errors" "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := &testing.T{} type Body struct { Name string Value any } type Request struct { Body *Body } type Transaction struct { Request } type ValueNum struct { Num int } got := &Transaction{ Request: Request{ Body: &Body{ Name: "test", Value: &ValueNum{Num: 123}, }, }, } // Want to check whether Num is between 100 and 200? ok := td.CmpSmuggle(t, got, func(t *Transaction) (int, error) { if t.Request.Body == nil || t.Request.Body.Value == nil { return 0, errors.New("Request.Body or Request.Body.Value is nil") } if v, ok := t.Request.Body.Value.(*ValueNum); ok && v != nil { return v.Num, nil } return 0, errors.New("Request.Body.Value isn't *ValueNum or nil") }, td.Between(100, 200)) fmt.Println("check Num by hand:", ok) // Same, but automagically generated... ok = td.CmpSmuggle(t, got, "Request.Body.Value.Num", td.Between(100, 200)) fmt.Println("check Num using a fields-path:", ok) // And as Request is an anonymous field, can be simplified further // as it can be omitted ok = td.CmpSmuggle(t, got, "Body.Value.Num", td.Between(100, 200)) fmt.Println("check Num using an other fields-path:", ok) // Note that maps and array/slices are supported got.Request.Body.Value = map[string]any{ "foo": []any{ 3: map[int]string{666: "bar"}, }, } ok = td.CmpSmuggle(t, got, "Body.Value[foo][3][666]", "bar") fmt.Println("check fields-path including maps/slices:", ok) }
Output: check Num by hand: true check Num using a fields-path: true check Num using an other fields-path: true check fields-path including maps/slices: true
Example (Interface) ¶
package main import ( "fmt" "testing" "time" "github.com/maxatome/go-testdeep/td" ) func main() { t := &testing.T{} gotTime, err := time.Parse(time.RFC3339, "2018-05-23T12:13:14Z") if err != nil { t.Fatal(err) } // Do not check the struct itself, but its stringified form ok := td.CmpSmuggle(t, gotTime, func(s fmt.Stringer) string { return s.String() }, "2018-05-23 12:13:14 +0000 UTC") fmt.Println("stringified time.Time OK:", ok) // If got does not implement the fmt.Stringer interface, it fails // without calling the Smuggle func type MyTime time.Time ok = td.CmpSmuggle(t, MyTime(gotTime), func(s fmt.Stringer) string { fmt.Println("Smuggle func called!") return s.String() }, "2018-05-23 12:13:14 +0000 UTC") fmt.Println("stringified MyTime OK:", ok) }
Output: stringified time.Time OK: true stringified MyTime OK: false
Example (Lax) ¶
package main import ( "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := &testing.T{} // got is an int16 and Smuggle func input is an int64: it is OK got := int(123) ok := td.CmpSmuggle(t, got, func(n int64) uint32 { return uint32(n) }, uint32(123)) fmt.Println("got int16(123) → smuggle via int64 → uint32(123):", ok) }
Output: got int16(123) → smuggle via int64 → uint32(123): true
func CmpString ¶
CmpString is a shortcut for:
td.Cmp(t, got, td.String(expected), args...)
See String for details.
Returns true if the test is OK, false if it fails.
If t is a *T then its Config field is inherited.
args... are optional and allow to name the test. This name is used in case of failure to qualify the test. If len(args) > 1 and the first item of args is a string and contains a '%' rune then fmt.Fprintf is used to compose the name, else args are passed to fmt.Fprint. Do not forget it is the name of the test, not the reason of a potential failure.
Example ¶
package main import ( "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := &testing.T{} got := "foobar" ok := td.CmpString(t, got, "foobar", "checks %s", got) fmt.Println("using string:", ok) ok = td.Cmp(t, []byte(got), td.String("foobar"), "checks %s", got) fmt.Println("using []byte:", ok) }
Output: using string: true using []byte: true
Example (Error) ¶
package main import ( "errors" "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := &testing.T{} got := errors.New("foobar") ok := td.CmpString(t, got, "foobar", "checks %s", got) fmt.Println(ok) }
Output: true
Example (Stringer) ¶
package main import ( "bytes" "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := &testing.T{} // bytes.Buffer implements fmt.Stringer got := bytes.NewBufferString("foobar") ok := td.CmpString(t, got, "foobar", "checks %s", got) fmt.Println(ok) }
Output: true
func CmpStruct ¶
func CmpStruct(t TestingT, got, model any, expectedFields StructFields, args ...any) bool
CmpStruct is a shortcut for:
td.Cmp(t, got, td.Struct(model, expectedFields), args...)
See Struct for details.
Struct optional parameter expectedFields is here mandatory. nil value should be passed to mimic its absence in original Struct call.
Returns true if the test is OK, false if it fails.
If t is a *T then its Config field is inherited.
args... are optional and allow to name the test. This name is used in case of failure to qualify the test. If len(args) > 1 and the first item of args is a string and contains a '%' rune then fmt.Fprintf is used to compose the name, else args are passed to fmt.Fprint. Do not forget it is the name of the test, not the reason of a potential failure.
Example ¶
package main import ( "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := &testing.T{} type Person struct { Name string Age int NumChildren int } got := Person{ Name: "Foobar", Age: 42, NumChildren: 3, } // As NumChildren is zero in Struct() call, it is not checked ok := td.CmpStruct(t, got, Person{Name: "Foobar"}, td.StructFields{ "Age": td.Between(40, 50), }, "checks %v is the right Person") fmt.Println("Foobar is between 40 & 50:", ok) // Model can be empty ok = td.CmpStruct(t, got, Person{}, td.StructFields{ "Name": "Foobar", "Age": td.Between(40, 50), "NumChildren": td.Not(0), }, "checks %v is the right Person") fmt.Println("Foobar has some children:", ok) // Works with pointers too ok = td.CmpStruct(t, &got, &Person{}, td.StructFields{ "Name": "Foobar", "Age": td.Between(40, 50), "NumChildren": td.Not(0), }, "checks %v is the right Person") fmt.Println("Foobar has some children (using pointer):", ok) // Model does not need to be instanciated ok = td.CmpStruct(t, &got, (*Person)(nil), td.StructFields{ "Name": "Foobar", "Age": td.Between(40, 50), "NumChildren": td.Not(0), }, "checks %v is the right Person") fmt.Println("Foobar has some children (using nil model):", ok) }
Output: Foobar is between 40 & 50: true Foobar has some children: true Foobar has some children (using pointer): true Foobar has some children (using nil model): true
Example (Overwrite_model) ¶
package main import ( "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := &testing.T{} type Person struct { Name string Age int NumChildren int } got := Person{ Name: "Foobar", Age: 42, NumChildren: 3, } ok := td.CmpStruct(t, got, Person{ Name: "Foobar", Age: 53, }, td.StructFields{ ">Age": td.Between(40, 50), // ">" to overwrite Age:53 in model "NumChildren": td.Gt(2), }, "checks %v is the right Person") fmt.Println("Foobar is between 40 & 50:", ok) ok = td.CmpStruct(t, got, Person{ Name: "Foobar", Age: 53, }, td.StructFields{ "> Age": td.Between(40, 50), // same, ">" can be followed by spaces "NumChildren": td.Gt(2), }, "checks %v is the right Person") fmt.Println("Foobar is between 40 & 50:", ok) }
Output: Foobar is between 40 & 50: true Foobar is between 40 & 50: true
Example (Patterns) ¶
package main import ( "fmt" "testing" "time" "github.com/maxatome/go-testdeep/td" ) func main() { t := &testing.T{} type Person struct { Firstname string Lastname string Surname string Nickname string CreatedAt time.Time UpdatedAt time.Time DeletedAt *time.Time } now := time.Now() got := Person{ Firstname: "Maxime", Lastname: "Foo", Surname: "Max", Nickname: "max", CreatedAt: now, UpdatedAt: now, DeletedAt: nil, // not deleted yet } ok := td.CmpStruct(t, got, Person{Lastname: "Foo"}, td.StructFields{ `DeletedAt`: nil, `= *name`: td.Re(`^(?i)max`), // shell pattern, matches all names except Lastname as in model `=~ At\z`: td.Lte(time.Now()), // regexp, matches CreatedAt & UpdatedAt }, "mix shell & regexp patterns") fmt.Println("Patterns match only remaining fields:", ok) ok = td.CmpStruct(t, got, Person{Lastname: "Foo"}, td.StructFields{ `DeletedAt`: nil, `1 = *name`: td.Re(`^(?i)max`), // shell pattern, matches all names except Lastname as in model `2 =~ At\z`: td.Lte(time.Now()), // regexp, matches CreatedAt & UpdatedAt }, "ordered patterns") fmt.Println("Ordered patterns match only remaining fields:", ok) }
Output: Patterns match only remaining fields: true Ordered patterns match only remaining fields: true
func CmpSubBagOf ¶
CmpSubBagOf is a shortcut for:
td.Cmp(t, got, td.SubBagOf(expectedItems...), args...)
See SubBagOf for details.
Returns true if the test is OK, false if it fails.
If t is a *T then its Config field is inherited.
args... are optional and allow to name the test. This name is used in case of failure to qualify the test. If len(args) > 1 and the first item of args is a string and contains a '%' rune then fmt.Fprintf is used to compose the name, else args are passed to fmt.Fprint. Do not forget it is the name of the test, not the reason of a potential failure.
Example ¶
package main import ( "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := &testing.T{} got := []int{1, 3, 5, 8, 8, 1, 2} ok := td.CmpSubBagOf(t, got, []any{0, 0, 1, 1, 2, 2, 3, 3, 5, 5, 8, 8, 9, 9}, "checks at least all items are present, in any order") fmt.Println(ok) // got contains one 8 too many ok = td.CmpSubBagOf(t, got, []any{0, 0, 1, 1, 2, 2, 3, 3, 5, 5, 8, 9, 9}, "checks at least all items are present, in any order") fmt.Println(ok) got = []int{1, 3, 5, 2} ok = td.CmpSubBagOf(t, got, []any{td.Between(0, 3), td.Between(0, 3), td.Between(0, 3), td.Between(0, 3), td.Gt(4), td.Gt(4)}, "checks at least all items match, in any order with TestDeep operators") fmt.Println(ok) // When expected is already a non-[]any slice, it cannot be // flattened directly using expected... without copying it to a new // []any slice, then use td.Flatten! expected := []int{1, 2, 3, 5, 9, 8} ok = td.CmpSubBagOf(t, got, []any{td.Flatten(expected)}, "checks at least all expected items are present, in any order") fmt.Println(ok) }
Output: true false true true
func CmpSubJSONOf ¶
CmpSubJSONOf is a shortcut for:
td.Cmp(t, got, td.SubJSONOf(expectedJSON, params...), args...)
See SubJSONOf for details.
Returns true if the test is OK, false if it fails.
If t is a *T then its Config field is inherited.
args... are optional and allow to name the test. This name is used in case of failure to qualify the test. If len(args) > 1 and the first item of args is a string and contains a '%' rune then fmt.Fprintf is used to compose the name, else args are passed to fmt.Fprint. Do not forget it is the name of the test, not the reason of a potential failure.
Example (Basic) ¶
package main import ( "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := &testing.T{} got := &struct { Fullname string `json:"fullname"` Age int `json:"age"` }{ Fullname: "Bob", Age: 42, } ok := td.CmpSubJSONOf(t, got, `{"age":42,"fullname":"Bob","gender":"male"}`, nil) fmt.Println("check got with age then fullname:", ok) ok = td.CmpSubJSONOf(t, got, `{"fullname":"Bob","age":42,"gender":"male"}`, nil) fmt.Println("check got with fullname then age:", ok) ok = td.CmpSubJSONOf(t, got, ` // This should be the JSON representation of a struct { // A person: "fullname": "Bob", // The name of this person "age": 42, /* The age of this person: - 42 of course - to demonstrate a multi-lines comment */ "gender": "male" // This field is ignored as SubJSONOf }`, nil) fmt.Println("check got with nicely formatted and commented JSON:", ok) ok = td.CmpSubJSONOf(t, got, `{"fullname":"Bob","gender":"male"}`, nil) fmt.Println("check got without age field:", ok) }
Output: check got with age then fullname: true check got with fullname then age: true check got with nicely formatted and commented JSON: true check got without age field: false
Example (File) ¶
package main import ( "fmt" "os" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := &testing.T{} got := &struct { Fullname string `json:"fullname"` Age int `json:"age"` Gender string `json:"gender"` }{ Fullname: "Bob Foobar", Age: 42, Gender: "male", } tmpDir, err := os.MkdirTemp("", "") if err != nil { t.Fatal(err) } defer os.RemoveAll(tmpDir) // clean up filename := tmpDir + "/test.json" if err = os.WriteFile(filename, []byte(` { "fullname": "$name", "age": "$age", "gender": "$gender", "details": { "city": "TestCity", "zip": 666 } }`), 0644); err != nil { t.Fatal(err) } // OK let's test with this file ok := td.CmpSubJSONOf(t, got, filename, []any{td.Tag("name", td.HasPrefix("Bob")), td.Tag("age", td.Between(40, 45)), td.Tag("gender", td.Re(`^(male|female)\z`))}) fmt.Println("Full match from file name:", ok) // When the file is already open file, err := os.Open(filename) if err != nil { t.Fatal(err) } ok = td.CmpSubJSONOf(t, got, file, []any{td.Tag("name", td.HasPrefix("Bob")), td.Tag("age", td.Between(40, 45)), td.Tag("gender", td.Re(`^(male|female)\z`))}) fmt.Println("Full match from io.Reader:", ok) }
Output: Full match from file name: true Full match from io.Reader: true
Example (Placeholders) ¶
package main import ( "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := &testing.T{} got := &struct { Fullname string `json:"fullname"` Age int `json:"age"` }{ Fullname: "Bob Foobar", Age: 42, } ok := td.CmpSubJSONOf(t, got, `{"age": $1, "fullname": $2, "gender": $3}`, []any{42, "Bob Foobar", "male"}) fmt.Println("check got with numeric placeholders without operators:", ok) ok = td.CmpSubJSONOf(t, got, `{"age": $1, "fullname": $2, "gender": $3}`, []any{td.Between(40, 45), td.HasSuffix("Foobar"), td.NotEmpty()}) fmt.Println("check got with numeric placeholders:", ok) ok = td.CmpSubJSONOf(t, got, `{"age": "$1", "fullname": "$2", "gender": "$3"}`, []any{td.Between(40, 45), td.HasSuffix("Foobar"), td.NotEmpty()}) fmt.Println("check got with double-quoted numeric placeholders:", ok) ok = td.CmpSubJSONOf(t, got, `{"age": $age, "fullname": $name, "gender": $gender}`, []any{td.Tag("age", td.Between(40, 45)), td.Tag("name", td.HasSuffix("Foobar")), td.Tag("gender", td.NotEmpty())}) fmt.Println("check got with named placeholders:", ok) ok = td.CmpSubJSONOf(t, got, `{"age": $^NotZero, "fullname": $^NotEmpty, "gender": $^NotEmpty}`, nil) fmt.Println("check got with operator shortcuts:", ok) }
Output: check got with numeric placeholders without operators: true check got with numeric placeholders: true check got with double-quoted numeric placeholders: true check got with named placeholders: true check got with operator shortcuts: true
func CmpSubMapOf ¶
func CmpSubMapOf(t TestingT, got, model any, expectedEntries MapEntries, args ...any) bool
CmpSubMapOf is a shortcut for:
td.Cmp(t, got, td.SubMapOf(model, expectedEntries), args...)
See SubMapOf for details.
Returns true if the test is OK, false if it fails.
If t is a *T then its Config field is inherited.
args... are optional and allow to name the test. This name is used in case of failure to qualify the test. If len(args) > 1 and the first item of args is a string and contains a '%' rune then fmt.Fprintf is used to compose the name, else args are passed to fmt.Fprint. Do not forget it is the name of the test, not the reason of a potential failure.
Example (Map) ¶
package main import ( "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := &testing.T{} got := map[string]int{"foo": 12, "bar": 42} ok := td.CmpSubMapOf(t, got, map[string]int{"bar": 42}, td.MapEntries{"foo": td.Lt(15), "zip": 666}, "checks map %v is included in expected keys/values", got) fmt.Println(ok) }
Output: true
Example (TypedMap) ¶
package main import ( "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := &testing.T{} type MyMap map[string]int got := MyMap{"foo": 12, "bar": 42} ok := td.CmpSubMapOf(t, got, MyMap{"bar": 42}, td.MapEntries{"foo": td.Lt(15), "zip": 666}, "checks typed map %v is included in expected keys/values", got) fmt.Println(ok) ok = td.CmpSubMapOf(t, &got, &MyMap{"bar": 42}, td.MapEntries{"foo": td.Lt(15), "zip": 666}, "checks pointed typed map %v is included in expected keys/values", got) fmt.Println(ok) }
Output: true true
func CmpSubSetOf ¶
CmpSubSetOf is a shortcut for:
td.Cmp(t, got, td.SubSetOf(expectedItems...), args...)
See SubSetOf for details.
Returns true if the test is OK, false if it fails.
If t is a *T then its Config field is inherited.
args... are optional and allow to name the test. This name is used in case of failure to qualify the test. If len(args) > 1 and the first item of args is a string and contains a '%' rune then fmt.Fprintf is used to compose the name, else args are passed to fmt.Fprint. Do not forget it is the name of the test, not the reason of a potential failure.
Example ¶
package main import ( "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := &testing.T{} got := []int{1, 3, 5, 8, 8, 1, 2} // Matches as all items are expected, ignoring duplicates ok := td.CmpSubSetOf(t, got, []any{1, 2, 3, 4, 5, 6, 7, 8}, "checks at least all items are present, in any order, ignoring duplicates") fmt.Println(ok) // Tries its best to not raise an error when a value can be matched // by several SubSetOf entries ok = td.CmpSubSetOf(t, got, []any{td.Between(1, 4), 3, td.Between(2, 10), td.Gt(100)}, "checks at least all items are present, in any order, ignoring duplicates") fmt.Println(ok) // When expected is already a non-[]any slice, it cannot be // flattened directly using expected... without copying it to a new // []any slice, then use td.Flatten! expected := []int{1, 2, 3, 4, 5, 6, 7, 8} ok = td.CmpSubSetOf(t, got, []any{td.Flatten(expected)}, "checks at least all expected items are present, in any order, ignoring duplicates") fmt.Println(ok) }
Output: true true true
func CmpSuperBagOf ¶
CmpSuperBagOf is a shortcut for:
td.Cmp(t, got, td.SuperBagOf(expectedItems...), args...)
See SuperBagOf for details.
Returns true if the test is OK, false if it fails.
If t is a *T then its Config field is inherited.
args... are optional and allow to name the test. This name is used in case of failure to qualify the test. If len(args) > 1 and the first item of args is a string and contains a '%' rune then fmt.Fprintf is used to compose the name, else args are passed to fmt.Fprint. Do not forget it is the name of the test, not the reason of a potential failure.
Example ¶
package main import ( "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := &testing.T{} got := []int{1, 3, 5, 8, 8, 1, 2} ok := td.CmpSuperBagOf(t, got, []any{8, 5, 8}, "checks the items are present, in any order") fmt.Println(ok) ok = td.CmpSuperBagOf(t, got, []any{td.Gt(5), td.Lte(2)}, "checks at least 2 items of %v match", got) fmt.Println(ok) // When expected is already a non-[]any slice, it cannot be // flattened directly using expected... without copying it to a new // []any slice, then use td.Flatten! expected := []int{8, 5, 8} ok = td.CmpSuperBagOf(t, got, []any{td.Flatten(expected)}, "checks the expected items are present, in any order") fmt.Println(ok) }
Output: true true true
func CmpSuperJSONOf ¶
CmpSuperJSONOf is a shortcut for:
td.Cmp(t, got, td.SuperJSONOf(expectedJSON, params...), args...)
See SuperJSONOf for details.
Returns true if the test is OK, false if it fails.
If t is a *T then its Config field is inherited.
args... are optional and allow to name the test. This name is used in case of failure to qualify the test. If len(args) > 1 and the first item of args is a string and contains a '%' rune then fmt.Fprintf is used to compose the name, else args are passed to fmt.Fprint. Do not forget it is the name of the test, not the reason of a potential failure.
Example (Basic) ¶
package main import ( "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := &testing.T{} got := &struct { Fullname string `json:"fullname"` Age int `json:"age"` Gender string `json:"gender"` City string `json:"city"` Zip int `json:"zip"` }{ Fullname: "Bob", Age: 42, Gender: "male", City: "TestCity", Zip: 666, } ok := td.CmpSuperJSONOf(t, got, `{"age":42,"fullname":"Bob","gender":"male"}`, nil) fmt.Println("check got with age then fullname:", ok) ok = td.CmpSuperJSONOf(t, got, `{"fullname":"Bob","age":42,"gender":"male"}`, nil) fmt.Println("check got with fullname then age:", ok) ok = td.CmpSuperJSONOf(t, got, ` // This should be the JSON representation of a struct { // A person: "fullname": "Bob", // The name of this person "age": 42, /* The age of this person: - 42 of course - to demonstrate a multi-lines comment */ "gender": "male" // The gender! }`, nil) fmt.Println("check got with nicely formatted and commented JSON:", ok) ok = td.CmpSuperJSONOf(t, got, `{"fullname":"Bob","gender":"male","details":{}}`, nil) fmt.Println("check got with details field:", ok) }
Output: check got with age then fullname: true check got with fullname then age: true check got with nicely formatted and commented JSON: true check got with details field: false
Example (File) ¶
package main import ( "fmt" "os" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := &testing.T{} got := &struct { Fullname string `json:"fullname"` Age int `json:"age"` Gender string `json:"gender"` City string `json:"city"` Zip int `json:"zip"` }{ Fullname: "Bob Foobar", Age: 42, Gender: "male", City: "TestCity", Zip: 666, } tmpDir, err := os.MkdirTemp("", "") if err != nil { t.Fatal(err) } defer os.RemoveAll(tmpDir) // clean up filename := tmpDir + "/test.json" if err = os.WriteFile(filename, []byte(` { "fullname": "$name", "age": "$age", "gender": "$gender" }`), 0644); err != nil { t.Fatal(err) } // OK let's test with this file ok := td.CmpSuperJSONOf(t, got, filename, []any{td.Tag("name", td.HasPrefix("Bob")), td.Tag("age", td.Between(40, 45)), td.Tag("gender", td.Re(`^(male|female)\z`))}) fmt.Println("Full match from file name:", ok) // When the file is already open file, err := os.Open(filename) if err != nil { t.Fatal(err) } ok = td.CmpSuperJSONOf(t, got, file, []any{td.Tag("name", td.HasPrefix("Bob")), td.Tag("age", td.Between(40, 45)), td.Tag("gender", td.Re(`^(male|female)\z`))}) fmt.Println("Full match from io.Reader:", ok) }
Output: Full match from file name: true Full match from io.Reader: true
Example (Placeholders) ¶
package main import ( "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := &testing.T{} got := &struct { Fullname string `json:"fullname"` Age int `json:"age"` Gender string `json:"gender"` City string `json:"city"` Zip int `json:"zip"` }{ Fullname: "Bob Foobar", Age: 42, Gender: "male", City: "TestCity", Zip: 666, } ok := td.CmpSuperJSONOf(t, got, `{"age": $1, "fullname": $2, "gender": $3}`, []any{42, "Bob Foobar", "male"}) fmt.Println("check got with numeric placeholders without operators:", ok) ok = td.CmpSuperJSONOf(t, got, `{"age": $1, "fullname": $2, "gender": $3}`, []any{td.Between(40, 45), td.HasSuffix("Foobar"), td.NotEmpty()}) fmt.Println("check got with numeric placeholders:", ok) ok = td.CmpSuperJSONOf(t, got, `{"age": "$1", "fullname": "$2", "gender": "$3"}`, []any{td.Between(40, 45), td.HasSuffix("Foobar"), td.NotEmpty()}) fmt.Println("check got with double-quoted numeric placeholders:", ok) ok = td.CmpSuperJSONOf(t, got, `{"age": $age, "fullname": $name, "gender": $gender}`, []any{td.Tag("age", td.Between(40, 45)), td.Tag("name", td.HasSuffix("Foobar")), td.Tag("gender", td.NotEmpty())}) fmt.Println("check got with named placeholders:", ok) ok = td.CmpSuperJSONOf(t, got, `{"age": $^NotZero, "fullname": $^NotEmpty, "gender": $^NotEmpty}`, nil) fmt.Println("check got with operator shortcuts:", ok) }
Output: check got with numeric placeholders without operators: true check got with numeric placeholders: true check got with double-quoted numeric placeholders: true check got with named placeholders: true check got with operator shortcuts: true
func CmpSuperMapOf ¶
func CmpSuperMapOf(t TestingT, got, model any, expectedEntries MapEntries, args ...any) bool
CmpSuperMapOf is a shortcut for:
td.Cmp(t, got, td.SuperMapOf(model, expectedEntries), args...)
See SuperMapOf for details.
Returns true if the test is OK, false if it fails.
If t is a *T then its Config field is inherited.
args... are optional and allow to name the test. This name is used in case of failure to qualify the test. If len(args) > 1 and the first item of args is a string and contains a '%' rune then fmt.Fprintf is used to compose the name, else args are passed to fmt.Fprint. Do not forget it is the name of the test, not the reason of a potential failure.
Example (Map) ¶
package main import ( "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := &testing.T{} got := map[string]int{"foo": 12, "bar": 42, "zip": 89} ok := td.CmpSuperMapOf(t, got, map[string]int{"bar": 42}, td.MapEntries{"foo": td.Lt(15)}, "checks map %v contains at leat all expected keys/values", got) fmt.Println(ok) }
Output: true
Example (TypedMap) ¶
package main import ( "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := &testing.T{} type MyMap map[string]int got := MyMap{"foo": 12, "bar": 42, "zip": 89} ok := td.CmpSuperMapOf(t, got, MyMap{"bar": 42}, td.MapEntries{"foo": td.Lt(15)}, "checks typed map %v contains at leat all expected keys/values", got) fmt.Println(ok) ok = td.CmpSuperMapOf(t, &got, &MyMap{"bar": 42}, td.MapEntries{"foo": td.Lt(15)}, "checks pointed typed map %v contains at leat all expected keys/values", got) fmt.Println(ok) }
Output: true true
func CmpSuperSetOf ¶
CmpSuperSetOf is a shortcut for:
td.Cmp(t, got, td.SuperSetOf(expectedItems...), args...)
See SuperSetOf for details.
Returns true if the test is OK, false if it fails.
If t is a *T then its Config field is inherited.
args... are optional and allow to name the test. This name is used in case of failure to qualify the test. If len(args) > 1 and the first item of args is a string and contains a '%' rune then fmt.Fprintf is used to compose the name, else args are passed to fmt.Fprint. Do not forget it is the name of the test, not the reason of a potential failure.
Example ¶
package main import ( "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := &testing.T{} got := []int{1, 3, 5, 8, 8, 1, 2} ok := td.CmpSuperSetOf(t, got, []any{1, 2, 3}, "checks the items are present, in any order and ignoring duplicates") fmt.Println(ok) ok = td.CmpSuperSetOf(t, got, []any{td.Gt(5), td.Lte(2)}, "checks at least 2 items of %v match ignoring duplicates", got) fmt.Println(ok) // When expected is already a non-[]any slice, it cannot be // flattened directly using expected... without copying it to a new // []any slice, then use td.Flatten! expected := []int{1, 2, 3} ok = td.CmpSuperSetOf(t, got, []any{td.Flatten(expected)}, "checks the expected items are present, in any order and ignoring duplicates") fmt.Println(ok) }
Output: true true true
func CmpSuperSliceOf ¶ added in v1.10.0
func CmpSuperSliceOf(t TestingT, got, model any, expectedEntries ArrayEntries, args ...any) bool
CmpSuperSliceOf is a shortcut for:
td.Cmp(t, got, td.SuperSliceOf(model, expectedEntries), args...)
See SuperSliceOf for details.
Returns true if the test is OK, false if it fails.
If t is a *T then its Config field is inherited.
args... are optional and allow to name the test. This name is used in case of failure to qualify the test. If len(args) > 1 and the first item of args is a string and contains a '%' rune then fmt.Fprintf is used to compose the name, else args are passed to fmt.Fprint. Do not forget it is the name of the test, not the reason of a potential failure.
Example (Array) ¶
package main import ( "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := &testing.T{} got := [4]int{42, 58, 26, 666} ok := td.CmpSuperSliceOf(t, got, [4]int{1: 58}, td.ArrayEntries{3: td.Gt(660)}, "checks array %v", got) fmt.Println("Only check items #1 & #3:", ok) ok = td.CmpSuperSliceOf(t, got, [4]int{}, td.ArrayEntries{0: 42, 3: td.Between(660, 670)}, "checks array %v", got) fmt.Println("Only check items #0 & #3:", ok) ok = td.CmpSuperSliceOf(t, &got, &[4]int{}, td.ArrayEntries{0: 42, 3: td.Between(660, 670)}, "checks array %v", got) fmt.Println("Only check items #0 & #3 of an array pointer:", ok) ok = td.CmpSuperSliceOf(t, &got, (*[4]int)(nil), td.ArrayEntries{0: 42, 3: td.Between(660, 670)}, "checks array %v", got) fmt.Println("Only check items #0 & #3 of an array pointer, using nil model:", ok) }
Output: Only check items #1 & #3: true Only check items #0 & #3: true Only check items #0 & #3 of an array pointer: true Only check items #0 & #3 of an array pointer, using nil model: true
Example (Slice) ¶
package main import ( "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := &testing.T{} got := []int{42, 58, 26, 666} ok := td.CmpSuperSliceOf(t, got, []int{1: 58}, td.ArrayEntries{3: td.Gt(660)}, "checks array %v", got) fmt.Println("Only check items #1 & #3:", ok) ok = td.CmpSuperSliceOf(t, got, []int{}, td.ArrayEntries{0: 42, 3: td.Between(660, 670)}, "checks array %v", got) fmt.Println("Only check items #0 & #3:", ok) ok = td.CmpSuperSliceOf(t, &got, &[]int{}, td.ArrayEntries{0: 42, 3: td.Between(660, 670)}, "checks array %v", got) fmt.Println("Only check items #0 & #3 of a slice pointer:", ok) ok = td.CmpSuperSliceOf(t, &got, (*[]int)(nil), td.ArrayEntries{0: 42, 3: td.Between(660, 670)}, "checks array %v", got) fmt.Println("Only check items #0 & #3 of a slice pointer, using nil model:", ok) }
Output: Only check items #1 & #3: true Only check items #0 & #3: true Only check items #0 & #3 of a slice pointer: true Only check items #0 & #3 of a slice pointer, using nil model: true
Example (TypedArray) ¶
package main import ( "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := &testing.T{} type MyArray [4]int got := MyArray{42, 58, 26, 666} ok := td.CmpSuperSliceOf(t, got, MyArray{1: 58}, td.ArrayEntries{3: td.Gt(660)}, "checks typed array %v", got) fmt.Println("Only check items #1 & #3:", ok) ok = td.CmpSuperSliceOf(t, got, MyArray{}, td.ArrayEntries{0: 42, 3: td.Between(660, 670)}, "checks array %v", got) fmt.Println("Only check items #0 & #3:", ok) ok = td.CmpSuperSliceOf(t, &got, &MyArray{}, td.ArrayEntries{0: 42, 3: td.Between(660, 670)}, "checks array %v", got) fmt.Println("Only check items #0 & #3 of an array pointer:", ok) ok = td.CmpSuperSliceOf(t, &got, (*MyArray)(nil), td.ArrayEntries{0: 42, 3: td.Between(660, 670)}, "checks array %v", got) fmt.Println("Only check items #0 & #3 of an array pointer, using nil model:", ok) }
Output: Only check items #1 & #3: true Only check items #0 & #3: true Only check items #0 & #3 of an array pointer: true Only check items #0 & #3 of an array pointer, using nil model: true
Example (TypedSlice) ¶
package main import ( "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := &testing.T{} type MySlice []int got := MySlice{42, 58, 26, 666} ok := td.CmpSuperSliceOf(t, got, MySlice{1: 58}, td.ArrayEntries{3: td.Gt(660)}, "checks typed array %v", got) fmt.Println("Only check items #1 & #3:", ok) ok = td.CmpSuperSliceOf(t, got, MySlice{}, td.ArrayEntries{0: 42, 3: td.Between(660, 670)}, "checks array %v", got) fmt.Println("Only check items #0 & #3:", ok) ok = td.CmpSuperSliceOf(t, &got, &MySlice{}, td.ArrayEntries{0: 42, 3: td.Between(660, 670)}, "checks array %v", got) fmt.Println("Only check items #0 & #3 of a slice pointer:", ok) ok = td.CmpSuperSliceOf(t, &got, (*MySlice)(nil), td.ArrayEntries{0: 42, 3: td.Between(660, 670)}, "checks array %v", got) fmt.Println("Only check items #0 & #3 of a slice pointer, using nil model:", ok) }
Output: Only check items #1 & #3: true Only check items #0 & #3: true Only check items #0 & #3 of a slice pointer: true Only check items #0 & #3 of a slice pointer, using nil model: true
func CmpTrue ¶
CmpTrue is a shortcut for:
td.Cmp(t, got, true, args...)
Returns true if the test is OK, false if it fails.
td.CmpTrue(t, IsAvailable(x), "x should be available")
args... are optional and allow to name the test. This name is used in case of failure to qualify the test. If len(args) > 1 and the first item of args is a string and contains a '%' rune then fmt.Fprintf is used to compose the name, else args are passed to fmt.Fprint. Do not forget it is the name of the test, not the reason of a potential failure.
Example ¶
package main import ( "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := &testing.T{} got := true ok := td.CmpTrue(t, got, "check that got is true!") fmt.Println(ok) got = false ok = td.CmpTrue(t, got, "check that got is true!") fmt.Println(ok) }
Output: true false
func CmpTruncTime ¶
CmpTruncTime is a shortcut for:
td.Cmp(t, got, td.TruncTime(expectedTime, trunc), args...)
See TruncTime for details.
TruncTime optional parameter trunc is here mandatory. 0 value should be passed to mimic its absence in original TruncTime call.
Returns true if the test is OK, false if it fails.
If t is a *T then its Config field is inherited.
args... are optional and allow to name the test. This name is used in case of failure to qualify the test. If len(args) > 1 and the first item of args is a string and contains a '%' rune then fmt.Fprintf is used to compose the name, else args are passed to fmt.Fprint. Do not forget it is the name of the test, not the reason of a potential failure.
Example ¶
package main import ( "fmt" "testing" "time" "github.com/maxatome/go-testdeep/td" ) func main() { t := &testing.T{} dateToTime := func(str string) time.Time { t, err := time.Parse(time.RFC3339Nano, str) if err != nil { panic(err) } return t } got := dateToTime("2018-05-01T12:45:53.123456789Z") // Compare dates ignoring nanoseconds and monotonic parts expected := dateToTime("2018-05-01T12:45:53Z") ok := td.CmpTruncTime(t, got, expected, time.Second, "checks date %v, truncated to the second", got) fmt.Println(ok) // Compare dates ignoring time and so monotonic parts expected = dateToTime("2018-05-01T11:22:33.444444444Z") ok = td.CmpTruncTime(t, got, expected, 24*time.Hour, "checks date %v, truncated to the day", got) fmt.Println(ok) // Compare dates exactly but ignoring monotonic part expected = dateToTime("2018-05-01T12:45:53.123456789Z") ok = td.CmpTruncTime(t, got, expected, 0, "checks date %v ignoring monotonic part", got) fmt.Println(ok) }
Output: true true true
func CmpValues ¶
CmpValues is a shortcut for:
td.Cmp(t, got, td.Values(val), args...)
See Values for details.
Returns true if the test is OK, false if it fails.
If t is a *T then its Config field is inherited.
args... are optional and allow to name the test. This name is used in case of failure to qualify the test. If len(args) > 1 and the first item of args is a string and contains a '%' rune then fmt.Fprintf is used to compose the name, else args are passed to fmt.Fprint. Do not forget it is the name of the test, not the reason of a potential failure.
Example ¶
package main import ( "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := &testing.T{} got := map[string]int{"foo": 1, "bar": 2, "zip": 3} // Values tests values in an ordered manner ok := td.CmpValues(t, got, []int{1, 2, 3}) fmt.Println("All sorted values are found:", ok) // If the expected values are not ordered, it fails ok = td.CmpValues(t, got, []int{3, 1, 2}) fmt.Println("All unsorted values are found:", ok) // To circumvent that, one can use Bag operator ok = td.CmpValues(t, got, td.Bag(3, 1, 2)) fmt.Println("All unsorted values are found, with the help of Bag operator:", ok) // Check that each value is between 1 and 3 ok = td.CmpValues(t, got, td.ArrayEach(td.Between(1, 3))) fmt.Println("Each value is between 1 and 3:", ok) }
Output: All sorted values are found: true All unsorted values are found: false All unsorted values are found, with the help of Bag operator: true Each value is between 1 and 3: true
func CmpZero ¶
CmpZero is a shortcut for:
td.Cmp(t, got, td.Zero(), args...)
See Zero for details.
Returns true if the test is OK, false if it fails.
If t is a *T then its Config field is inherited.
args... are optional and allow to name the test. This name is used in case of failure to qualify the test. If len(args) > 1 and the first item of args is a string and contains a '%' rune then fmt.Fprintf is used to compose the name, else args are passed to fmt.Fprint. Do not forget it is the name of the test, not the reason of a potential failure.
Example ¶
package main import ( "bytes" "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := &testing.T{} ok := td.CmpZero(t, 0) fmt.Println(ok) ok = td.CmpZero(t, float64(0)) fmt.Println(ok) ok = td.CmpZero(t, 12) // fails, as 12 is not 0 :) fmt.Println(ok) ok = td.CmpZero(t, (map[string]int)(nil)) fmt.Println(ok) ok = td.CmpZero(t, map[string]int{}) // fails, as not nil fmt.Println(ok) ok = td.CmpZero(t, ([]int)(nil)) fmt.Println(ok) ok = td.CmpZero(t, []int{}) // fails, as not nil fmt.Println(ok) ok = td.CmpZero(t, [3]int{}) fmt.Println(ok) ok = td.CmpZero(t, [3]int{0, 1}) // fails, DATA[1] is not 0 fmt.Println(ok) ok = td.CmpZero(t, bytes.Buffer{}) fmt.Println(ok) ok = td.CmpZero(t, &bytes.Buffer{}) // fails, as pointer not nil fmt.Println(ok) ok = td.Cmp(t, &bytes.Buffer{}, td.Ptr(td.Zero())) // OK with the help of Ptr() fmt.Println(ok) }
Output: true true false true false true false true false true false true
func EqDeeply ¶
EqDeeply returns true if got matches expected. expected can be the same type as got is, or contains some TestDeep operators.
got := "foobar" td.EqDeeply(got, "foobar") // returns true td.EqDeeply(got, td.HasPrefix("foo")) // returns true
Example ¶
package main import ( "fmt" "github.com/maxatome/go-testdeep/td" ) func main() { type MyStruct struct { Name string Num int Items []int } got := &MyStruct{ Name: "Foobar", Num: 12, Items: []int{4, 5, 9, 3, 8}, } if td.EqDeeply(got, td.Struct(&MyStruct{}, td.StructFields{ "Name": td.Re("^Foo"), "Num": td.Between(10, 20), "Items": td.ArrayEach(td.Between(3, 9)), })) { fmt.Println("Match!") } else { fmt.Println("NO!") } }
Output: Match!
func EqDeeplyError ¶
EqDeeplyError returns nil if got matches expected. expected can be the same type as got is, or contains some TestDeep operators. If got does not match expected, the returned *ctxerr.Error contains the reason of the first mismatch detected.
got := "foobar" if err := td.EqDeeplyError(got, "foobar"); err != nil { // … } if err := td.EqDeeplyError(got, td.HasPrefix("foo")); err != nil { // … }
Example ¶
package main import ( "fmt" "github.com/maxatome/go-testdeep/td" ) func main() { //line /testdeep/example.go:1 type MyStruct struct { Name string Num int Items []int } got := &MyStruct{ Name: "Foobar", Num: 12, Items: []int{4, 5, 9, 3, 8}, } err := td.EqDeeplyError(got, td.Struct(&MyStruct{}, td.StructFields{ "Name": td.Re("^Foo"), "Num": td.Between(10, 20), "Items": td.ArrayEach(td.Between(3, 8)), })) if err != nil { fmt.Println(err) } }
Output: DATA.Items[2]: values differ got: 9 expected: 3 ≤ got ≤ 8 [under operator Between at example.go:18]
func Flatten ¶ added in v1.5.0
Flatten allows to flatten any slice, array or map in parameters of operators expecting ...any. fn parameter allows to filter and/or transform items before flattening and is described below.
For example the Set operator is defined as:
func Set(expectedItems ...any) TestDeep
so when comparing to a []int slice, we usually do:
got := []int{42, 66, 22} td.Cmp(t, got, td.Set(22, 42, 66))
it works but if the expected items are already in a []int, we have to copy them in a []any as it can not be flattened directly in Set parameters:
expected := []int{22, 42, 66} expectedIf := make([]any, len(expected)) for i, item := range expected { expectedIf[i] = item } td.Cmp(t, got, td.Set(expectedIf...))
but it is a bit boring and less efficient, as Set does not keep the []any behind the scene.
The same with Flatten follows:
expected := []int{22, 42, 66} td.Cmp(t, got, td.Set(td.Flatten(expected)))
Several Flatten calls can be passed, and even combined with normal parameters:
expectedPart1 := []int{11, 22, 33} expectedPart2 := []int{55, 66, 77} expectedPart3 := []int{99} td.Cmp(t, got, td.Set( td.Flatten(expectedPart1), 44, td.Flatten(expectedPart2), 88, td.Flatten(expectedPart3), ))
is exactly the same as:
td.Cmp(t, got, td.Set(11, 22, 33, 44, 55, 66, 77, 88, 99))
Note that Flatten calls can even be nested:
td.Cmp(t, got, td.Set( td.Flatten([]any{ 11, td.Flatten([]int{22, 33}), td.Flatten([]int{44, 55, 66}), }), 77, ))
is exactly the same as:
td.Cmp(t, got, td.Set(11, 22, 33, 44, 55, 66, 77))
Maps can be flattened too, keeping in mind there is no particular order:
td.Flatten(map[int]int{1: 2, 3: 4})
is flattened as 1, 2, 3, 4 or 3, 4, 1, 2.
Optional fn parameter can be used to filter and/or transform items before flattening. If passed, it has to be one element length and this single element can be:
- untyped nil: it is a no-op, as if it was not passed
- a function
- a string shortcut
If it is a function, it must be a non-nil function with a signature like:
func(T) V func(T) (V, bool)
T can be the same as V, but it is not mandatory. The (V, bool) returned case allows to exclude some items when returning false.
If the function signature does not match these cases, Flatten panics.
If the type of an item of sliceOrMap is not convertible to T, the item is dropped silently, as if fn returned false.
This single element can also be a string among:
"Smuggle:FIELD" "JSONPointer:/PATH"
that are shortcuts for respectively:
func(in any) any { return td.Smuggle("FIELD", in) } func(in any) any { return td.JSONPointer("/PATH", in) }
See Smuggle and JSONPointer for a description of what "FIELD" and "/PATH" can really be.
Flatten with an fn can be useful when testing some fields of structs in a slice with Set or Bag operators families. As an example, here we test only "Name" field for each item of a person slice:
type person struct { Name string `json:"name"` Age int `json:"age"` } got := []person{{"alice", 22}, {"bob", 18}, {"brian", 34}, {"britt", 32}} td.Cmp(t, got, td.Bag(td.Flatten( []string{"alice", "britt", "brian", "bob"}, func(name string) any { return td.Smuggle("Name", name) }))) // distributes td.Smuggle for each Name, so is equivalent of: td.Cmp(t, got, td.Bag( td.Smuggle("Name", "alice"), td.Smuggle("Name", "britt"), td.Smuggle("Name", "brian"), td.Smuggle("Name", "bob"), )) // Same here using Smuggle string shortcut td.Cmp(t, got, td.Bag(td.Flatten( []string{"alice", "britt", "brian", "bob"}, "Smuggle:Name"))) // Same here, but using JSONPointer operator td.Cmp(t, got, td.Bag(td.Flatten( []string{"alice", "britt", "brian", "bob"}, func(name string) any { return td.JSONPointer("/name", name) }))) // Same here using JSONPointer string shortcut td.Cmp(t, got, td.Bag(td.Flatten( []string{"alice", "britt", "brian", "bob"}, "JSONPointer:/name"))) // Same here, but using SuperJSONOf operator td.Cmp(t, got, td.Bag(td.Flatten( []string{"alice", "britt", "brian", "bob"}, func(name string) any { return td.SuperJSONOf(`{"name":$1}`, name) }))) // Same here, but using Struct operator td.Cmp(t, got, td.Bag(td.Flatten( []string{"alice", "britt", "brian", "bob"}, func(name string) any { return td.Struct(person{Name: name}) })))
See also Grep.
func S ¶ added in v1.13.0
S returns a string based on args as Cmp* functions do with their own args parameter to name their test. So behind the scene, tdutil.BuildTestName is used.
If len(args) > 1 and the first item of args is a string and contains a '%' rune then fmt.Fprintf is used to compose the returned string, else args are passed to fmt.Fprint.
It can be used as a shorter fmt.Sprintf:
t.Run(fmt.Sprintf("Foo #%d", i), func(t *td.T) {}) t.Run(td.S("Foo #%d", i), func(t *td.T) {})
or to print any values as fmt.Sprint handles them:
a, ok := []int{1, 2, 3}, true t.Run(fmt.Sprint(a, ok), func(t *td.T) {}) t.Run(td.S(a, ok), func(t *td.T) {})
The only gain is less characters to type.
Types ¶
type ArrayEntries ¶
ArrayEntries allows to pass array or slice entries to check in functions Array, Slice and SuperSliceOf. It is a map whose each key is the item index and the corresponding value the expected item value (which can be a TestDeep operator as well as a zero value).
type BoundsKind ¶
type BoundsKind uint8
BoundsKind type qualifies the Between bounds.
const ( BoundsInIn BoundsKind // allows to match between "from" and "to" both included. BoundsInOut // allows to match between "from" included and "to" excluded. BoundsOutIn // allows to match between "from" excluded and "to" included. BoundsOutOut // allows to match between "from" and "to" both excluded. )
type ContextConfig ¶
type ContextConfig struct { // RootName is the string used to represent the root of got data. It // defaults to "DATA". For an HTTP response body, it could be "BODY" // for example. RootName string // MaxErrors is the maximal number of errors to dump in case of Cmp* // failure. // // It defaults to 10 except if the environment variable // TESTDEEP_MAX_ERRORS is set. In this latter case, the // TESTDEEP_MAX_ERRORS value is converted to an int and used as is. // // Setting it to 0 has the same effect as 1: only the first error // will be dumped without the "Too many errors" error. // // Setting it to a negative number means no limit: all errors // will be dumped. MaxErrors int // FailureIsFatal allows to Fatal() (instead of Error()) when a test // fails. Using *testing.T or *testing.B instance as t.TB value, FailNow() // is called behind the scenes when Fatal() is called. See testing // documentation for details. FailureIsFatal bool // UseEqual allows to use the Equal method on got (if it exists) or // on any of its component to compare got and expected values. // // The signature of the Equal method should be: // (A) Equal(B) bool // with B assignable to A. // // See time.Time as an example of accepted Equal() method. // // See (*T).UseEqual method to only apply this property to some // specific types. UseEqual bool // BeLax allows to compare different but convertible types. If set // to false (default), got and expected types must be the same. If // set to true and expected type is convertible to got one, expected // is first converted to go type before its comparison. See CmpLax // function/method and Lax operator to set this flag without // providing a specific configuration. BeLax bool // IgnoreUnexported allows to ignore unexported struct fields. Be // careful about structs entirely composed of unexported fields // (like time.Time for example). With this flag set to true, they // are all equal. In such case it is advised to set UseEqual flag, // to use (*T).UseEqual method or to add a Cmp hook using // (*T).WithCmpHooks method. // // See (*T).IgnoreUnexported method to only apply this property to some // specific types. IgnoreUnexported bool // TestDeepInGotOK allows to accept TestDeep operator in got Cmp* // parameter. By default it is forbidden and a panic occurs, because // most of the time it is a mistake to compare (expected, got) // instead of official (got, expected). TestDeepInGotOK bool // contains filtered or unexported fields }
ContextConfig allows to configure finely how tests failures are rendered.
See NewT function to use it.
func (ContextConfig) Equal ¶
func (c ContextConfig) Equal(o ContextConfig) bool
Equal returns true if both c and o are equal. Only public fields are taken into account to check equality.
func (ContextConfig) OriginalPath ¶ added in v1.12.0
func (c ContextConfig) OriginalPath() string
OriginalPath returns the current path when the ContextConfig has been built. It always returns ContextConfig.RootName except if c has been built by Code operator. See Code documentation for an example of use.
type MapEntries ¶
MapEntries allows to pass map entries to check in functions Map, SubMapOf and SuperMapOf. It is a map whose each key is the expected entry key and the corresponding value the expected entry value (which can be a TestDeep operator as well as a zero value.)
type RecvKind ¶ added in v1.13.0
A RecvKind allows to match that nothing has been received on a channel or that a channel has been closed when using Recv operator.
const ( RecvNothing RecvKind // nothing read on channel RecvClosed // channel closed )
type SmuggledGot ¶
SmuggledGot can be returned by a Smuggle function to name the transformed / returned value.
type StructFields ¶
StructFields allows to pass struct fields to check in functions Struct and SStruct. It is a map whose each key is the expected field name (or a regexp or a shell pattern matching a field name, see Struct & SStruct docs for details) and the corresponding value the expected field value (which can be a TestDeep operator as well as a zero value.)
type T ¶
type T struct { testing.TB Config ContextConfig // defaults to DefaultContextConfig }
T is a type that encapsulates testing.TB interface (which is implemented by *testing.T and *testing.B) allowing to easily use *testing.T methods as well as T ones.
func Assert ¶
func Assert(t testing.TB, config ...ContextConfig) *T
Assert returns a new *T instance with FailureIsFatal flag set to false.
assert := Assert(t)
is roughly equivalent to:
assert := NewT(t).FailureIsFatal(false)
See NewT documentation for usefulness of config optional parameter.
See also Require, AssertRequire and T.Assert.
func AssertRequire ¶
func AssertRequire(t testing.TB, config ...ContextConfig) (assert, require *T)
AssertRequire returns 2 instances of *T. assert with FailureIsFatal flag set to false, and require with FailureIsFatal flag set to true.
assert, require := AssertRequire(t)
is roughly equivalent to:
assert, require := Assert(t), Require(t)
See NewT documentation for usefulness of config optional parameter.
func NewT ¶
func NewT(t testing.TB, config ...ContextConfig) *T
NewT returns a new *T instance. Typically used as:
import ( "testing" "github.com/maxatome/go-testdeep/td" ) type Record struct { Id uint64 Name string Age int CreatedAt time.Time } func TestCreateRecord(tt *testing.T) { t := NewT(tt, ContextConfig{ MaxErrors: 3, // in case of failure, will dump up to 3 errors }) before := time.Now() record, err := CreateRecord() if t.CmpNoError(err) { t.Log("No error, can now check struct contents") ok := t.Struct(record, &Record{ Name: "Bob", Age: 23, }, td.StructFields{ "Id": td.NotZero(), "CreatedAt": td.Between(before, time.Now()), }, "Newly created record") if ok { t.Log(Record created successfully!") } } }
config is an optional parameter and, if passed, must be unique. It allows to configure how failures will be rendered during the lifetime of the returned instance.
t := NewT(tt) t.Cmp( Record{Age: 12, Name: "Bob", Id: 12}, // got Record{Age: 21, Name: "John", Id: 28}) // expected
will produce:
=== RUN TestFoobar --- FAIL: TestFoobar (0.00s) foobar_test.go:88: Failed test DATA.Id: values differ got: (uint64) 12 expected: (uint64) 28 DATA.Name: values differ got: "Bob" expected: "John" DATA.Age: values differ got: 12 expected: 28 FAIL
Now with a special configuration:
t := NewT(tt, ContextConfig{ RootName: "RECORD", // got data named "RECORD" instead of "DATA" MaxErrors: 2, // stops after 2 errors instead of default 10 }) t.Cmp( Record{Age: 12, Name: "Bob", Id: 12}, // got Record{Age: 21, Name: "John", Id: 28}, // expected )
will produce:
=== RUN TestFoobar --- FAIL: TestFoobar (0.00s) foobar_test.go:96: Failed test RECORD.Id: values differ got: (uint64) 12 expected: (uint64) 28 RECORD.Name: values differ got: "Bob" expected: "John" Too many errors (use TESTDEEP_MAX_ERRORS=-1 to see all) FAIL
See T.RootName method to configure RootName in a more specific fashion.
Note that setting MaxErrors to a negative value produces a dump with all errors.
If MaxErrors is not set (or set to 0), it is set to DefaultContextConfig.MaxErrors which is potentially dependent from the TESTDEEP_MAX_ERRORS environment variable (else defaults to 10.) See ContextConfig documentation for details.
Of course t can already be a *T, in this special case if config is omitted, the Config of the new instance is a copy of the t Config, including hooks.
func Require ¶
func Require(t testing.TB, config ...ContextConfig) *T
Require returns a new *T instance with FailureIsFatal flag set to true.
require := Require(t)
is roughly equivalent to:
require := NewT(t).FailureIsFatal(true)
See NewT documentation for usefulness of config optional parameter.
See also Assert, AssertRequire and T.Require.
func (*T) A ¶
A is a synonym for T.Anchor.
import ( "testing" "github.com/maxatome/go-testdeep/td" ) func TestFunc(tt *testing.T) { got := Func() t := td.NewT(tt) t.Cmp(got, &MyStruct{ Name: "Bob", Details: &MyDetails{ Nick: t.A(td.HasPrefix("Bobby"), "").(string), Age: t.A(td.Between(40, 50)).(int), }, }) }
See also T.AnchorsPersistTemporarily, T.DoAnchorsPersist, T.ResetAnchors, T.SetAnchorsPersist and AddAnchorableStructType.
func (*T) All ¶
All is a shortcut for:
t.Cmp(got, td.All(expectedValues...), args...)
See All for details.
Returns true if the test is OK, false if it fails.
args... are optional and allow to name the test. This name is used in case of failure to qualify the test. If len(args) > 1 and the first item of args is a string and contains a '%' rune then fmt.Fprintf is used to compose the name, else args are passed to fmt.Fprint. Do not forget it is the name of the test, not the reason of a potential failure.
Example ¶
package main import ( "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := td.NewT(&testing.T{}) got := "foo/bar" // Checks got string against: // "o/b" regexp *AND* "bar" suffix *AND* exact "foo/bar" string ok := t.All(got, []any{td.Re("o/b"), td.HasSuffix("bar"), "foo/bar"}, "checks value %s", got) fmt.Println(ok) // Checks got string against: // "o/b" regexp *AND* "bar" suffix *AND* exact "fooX/Ybar" string ok = t.All(got, []any{td.Re("o/b"), td.HasSuffix("bar"), "fooX/Ybar"}, "checks value %s", got) fmt.Println(ok) // When some operators or values have to be reused and mixed between // several calls, Flatten can be used to avoid boring and // inefficient []any copies: regOps := td.Flatten([]td.TestDeep{td.Re("o/b"), td.Re(`^fo`), td.Re(`ar$`)}) ok = t.All(got, []any{td.HasPrefix("foo"), regOps, td.HasSuffix("bar")}, "checks all operators against value %s", got) fmt.Println(ok) }
Output: true false true
func (*T) Anchor ¶
Anchor returns a typed value allowing to anchor the TestDeep operator operator in a go classic literal like a struct, slice, array or map value.
If the TypeBehind method of operator returns non-nil, model can be omitted (like with Between operator in the example below). Otherwise, model should contain only one value corresponding to the returning type. It can be:
- a go value: returning type is the type of the value, whatever the value is;
- a reflect.Type.
It returns a typed value ready to be embed in a go data structure to be compared using T.Cmp or T.CmpLax:
import ( "testing" "github.com/maxatome/go-testdeep/td" ) func TestFunc(tt *testing.T) { got := Func() t := td.NewT(tt) t.Cmp(got, &MyStruct{ Name: "Bob", Details: &MyDetails{ Nick: t.Anchor(td.HasPrefix("Bobby"), "").(string), Age: t.Anchor(td.Between(40, 50)).(int), }, }) }
In this example:
- HasPrefix operates on several input types (string, fmt.Stringer, error, …), so its TypeBehind method returns always nil as it can not guess in advance on which type it operates. In this case, we must pass "" as model parameter in order to tell it to return the string type. Note that the .(string) type assertion is then mandatory to conform to the strict type checking.
- Between, on its side, knows the type on which it operates, as it is the same as the one of its parameters. So its TypeBehind method returns the right type, and so no need to pass it as model parameter. Note that the .(int) type assertion is still mandatory to conform to the strict type checking.
Without operator anchoring feature, the previous example would have been:
import ( "testing" "github.com/maxatome/go-testdeep/td" ) func TestFunc(tt *testing.T) { got := Func() t := td.NewT(tt) t.Cmp(got, td.Struct(&MyStruct{Name: "Bob"}, td.StructFields{ "Details": td.Struct(&MyDetails{}, td.StructFields{ "Nick": td.HasPrefix("Bobby"), "Age": td.Between(40, 50), }), })) }
using two times the Struct operator to work around the strict type checking of golang.
By default, the value returned by Anchor can only be used in the next T.Cmp or T.CmpLax call. To make it persistent across calls, see T.SetAnchorsPersist and T.AnchorsPersistTemporarily methods.
See T.A method for a shorter synonym of Anchor.
See also T.AnchorsPersistTemporarily, T.DoAnchorsPersist, T.ResetAnchors, T.SetAnchorsPersist and AddAnchorableStructType.
func (*T) AnchorsPersistTemporarily ¶
func (t *T) AnchorsPersistTemporarily() func()
AnchorsPersistTemporarily is used by helpers to temporarily enable anchors persistence. See tdhttp package for an example of use. It returns a function to be deferred, to restore the normal behavior (clear anchored operators if persistence was false, do nothing otherwise).
Typically used as:
defer t.AnchorsPersistTemporarily()() // or t.Cleanup(t.AnchorsPersistTemporarily())
See also T.Anchor, T.DoAnchorsPersist, T.ResetAnchors, T.SetAnchorsPersist and AddAnchorableStructType.
func (*T) Any ¶
Any is a shortcut for:
t.Cmp(got, td.Any(expectedValues...), args...)
See Any for details.
Returns true if the test is OK, false if it fails.
args... are optional and allow to name the test. This name is used in case of failure to qualify the test. If len(args) > 1 and the first item of args is a string and contains a '%' rune then fmt.Fprintf is used to compose the name, else args are passed to fmt.Fprint. Do not forget it is the name of the test, not the reason of a potential failure.
Example ¶
package main import ( "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := td.NewT(&testing.T{}) got := "foo/bar" // Checks got string against: // "zip" regexp *OR* "bar" suffix ok := t.Any(got, []any{td.Re("zip"), td.HasSuffix("bar")}, "checks value %s", got) fmt.Println(ok) // Checks got string against: // "zip" regexp *OR* "foo" suffix ok = t.Any(got, []any{td.Re("zip"), td.HasSuffix("foo")}, "checks value %s", got) fmt.Println(ok) // When some operators or values have to be reused and mixed between // several calls, Flatten can be used to avoid boring and // inefficient []any copies: regOps := td.Flatten([]td.TestDeep{td.Re("a/c"), td.Re(`^xx`), td.Re(`ar$`)}) ok = t.Any(got, []any{td.HasPrefix("xxx"), regOps, td.HasSuffix("zip")}, "check at least one operator matches value %s", got) fmt.Println(ok) }
Output: true false true
func (*T) Array ¶
func (t *T) Array(got, model any, expectedEntries ArrayEntries, args ...any) bool
Array is a shortcut for:
t.Cmp(got, td.Array(model, expectedEntries), args...)
See Array for details.
Returns true if the test is OK, false if it fails.
args... are optional and allow to name the test. This name is used in case of failure to qualify the test. If len(args) > 1 and the first item of args is a string and contains a '%' rune then fmt.Fprintf is used to compose the name, else args are passed to fmt.Fprint. Do not forget it is the name of the test, not the reason of a potential failure.
Example (Array) ¶
package main import ( "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := td.NewT(&testing.T{}) got := [3]int{42, 58, 26} ok := t.Array(got, [3]int{42}, td.ArrayEntries{1: 58, 2: td.Ignore()}, "checks array %v", got) fmt.Println("Simple array:", ok) ok = t.Array(&got, &[3]int{42}, td.ArrayEntries{1: 58, 2: td.Ignore()}, "checks array %v", got) fmt.Println("Array pointer:", ok) ok = t.Array(&got, (*[3]int)(nil), td.ArrayEntries{0: 42, 1: 58, 2: td.Ignore()}, "checks array %v", got) fmt.Println("Array pointer, nil model:", ok) }
Output: Simple array: true Array pointer: true Array pointer, nil model: true
Example (TypedArray) ¶
package main import ( "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := td.NewT(&testing.T{}) type MyArray [3]int got := MyArray{42, 58, 26} ok := t.Array(got, MyArray{42}, td.ArrayEntries{1: 58, 2: td.Ignore()}, "checks typed array %v", got) fmt.Println("Typed array:", ok) ok = t.Array(&got, &MyArray{42}, td.ArrayEntries{1: 58, 2: td.Ignore()}, "checks pointer on typed array %v", got) fmt.Println("Pointer on a typed array:", ok) ok = t.Array(&got, &MyArray{}, td.ArrayEntries{0: 42, 1: 58, 2: td.Ignore()}, "checks pointer on typed array %v", got) fmt.Println("Pointer on a typed array, empty model:", ok) ok = t.Array(&got, (*MyArray)(nil), td.ArrayEntries{0: 42, 1: 58, 2: td.Ignore()}, "checks pointer on typed array %v", got) fmt.Println("Pointer on a typed array, nil model:", ok) }
Output: Typed array: true Pointer on a typed array: true Pointer on a typed array, empty model: true Pointer on a typed array, nil model: true
func (*T) ArrayEach ¶
ArrayEach is a shortcut for:
t.Cmp(got, td.ArrayEach(expectedValue), args...)
See ArrayEach for details.
Returns true if the test is OK, false if it fails.
args... are optional and allow to name the test. This name is used in case of failure to qualify the test. If len(args) > 1 and the first item of args is a string and contains a '%' rune then fmt.Fprintf is used to compose the name, else args are passed to fmt.Fprint. Do not forget it is the name of the test, not the reason of a potential failure.
Example (Array) ¶
package main import ( "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := td.NewT(&testing.T{}) got := [3]int{42, 58, 26} ok := t.ArrayEach(got, td.Between(25, 60), "checks each item of array %v is in [25 .. 60]", got) fmt.Println(ok) }
Output: true
Example (Slice) ¶
package main import ( "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := td.NewT(&testing.T{}) got := []int{42, 58, 26} ok := t.ArrayEach(got, td.Between(25, 60), "checks each item of slice %v is in [25 .. 60]", got) fmt.Println(ok) }
Output: true
Example (TypedArray) ¶
package main import ( "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := td.NewT(&testing.T{}) type MyArray [3]int got := MyArray{42, 58, 26} ok := t.ArrayEach(got, td.Between(25, 60), "checks each item of typed array %v is in [25 .. 60]", got) fmt.Println(ok) ok = t.ArrayEach(&got, td.Between(25, 60), "checks each item of typed array pointer %v is in [25 .. 60]", got) fmt.Println(ok) }
Output: true true
Example (TypedSlice) ¶
package main import ( "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := td.NewT(&testing.T{}) type MySlice []int got := MySlice{42, 58, 26} ok := t.ArrayEach(got, td.Between(25, 60), "checks each item of typed slice %v is in [25 .. 60]", got) fmt.Println(ok) ok = t.ArrayEach(&got, td.Between(25, 60), "checks each item of typed slice pointer %v is in [25 .. 60]", got) fmt.Println(ok) }
Output: true true
func (*T) Assert ¶ added in v1.13.0
Assert returns a new *T instance inheriting the t config but with FailureIsFatal flag set to false.
It returns a new instance of *T so does not alter the original t
It is a shortcut for:
t.FailureIsFatal(false)
See also T.FailureIsFatal and T.Require.
func (*T) Bag ¶
Bag is a shortcut for:
t.Cmp(got, td.Bag(expectedItems...), args...)
See Bag for details.
Returns true if the test is OK, false if it fails.
args... are optional and allow to name the test. This name is used in case of failure to qualify the test. If len(args) > 1 and the first item of args is a string and contains a '%' rune then fmt.Fprintf is used to compose the name, else args are passed to fmt.Fprint. Do not forget it is the name of the test, not the reason of a potential failure.
Example ¶
package main import ( "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := td.NewT(&testing.T{}) got := []int{1, 3, 5, 8, 8, 1, 2} // Matches as all items are present ok := t.Bag(got, []any{1, 1, 2, 3, 5, 8, 8}, "checks all items are present, in any order") fmt.Println(ok) // Does not match as got contains 2 times 1 and 8, and these // duplicates are not expected ok = t.Bag(got, []any{1, 2, 3, 5, 8}, "checks all items are present, in any order") fmt.Println(ok) got = []int{1, 3, 5, 8, 2} // Duplicates of 1 and 8 are expected but not present in got ok = t.Bag(got, []any{1, 1, 2, 3, 5, 8, 8}, "checks all items are present, in any order") fmt.Println(ok) // Matches as all items are present ok = t.Bag(got, []any{1, 2, 3, 5, td.Gt(7)}, "checks all items are present, in any order") fmt.Println(ok) // When expected is already a non-[]any slice, it cannot be // flattened directly using expected... without copying it to a new // []any slice, then use td.Flatten! expected := []int{1, 2, 3, 5} ok = t.Bag(got, []any{td.Flatten(expected), td.Gt(7)}, "checks all expected items are present, in any order") fmt.Println(ok) }
Output: true false false true true
func (*T) BeLax ¶
BeLax allows to compare different but convertible types. If set to false, got and expected types must be the same. If set to true and expected type is convertible to got one, expected is first converted to go type before its comparison. See CmpLax or T.CmpLax and Lax operator to set this flag without providing a specific configuration.
It returns a new instance of *T so does not alter the original t.
Note that t.BeLax() acts as t.BeLax(true).
func (*T) Between ¶
func (t *T) Between(got, from, to any, bounds BoundsKind, args ...any) bool
Between is a shortcut for:
t.Cmp(got, td.Between(from, to, bounds), args...)
See Between for details.
Between optional parameter bounds is here mandatory. BoundsInIn value should be passed to mimic its absence in original Between call.
Returns true if the test is OK, false if it fails.
args... are optional and allow to name the test. This name is used in case of failure to qualify the test. If len(args) > 1 and the first item of args is a string and contains a '%' rune then fmt.Fprintf is used to compose the name, else args are passed to fmt.Fprint. Do not forget it is the name of the test, not the reason of a potential failure.
Example (Int) ¶
package main import ( "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := td.NewT(&testing.T{}) got := 156 ok := t.Between(got, 154, 156, td.BoundsInIn, "checks %v is in [154 .. 156]", got) fmt.Println(ok) // BoundsInIn is implicit ok = t.Between(got, 154, 156, td.BoundsInIn, "checks %v is in [154 .. 156]", got) fmt.Println(ok) ok = t.Between(got, 154, 156, td.BoundsInOut, "checks %v is in [154 .. 156[", got) fmt.Println(ok) ok = t.Between(got, 154, 156, td.BoundsOutIn, "checks %v is in ]154 .. 156]", got) fmt.Println(ok) ok = t.Between(got, 154, 156, td.BoundsOutOut, "checks %v is in ]154 .. 156[", got) fmt.Println(ok) }
Output: true true false true false
Example (String) ¶
package main import ( "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := td.NewT(&testing.T{}) got := "abc" ok := t.Between(got, "aaa", "abc", td.BoundsInIn, `checks "%v" is in ["aaa" .. "abc"]`, got) fmt.Println(ok) // BoundsInIn is implicit ok = t.Between(got, "aaa", "abc", td.BoundsInIn, `checks "%v" is in ["aaa" .. "abc"]`, got) fmt.Println(ok) ok = t.Between(got, "aaa", "abc", td.BoundsInOut, `checks "%v" is in ["aaa" .. "abc"[`, got) fmt.Println(ok) ok = t.Between(got, "aaa", "abc", td.BoundsOutIn, `checks "%v" is in ]"aaa" .. "abc"]`, got) fmt.Println(ok) ok = t.Between(got, "aaa", "abc", td.BoundsOutOut, `checks "%v" is in ]"aaa" .. "abc"[`, got) fmt.Println(ok) }
Output: true true false true false
Example (Time) ¶
package main import ( "fmt" "testing" "time" "github.com/maxatome/go-testdeep/td" ) func main() { t := td.NewT(&testing.T{}) before := time.Now() occurredAt := time.Now() after := time.Now() ok := t.Between(occurredAt, before, after, td.BoundsInIn) fmt.Println("It occurred between before and after:", ok) type MyTime time.Time ok = t.Between(MyTime(occurredAt), MyTime(before), MyTime(after), td.BoundsInIn) fmt.Println("Same for convertible MyTime type:", ok) ok = t.Between(MyTime(occurredAt), before, after, td.BoundsInIn) fmt.Println("MyTime vs time.Time:", ok) ok = t.Between(occurredAt, before, 10*time.Second, td.BoundsInIn) fmt.Println("Using a time.Duration as TO:", ok) ok = t.Between(MyTime(occurredAt), MyTime(before), 10*time.Second, td.BoundsInIn) fmt.Println("Using MyTime as FROM and time.Duration as TO:", ok) }
Output: It occurred between before and after: true Same for convertible MyTime type: true MyTime vs time.Time: false Using a time.Duration as TO: true Using MyTime as FROM and time.Duration as TO: true
func (*T) Cap ¶
Cap is a shortcut for:
t.Cmp(got, td.Cap(expectedCap), args...)
See Cap for details.
Returns true if the test is OK, false if it fails.
args... are optional and allow to name the test. This name is used in case of failure to qualify the test. If len(args) > 1 and the first item of args is a string and contains a '%' rune then fmt.Fprintf is used to compose the name, else args are passed to fmt.Fprint. Do not forget it is the name of the test, not the reason of a potential failure.
Example ¶
package main import ( "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := td.NewT(&testing.T{}) got := make([]int, 0, 12) ok := t.Cap(got, 12, "checks %v capacity is 12", got) fmt.Println(ok) ok = t.Cap(got, 0, "checks %v capacity is 0", got) fmt.Println(ok) got = nil ok = t.Cap(got, 0, "checks %v capacity is 0", got) fmt.Println(ok) }
Output: true false true
Example (Operator) ¶
package main import ( "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := td.NewT(&testing.T{}) got := make([]int, 0, 12) ok := t.Cap(got, td.Between(10, 12), "checks %v capacity is in [10 .. 12]", got) fmt.Println(ok) ok = t.Cap(got, td.Gt(10), "checks %v capacity is in [10 .. 12]", got) fmt.Println(ok) }
Output: true true
func (*T) Cmp ¶
Cmp is mostly a shortcut for:
Cmp(t.TB, got, expected, args...)
with the exception that t.Config is used to configure the test ContextConfig.
args... are optional and allow to name the test. This name is used in case of failure to qualify the test. If len(args) > 1 and the first item of args is a string and contains a '%' rune then fmt.Fprintf is used to compose the name, else args are passed to fmt.Fprint. Do not forget it is the name of the test, not the reason of a potential failure.
func (*T) CmpDeeply ¶
CmpDeeply works the same as Cmp and is still available for compatibility purpose. Use shorter Cmp in new code.
func (*T) CmpError ¶
CmpError checks that got is non-nil error.
_, err := MyFunction(1, 2, 3) t.CmpError(err, "MyFunction(1, 2, 3) should return an error")
CmpError and not Error to avoid collision with t.TB.Error method.
args... are optional and allow to name the test. This name is used in case of failure to qualify the test. If len(args) > 1 and the first item of args is a string and contains a '%' rune then fmt.Fprintf is used to compose the name, else args are passed to fmt.Fprint. Do not forget it is the name of the test, not the reason of a potential failure.
See also T.CmpNoError.
Example ¶
package main import ( "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := td.NewT(&testing.T{}) got := fmt.Errorf("Error #%d", 42) ok := t.CmpError(got, "An error occurred") fmt.Println(ok) got = nil ok = t.CmpError(got, "An error occurred") // fails fmt.Println(ok) }
Output: true false
func (*T) CmpErrorIs ¶ added in v1.13.0
CmpErrorIs is a shortcut for:
t.Cmp(got, td.ErrorIs(expected), args...)
See ErrorIs for details.
Returns true if the test is OK, false if it fails.
args... are optional and allow to name the test. This name is used in case of failure to qualify the test. If len(args) > 1 and the first item of args is a string and contains a '%' rune then fmt.Fprintf is used to compose the name, else args are passed to fmt.Fprint. Do not forget it is the name of the test, not the reason of a potential failure.
Example ¶
package main import ( "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := td.NewT(&testing.T{}) err1 := fmt.Errorf("failure1") err2 := fmt.Errorf("failure2: %w", err1) err3 := fmt.Errorf("failure3: %w", err2) err := fmt.Errorf("failure4: %w", err3) ok := t.CmpErrorIs(err, err) fmt.Println("error is itself:", ok) ok = t.CmpErrorIs(err, err1) fmt.Println("error is also err1:", ok) ok = t.CmpErrorIs(err1, err) fmt.Println("err1 is err:", ok) }
Output: error is itself: true error is also err1: true err1 is err: false
func (*T) CmpLax ¶
CmpLax is a shortcut for:
t.Cmp(got, td.Lax(expectedValue), args...)
See Lax for details.
Returns true if the test is OK, false if it fails.
args... are optional and allow to name the test. This name is used in case of failure to qualify the test. If len(args) > 1 and the first item of args is a string and contains a '%' rune then fmt.Fprintf is used to compose the name, else args are passed to fmt.Fprint. Do not forget it is the name of the test, not the reason of a potential failure.
Example ¶
package main import ( "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := td.NewT(&testing.T{}) gotInt64 := int64(1234) gotInt32 := int32(1235) type myInt uint16 gotMyInt := myInt(1236) expected := td.Between(1230, 1240) // int type here ok := t.CmpLax(gotInt64, expected) fmt.Println("int64 got between ints [1230 .. 1240]:", ok) ok = t.CmpLax(gotInt32, expected) fmt.Println("int32 got between ints [1230 .. 1240]:", ok) ok = t.CmpLax(gotMyInt, expected) fmt.Println("myInt got between ints [1230 .. 1240]:", ok) }
Output: int64 got between ints [1230 .. 1240]: true int32 got between ints [1230 .. 1240]: true myInt got between ints [1230 .. 1240]: true
func (*T) CmpNoError ¶
CmpNoError checks that got is nil error.
value, err := MyFunction(1, 2, 3) if t.CmpNoError(err) { // one can now check value... }
CmpNoError and not NoError to be consistent with T.CmpError method.
args... are optional and allow to name the test. This name is used in case of failure to qualify the test. If len(args) > 1 and the first item of args is a string and contains a '%' rune then fmt.Fprintf is used to compose the name, else args are passed to fmt.Fprint. Do not forget it is the name of the test, not the reason of a potential failure.
See also T.CmpError.
Example ¶
package main import ( "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := td.NewT(&testing.T{}) got := fmt.Errorf("Error #%d", 42) ok := t.CmpNoError(got, "An error occurred") // fails fmt.Println(ok) got = nil ok = t.CmpNoError(got, "An error occurred") fmt.Println(ok) }
Output: false true
func (*T) CmpNotPanic ¶
CmpNotPanic calls fn and checks no panic() occurred. If a panic() occurred false is returned then the panic() parameter and the stack trace appear in the test report.
Note that calling panic(nil) in fn body is detected as a panic.
t.CmpNotPanic(func() {}) // succeeds as function does not panic t.CmpNotPanic(func() { panic("I am panicking!") }) // fails t.CmpNotPanic(func() { panic(nil) }) // fails too
args... are optional and allow to name the test. This name is used in case of failure to qualify the test. If len(args) > 1 and the first item of args is a string and contains a '%' rune then fmt.Fprintf is used to compose the name, else args are passed to fmt.Fprint. Do not forget it is the name of the test, not the reason of a potential failure.
See also T.CmpPanic.
Example ¶
package main import ( "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := td.NewT(&testing.T{}) ok := t.CmpNotPanic(func() {}, nil) fmt.Println("checks a panic DID NOT occur:", ok) // Classic panic ok = t.CmpNotPanic(func() { panic("I am panicking!") }, "Hope it does not panic!") fmt.Println("still no panic?", ok) // Can detect panic(nil) ok = t.CmpNotPanic(func() { panic(nil) }, "Checks for panic(nil)") fmt.Println("last no panic?", ok) }
Output: checks a panic DID NOT occur: true still no panic? false last no panic? false
func (*T) CmpPanic ¶
CmpPanic calls fn and checks a panic() occurred with the expectedPanic parameter. It returns true only if both conditions are fulfilled.
Note that calling panic(nil) in fn body is detected as a panic (in this case expectedPanic has to be nil).
t.CmpPanic(func() { panic("I am panicking!") }, "I am panicking!", "The function should panic with the right string") t.CmpPanic(func() { panic("I am panicking!") }, Contains("panicking!"), "The function should panic with a string containing `panicking!`") t.CmpPanic(t, func() { panic(nil) }, nil, "Checks for panic(nil)")
args... are optional and allow to name the test. This name is used in case of failure to qualify the test. If len(args) > 1 and the first item of args is a string and contains a '%' rune then fmt.Fprintf is used to compose the name, else args are passed to fmt.Fprint. Do not forget it is the name of the test, not the reason of a potential failure.
See also T.CmpNotPanic.
Example ¶
package main import ( "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := td.NewT(&testing.T{}) ok := t.CmpPanic(func() { panic("I am panicking!") }, "I am panicking!", "Checks for panic") fmt.Println("checks exact panic() string:", ok) // Can use TestDeep operator too ok = t.CmpPanic( func() { panic("I am panicking!") }, td.Contains("panicking!"), "Checks for panic") fmt.Println("checks panic() sub-string:", ok) // Can detect panic(nil) ok = t.CmpPanic(func() { panic(nil) }, nil, "Checks for panic(nil)") fmt.Println("checks for panic(nil):", ok) // As well as structured data panic type PanicStruct struct { Error string Code int } ok = t.CmpPanic( func() { panic(PanicStruct{Error: "Memory violation", Code: 11}) }, PanicStruct{ Error: "Memory violation", Code: 11, }) fmt.Println("checks exact panic() struct:", ok) // or combined with TestDeep operators too ok = t.CmpPanic( func() { panic(PanicStruct{Error: "Memory violation", Code: 11}) }, td.Struct(PanicStruct{}, td.StructFields{ "Code": td.Between(10, 20), })) fmt.Println("checks panic() struct against TestDeep operators:", ok) // Of course, do not panic = test failure, even for expected nil // panic parameter ok = t.CmpPanic(func() {}, nil) fmt.Println("checks a panic occurred:", ok) }
Output: checks exact panic() string: true checks panic() sub-string: true checks for panic(nil): true checks exact panic() struct: true checks panic() struct against TestDeep operators: true checks a panic occurred: false
func (*T) Code ¶
Code is a shortcut for:
t.Cmp(got, td.Code(fn), args...)
See Code for details.
Returns true if the test is OK, false if it fails.
args... are optional and allow to name the test. This name is used in case of failure to qualify the test. If len(args) > 1 and the first item of args is a string and contains a '%' rune then fmt.Fprintf is used to compose the name, else args are passed to fmt.Fprint. Do not forget it is the name of the test, not the reason of a potential failure.
Example ¶
package main import ( "fmt" "strconv" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := td.NewT(&testing.T{}) got := "12" ok := t.Code(got, func(num string) bool { n, err := strconv.Atoi(num) return err == nil && n > 10 && n < 100 }, "checks string `%s` contains a number and this number is in ]10 .. 100[", got) fmt.Println(ok) // Same with failure reason ok = t.Code(got, func(num string) (bool, string) { n, err := strconv.Atoi(num) if err != nil { return false, "not a number" } if n > 10 && n < 100 { return true, "" } return false, "not in ]10 .. 100[" }, "checks string `%s` contains a number and this number is in ]10 .. 100[", got) fmt.Println(ok) // Same with failure reason thanks to error ok = t.Code(got, func(num string) error { n, err := strconv.Atoi(num) if err != nil { return err } if n > 10 && n < 100 { return nil } return fmt.Errorf("%d not in ]10 .. 100[", n) }, "checks string `%s` contains a number and this number is in ]10 .. 100[", got) fmt.Println(ok) }
Output: true true true
Example (Custom) ¶
package main import ( "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := td.NewT(&testing.T{}) got := 123 ok := t.Code(got, func(t *td.T, num int) { t.Cmp(num, 123) }) fmt.Println("with one *td.T:", ok) ok = t.Code(got, func(assert, require *td.T, num int) { assert.Cmp(num, 123) require.Cmp(num, 123) }) fmt.Println("with assert & require *td.T:", ok) }
Output: with one *td.T: true with assert & require *td.T: true
func (*T) Contains ¶
Contains is a shortcut for:
t.Cmp(got, td.Contains(expectedValue), args...)
See Contains for details.
Returns true if the test is OK, false if it fails.
args... are optional and allow to name the test. This name is used in case of failure to qualify the test. If len(args) > 1 and the first item of args is a string and contains a '%' rune then fmt.Fprintf is used to compose the name, else args are passed to fmt.Fprint. Do not forget it is the name of the test, not the reason of a potential failure.
Example (ArraySlice) ¶
package main import ( "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := td.NewT(&testing.T{}) ok := t.Contains([...]int{11, 22, 33, 44}, 22) fmt.Println("array contains 22:", ok) ok = t.Contains([...]int{11, 22, 33, 44}, td.Between(20, 25)) fmt.Println("array contains at least one item in [20 .. 25]:", ok) ok = t.Contains([]int{11, 22, 33, 44}, 22) fmt.Println("slice contains 22:", ok) ok = t.Contains([]int{11, 22, 33, 44}, td.Between(20, 25)) fmt.Println("slice contains at least one item in [20 .. 25]:", ok) ok = t.Contains([]int{11, 22, 33, 44}, []int{22, 33}) fmt.Println("slice contains the sub-slice [22, 33]:", ok) }
Output: array contains 22: true array contains at least one item in [20 .. 25]: true slice contains 22: true slice contains at least one item in [20 .. 25]: true slice contains the sub-slice [22, 33]: true
Example (Error) ¶
package main import ( "errors" "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := td.NewT(&testing.T{}) got := errors.New("foobar") ok := t.Contains(got, "oob", "checks %s", got) fmt.Println("contains `oob` string:", ok) ok = t.Contains(got, 'b', "checks %s", got) fmt.Println("contains 'b' rune:", ok) ok = t.Contains(got, byte('a'), "checks %s", got) fmt.Println("contains 'a' byte:", ok) ok = t.Contains(got, td.Between('n', 'p'), "checks %s", got) fmt.Println("contains at least one character ['n' .. 'p']:", ok) }
Output: contains `oob` string: true contains 'b' rune: true contains 'a' byte: true contains at least one character ['n' .. 'p']: true
Example (Map) ¶
package main import ( "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := td.NewT(&testing.T{}) ok := t.Contains(map[string]int{"foo": 11, "bar": 22, "zip": 33}, 22) fmt.Println("map contains value 22:", ok) ok = t.Contains(map[string]int{"foo": 11, "bar": 22, "zip": 33}, td.Between(20, 25)) fmt.Println("map contains at least one value in [20 .. 25]:", ok) }
Output: map contains value 22: true map contains at least one value in [20 .. 25]: true
Example (Nil) ¶
package main import ( "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := td.NewT(&testing.T{}) num := 123 got := [...]*int{&num, nil} ok := t.Contains(got, nil) fmt.Println("array contains untyped nil:", ok) ok = t.Contains(got, (*int)(nil)) fmt.Println("array contains *int nil:", ok) ok = t.Contains(got, td.Nil()) fmt.Println("array contains Nil():", ok) ok = t.Contains(got, (*byte)(nil)) fmt.Println("array contains *byte nil:", ok) // types differ: *byte ≠ *int }
Output: array contains untyped nil: true array contains *int nil: true array contains Nil(): true array contains *byte nil: false
Example (String) ¶
package main import ( "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := td.NewT(&testing.T{}) got := "foobar" ok := t.Contains(got, "oob", "checks %s", got) fmt.Println("contains `oob` string:", ok) ok = t.Contains(got, []byte("oob"), "checks %s", got) fmt.Println("contains `oob` []byte:", ok) ok = t.Contains(got, 'b', "checks %s", got) fmt.Println("contains 'b' rune:", ok) ok = t.Contains(got, byte('a'), "checks %s", got) fmt.Println("contains 'a' byte:", ok) ok = t.Contains(got, td.Between('n', 'p'), "checks %s", got) fmt.Println("contains at least one character ['n' .. 'p']:", ok) }
Output: contains `oob` string: true contains `oob` []byte: true contains 'b' rune: true contains 'a' byte: true contains at least one character ['n' .. 'p']: true
Example (Stringer) ¶
package main import ( "bytes" "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := td.NewT(&testing.T{}) // bytes.Buffer implements fmt.Stringer got := bytes.NewBufferString("foobar") ok := t.Contains(got, "oob", "checks %s", got) fmt.Println("contains `oob` string:", ok) ok = t.Contains(got, 'b', "checks %s", got) fmt.Println("contains 'b' rune:", ok) ok = t.Contains(got, byte('a'), "checks %s", got) fmt.Println("contains 'a' byte:", ok) ok = t.Contains(got, td.Between('n', 'p'), "checks %s", got) fmt.Println("contains at least one character ['n' .. 'p']:", ok) }
Output: contains `oob` string: true contains 'b' rune: true contains 'a' byte: true contains at least one character ['n' .. 'p']: true
func (*T) ContainsKey ¶
ContainsKey is a shortcut for:
t.Cmp(got, td.ContainsKey(expectedValue), args...)
See ContainsKey for details.
Returns true if the test is OK, false if it fails.
args... are optional and allow to name the test. This name is used in case of failure to qualify the test. If len(args) > 1 and the first item of args is a string and contains a '%' rune then fmt.Fprintf is used to compose the name, else args are passed to fmt.Fprint. Do not forget it is the name of the test, not the reason of a potential failure.
Example ¶
package main import ( "fmt" "strings" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := td.NewT(&testing.T{}) ok := t.ContainsKey(map[string]int{"foo": 11, "bar": 22, "zip": 33}, "foo") fmt.Println(`map contains key "foo":`, ok) ok = t.ContainsKey(map[int]bool{12: true, 24: false, 42: true, 51: false}, td.Between(40, 50)) fmt.Println("map contains at least a key in [40 .. 50]:", ok) ok = t.ContainsKey(map[string]int{"FOO": 11, "bar": 22, "zip": 33}, td.Smuggle(strings.ToLower, "foo")) fmt.Println(`map contains key "foo" without taking case into account:`, ok) }
Output: map contains key "foo": true map contains at least a key in [40 .. 50]: true map contains key "foo" without taking case into account: true
Example (Nil) ¶
package main import ( "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := td.NewT(&testing.T{}) num := 1234 got := map[*int]bool{&num: false, nil: true} ok := t.ContainsKey(got, nil) fmt.Println("map contains untyped nil key:", ok) ok = t.ContainsKey(got, (*int)(nil)) fmt.Println("map contains *int nil key:", ok) ok = t.ContainsKey(got, td.Nil()) fmt.Println("map contains Nil() key:", ok) ok = t.ContainsKey(got, (*byte)(nil)) fmt.Println("map contains *byte nil key:", ok) // types differ: *byte ≠ *int }
Output: map contains untyped nil key: true map contains *int nil key: true map contains Nil() key: true map contains *byte nil key: false
func (*T) DoAnchorsPersist ¶
DoAnchorsPersist returns true if anchors persistence is enabled, false otherwise.
See also T.Anchor, T.AnchorsPersistTemporarily, T.ResetAnchors, T.SetAnchorsPersist and AddAnchorableStructType.
func (*T) Empty ¶
Empty is a shortcut for:
t.Cmp(got, td.Empty(), args...)
See Empty for details.
Returns true if the test is OK, false if it fails.
args... are optional and allow to name the test. This name is used in case of failure to qualify the test. If len(args) > 1 and the first item of args is a string and contains a '%' rune then fmt.Fprintf is used to compose the name, else args are passed to fmt.Fprint. Do not forget it is the name of the test, not the reason of a potential failure.
Example ¶
package main import ( "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := td.NewT(&testing.T{}) ok := t.Empty(nil) // special case: nil is considered empty fmt.Println(ok) // fails, typed nil is not empty (expect for channel, map, slice or // pointers on array, channel, map slice and strings) ok = t.Empty((*int)(nil)) fmt.Println(ok) ok = t.Empty("") fmt.Println(ok) // Fails as 0 is a number, so not empty. Use Zero() instead ok = t.Empty(0) fmt.Println(ok) ok = t.Empty((map[string]int)(nil)) fmt.Println(ok) ok = t.Empty(map[string]int{}) fmt.Println(ok) ok = t.Empty(([]int)(nil)) fmt.Println(ok) ok = t.Empty([]int{}) fmt.Println(ok) ok = t.Empty([]int{3}) // fails, as not empty fmt.Println(ok) ok = t.Empty([3]int{}) // fails, Empty() is not Zero()! fmt.Println(ok) }
Output: true false true false true true true true false false
Example (Pointers) ¶
package main import ( "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := td.NewT(&testing.T{}) type MySlice []int ok := t.Empty(MySlice{}) // Ptr() not needed fmt.Println(ok) ok = t.Empty(&MySlice{}) fmt.Println(ok) l1 := &MySlice{} l2 := &l1 l3 := &l2 ok = t.Empty(&l3) fmt.Println(ok) // Works the same for array, map, channel and string // But not for others types as: type MyStruct struct { Value int } ok = t.Empty(&MyStruct{}) // fails, use Zero() instead fmt.Println(ok) }
Output: true true true false
func (*T) ErrorTrace ¶ added in v1.11.0
ErrorTrace uses t.TB.Error() to log a stack trace.
args... are optional and allow to prefix the trace by a message. If empty, this message defaults to "Stack trace:\n". If this message does not end with a "\n", one is automatically added. If len(args) > 1 and the first item of args is a string and contains a '%' rune then fmt.Fprintf is used to compose the name, else args are passed to fmt.Fprint.
See also T.LogTrace and T.FatalTrace.
func (*T) FailureIsFatal ¶
FailureIsFatal allows to choose whether t.TB.Fatal() or t.TB.Error() will be used to print the next failure reports. When enable is true (or missing) testing.Fatal() will be called, else testing.Error(). Using *testing.T or *testing.B instance as t.TB value, FailNow() method is called behind the scenes when Fatal() is called. See testing documentation for details.
It returns a new instance of *T so does not alter the original t and used as follows:
// Following t.Cmp() will call Fatal() if failure t = t.FailureIsFatal() t.Cmp(...) t.Cmp(...) // Following t.Cmp() won't call Fatal() if failure t = t.FailureIsFatal(false) t.Cmp(...)
or, if only one call is critic:
// This Cmp() call will call Fatal() if failure t.FailureIsFatal().Cmp(...) // Following t.Cmp() won't call Fatal() if failure t.Cmp(...) t.Cmp(...)
Note that t.FailureIsFatal() acts as t.FailureIsFatal(true).
func (*T) False ¶
False is shortcut for:
t.Cmp(got, false, args...)
Returns true if the test is OK, false if it fails.
t.False(IsAvailable(x), "x should not be available")
args... are optional and allow to name the test. This name is used in case of failure to qualify the test. If len(args) > 1 and the first item of args is a string and contains a '%' rune then fmt.Fprintf is used to compose the name, else args are passed to fmt.Fprint. Do not forget it is the name of the test, not the reason of a potential failure.
See also T.True.
Example ¶
package main import ( "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := td.NewT(&testing.T{}) got := false ok := t.False(got, "check that got is false!") fmt.Println(ok) got = true ok = t.False(got, "check that got is false!") fmt.Println(ok) }
Output: true false
func (*T) FatalTrace ¶ added in v1.11.0
FatalTrace uses t.TB.Fatal() to log a stack trace.
args... are optional and allow to prefix the trace by a message. If empty, this message defaults to "Stack trace:\n". If this message does not end with a "\n", one is automatically added. If len(args) > 1 and the first item of args is a string and contains a '%' rune then fmt.Fprintf is used to compose the name, else args are passed to fmt.Fprint.
See also T.LogTrace and T.ErrorTrace.
func (*T) First ¶ added in v1.13.0
First is a shortcut for:
t.Cmp(got, td.First(filter, expectedValue), args...)
See First for details.
Returns true if the test is OK, false if it fails.
args... are optional and allow to name the test. This name is used in case of failure to qualify the test. If len(args) > 1 and the first item of args is a string and contains a '%' rune then fmt.Fprintf is used to compose the name, else args are passed to fmt.Fprint. Do not forget it is the name of the test, not the reason of a potential failure.
Example (Classic) ¶
package main import ( "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := td.NewT(&testing.T{}) got := []int{-3, -2, -1, 0, 1, 2, 3} ok := t.First(got, td.Gt(0), 1) fmt.Println("first positive number is 1:", ok) isEven := func(x int) bool { return x%2 == 0 } ok = t.First(got, isEven, -2) fmt.Println("first even number is -2:", ok) ok = t.First(got, isEven, td.Lt(0)) fmt.Println("first even number is < 0:", ok) ok = t.First(got, isEven, td.Code(isEven)) fmt.Println("first even number is well even:", ok) }
Output: first positive number is 1: true first even number is -2: true first even number is < 0: true first even number is well even: true
Example (Empty) ¶
package main import ( "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := td.NewT(&testing.T{}) ok := t.First(([]int)(nil), td.Gt(0), td.Gt(0)) fmt.Println("first in nil slice:", ok) ok = t.First([]int{}, td.Gt(0), td.Gt(0)) fmt.Println("first in empty slice:", ok) ok = t.First(&[]int{}, td.Gt(0), td.Gt(0)) fmt.Println("first in empty pointed slice:", ok) ok = t.First([0]int{}, td.Gt(0), td.Gt(0)) fmt.Println("first in empty array:", ok) }
Output: first in nil slice: false first in empty slice: false first in empty pointed slice: false first in empty array: false
Example (Struct) ¶
package main import ( "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := td.NewT(&testing.T{}) type Person struct { Fullname string `json:"fullname"` Age int `json:"age"` } got := []*Person{ { Fullname: "Bob Foobar", Age: 42, }, { Fullname: "Alice Bingo", Age: 37, }, } ok := t.First(got, td.Smuggle("Age", td.Gt(30)), td.Smuggle("Fullname", "Bob Foobar")) fmt.Println("first person.Age > 30 → Bob:", ok) ok = t.First(got, td.JSONPointer("/age", td.Gt(30)), td.SuperJSONOf(`{"fullname":"Bob Foobar"}`)) fmt.Println("first person.Age > 30 → Bob, using JSON:", ok) ok = t.First(got, td.JSONPointer("/age", td.Gt(30)), td.JSONPointer("/fullname", td.HasPrefix("Bob"))) fmt.Println("first person.Age > 30 → Bob, using JSONPointer:", ok) }
Output: first person.Age > 30 → Bob: true first person.Age > 30 → Bob, using JSON: true first person.Age > 30 → Bob, using JSONPointer: true
func (*T) Grep ¶ added in v1.13.0
Grep is a shortcut for:
t.Cmp(got, td.Grep(filter, expectedValue), args...)
See Grep for details.
Returns true if the test is OK, false if it fails.
args... are optional and allow to name the test. This name is used in case of failure to qualify the test. If len(args) > 1 and the first item of args is a string and contains a '%' rune then fmt.Fprintf is used to compose the name, else args are passed to fmt.Fprint. Do not forget it is the name of the test, not the reason of a potential failure.
Example (Classic) ¶
package main import ( "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := td.NewT(&testing.T{}) got := []int{-3, -2, -1, 0, 1, 2, 3} ok := t.Grep(got, td.Gt(0), []int{1, 2, 3}) fmt.Println("check positive numbers:", ok) isEven := func(x int) bool { return x%2 == 0 } ok = t.Grep(got, isEven, []int{-2, 0, 2}) fmt.Println("even numbers are -2, 0 and 2:", ok) ok = t.Grep(got, isEven, td.Set(0, 2, -2)) fmt.Println("even numbers are also 0, 2 and -2:", ok) ok = t.Grep(got, isEven, td.ArrayEach(td.Code(isEven))) fmt.Println("even numbers are each even:", ok) }
Output: check positive numbers: true even numbers are -2, 0 and 2: true even numbers are also 0, 2 and -2: true even numbers are each even: true
Example (Nil) ¶
package main import ( "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := td.NewT(&testing.T{}) var got []int ok := t.Grep(got, td.Gt(0), ([]int)(nil)) fmt.Println("typed []int nil:", ok) ok = t.Grep(got, td.Gt(0), ([]string)(nil)) fmt.Println("typed []string nil:", ok) ok = t.Grep(got, td.Gt(0), td.Nil()) fmt.Println("td.Nil:", ok) ok = t.Grep(got, td.Gt(0), []int{}) fmt.Println("empty non-nil slice:", ok) }
Output: typed []int nil: true typed []string nil: false td.Nil: true empty non-nil slice: false
Example (Struct) ¶
package main import ( "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := td.NewT(&testing.T{}) type Person struct { Fullname string `json:"fullname"` Age int `json:"age"` } got := []*Person{ { Fullname: "Bob Foobar", Age: 42, }, { Fullname: "Alice Bingo", Age: 27, }, } ok := t.Grep(got, td.Smuggle("Age", td.Gt(30)), td.All( td.Len(1), td.ArrayEach(td.Smuggle("Fullname", "Bob Foobar")), )) fmt.Println("person.Age > 30 → only Bob:", ok) ok = t.Grep(got, td.JSONPointer("/age", td.Gt(30)), td.JSON(`[ SuperMapOf({"fullname":"Bob Foobar"}) ]`)) fmt.Println("person.Age > 30 → only Bob, using JSON:", ok) }
Output: person.Age > 30 → only Bob: true person.Age > 30 → only Bob, using JSON: true
func (*T) Gt ¶
Gt is a shortcut for:
t.Cmp(got, td.Gt(minExpectedValue), args...)
See Gt for details.
Returns true if the test is OK, false if it fails.
args... are optional and allow to name the test. This name is used in case of failure to qualify the test. If len(args) > 1 and the first item of args is a string and contains a '%' rune then fmt.Fprintf is used to compose the name, else args are passed to fmt.Fprint. Do not forget it is the name of the test, not the reason of a potential failure.
Example (Int) ¶
package main import ( "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := td.NewT(&testing.T{}) got := 156 ok := t.Gt(got, 155, "checks %v is > 155", got) fmt.Println(ok) ok = t.Gt(got, 156, "checks %v is > 156", got) fmt.Println(ok) }
Output: true false
Example (String) ¶
package main import ( "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := td.NewT(&testing.T{}) got := "abc" ok := t.Gt(got, "abb", `checks "%v" is > "abb"`, got) fmt.Println(ok) ok = t.Gt(got, "abc", `checks "%v" is > "abc"`, got) fmt.Println(ok) }
Output: true false
func (*T) Gte ¶
Gte is a shortcut for:
t.Cmp(got, td.Gte(minExpectedValue), args...)
See Gte for details.
Returns true if the test is OK, false if it fails.
args... are optional and allow to name the test. This name is used in case of failure to qualify the test. If len(args) > 1 and the first item of args is a string and contains a '%' rune then fmt.Fprintf is used to compose the name, else args are passed to fmt.Fprint. Do not forget it is the name of the test, not the reason of a potential failure.
Example (Int) ¶
package main import ( "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := td.NewT(&testing.T{}) got := 156 ok := t.Gte(got, 156, "checks %v is ≥ 156", got) fmt.Println(ok) ok = t.Gte(got, 155, "checks %v is ≥ 155", got) fmt.Println(ok) ok = t.Gte(got, 157, "checks %v is ≥ 157", got) fmt.Println(ok) }
Output: true true false
Example (String) ¶
package main import ( "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := td.NewT(&testing.T{}) got := "abc" ok := t.Gte(got, "abc", `checks "%v" is ≥ "abc"`, got) fmt.Println(ok) ok = t.Gte(got, "abb", `checks "%v" is ≥ "abb"`, got) fmt.Println(ok) ok = t.Gte(got, "abd", `checks "%v" is ≥ "abd"`, got) fmt.Println(ok) }
Output: true true false
func (*T) HasPrefix ¶
HasPrefix is a shortcut for:
t.Cmp(got, td.HasPrefix(expected), args...)
See HasPrefix for details.
Returns true if the test is OK, false if it fails.
args... are optional and allow to name the test. This name is used in case of failure to qualify the test. If len(args) > 1 and the first item of args is a string and contains a '%' rune then fmt.Fprintf is used to compose the name, else args are passed to fmt.Fprint. Do not forget it is the name of the test, not the reason of a potential failure.
Example ¶
package main import ( "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := td.NewT(&testing.T{}) got := "foobar" ok := t.HasPrefix(got, "foo", "checks %s", got) fmt.Println("using string:", ok) ok = t.Cmp([]byte(got), td.HasPrefix("foo"), "checks %s", got) fmt.Println("using []byte:", ok) }
Output: using string: true using []byte: true
Example (Error) ¶
package main import ( "errors" "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := td.NewT(&testing.T{}) got := errors.New("foobar") ok := t.HasPrefix(got, "foo", "checks %s", got) fmt.Println(ok) }
Output: true
Example (Stringer) ¶
package main import ( "bytes" "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := td.NewT(&testing.T{}) // bytes.Buffer implements fmt.Stringer got := bytes.NewBufferString("foobar") ok := t.HasPrefix(got, "foo", "checks %s", got) fmt.Println(ok) }
Output: true
func (*T) HasSuffix ¶
HasSuffix is a shortcut for:
t.Cmp(got, td.HasSuffix(expected), args...)
See HasSuffix for details.
Returns true if the test is OK, false if it fails.
args... are optional and allow to name the test. This name is used in case of failure to qualify the test. If len(args) > 1 and the first item of args is a string and contains a '%' rune then fmt.Fprintf is used to compose the name, else args are passed to fmt.Fprint. Do not forget it is the name of the test, not the reason of a potential failure.
Example ¶
package main import ( "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := td.NewT(&testing.T{}) got := "foobar" ok := t.HasSuffix(got, "bar", "checks %s", got) fmt.Println("using string:", ok) ok = t.Cmp([]byte(got), td.HasSuffix("bar"), "checks %s", got) fmt.Println("using []byte:", ok) }
Output: using string: true using []byte: true
Example (Error) ¶
package main import ( "errors" "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := td.NewT(&testing.T{}) got := errors.New("foobar") ok := t.HasSuffix(got, "bar", "checks %s", got) fmt.Println(ok) }
Output: true
Example (Stringer) ¶
package main import ( "bytes" "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := td.NewT(&testing.T{}) // bytes.Buffer implements fmt.Stringer got := bytes.NewBufferString("foobar") ok := t.HasSuffix(got, "bar", "checks %s", got) fmt.Println(ok) }
Output: true
func (*T) IgnoreUnexported ¶ added in v1.10.0
IgnoreUnexported tells go-testdeep to ignore unexported fields of structs whose type is one of types.
It always returns a new instance of *T so does not alter the original t.
t = t.IgnoreUnexported(MyStruct1{}, MyStruct2{})
types items can also be reflect.Type items. In this case, the target type is the one reflected by the reflect.Type.
t = t.IgnoreUnexported(reflect.TypeOf(MyStruct1{}))
As a special case, calling t.IgnoreUnexported() or t.IgnoreUnexported(true) returns an instance ignoring unexported fields globally, for all struct types. t.IgnoreUnexported(false) returns an instance not ignoring unexported fields anymore, except for types already recorded using a previous IgnoreUnexported call.
func (*T) Isa ¶
Isa is a shortcut for:
t.Cmp(got, td.Isa(model), args...)
See Isa for details.
Returns true if the test is OK, false if it fails.
args... are optional and allow to name the test. This name is used in case of failure to qualify the test. If len(args) > 1 and the first item of args is a string and contains a '%' rune then fmt.Fprintf is used to compose the name, else args are passed to fmt.Fprint. Do not forget it is the name of the test, not the reason of a potential failure.
Example ¶
package main import ( "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := td.NewT(&testing.T{}) type TstStruct struct { Field int } got := TstStruct{Field: 1} ok := t.Isa(got, TstStruct{}, "checks got is a TstStruct") fmt.Println(ok) ok = t.Isa(got, &TstStruct{}, "checks got is a pointer on a TstStruct") fmt.Println(ok) ok = t.Isa(&got, &TstStruct{}, "checks &got is a pointer on a TstStruct") fmt.Println(ok) }
Output: true false true
Example (Interface) ¶
package main import ( "bytes" "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := td.NewT(&testing.T{}) got := bytes.NewBufferString("foobar") ok := t.Isa(got, (*fmt.Stringer)(nil), "checks got implements fmt.Stringer interface") fmt.Println(ok) errGot := fmt.Errorf("An error #%d occurred", 123) ok = t.Isa(errGot, (*error)(nil), "checks errGot is a *error or implements error interface") fmt.Println(ok) // As nil, is passed below, it is not an interface but nil… So it // does not match errGot = nil ok = t.Isa(errGot, (*error)(nil), "checks errGot is a *error or implements error interface") fmt.Println(ok) // BUT if its address is passed, now it is OK as the types match ok = t.Isa(&errGot, (*error)(nil), "checks &errGot is a *error or implements error interface") fmt.Println(ok) }
Output: true true false true
func (*T) JSON ¶
JSON is a shortcut for:
t.Cmp(got, td.JSON(expectedJSON, params...), args...)
See JSON for details.
Returns true if the test is OK, false if it fails.
args... are optional and allow to name the test. This name is used in case of failure to qualify the test. If len(args) > 1 and the first item of args is a string and contains a '%' rune then fmt.Fprintf is used to compose the name, else args are passed to fmt.Fprint. Do not forget it is the name of the test, not the reason of a potential failure.
Example (Basic) ¶
package main import ( "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := td.NewT(&testing.T{}) got := &struct { Fullname string `json:"fullname"` Age int `json:"age"` }{ Fullname: "Bob", Age: 42, } ok := t.JSON(got, `{"age":42,"fullname":"Bob"}`, nil) fmt.Println("check got with age then fullname:", ok) ok = t.JSON(got, `{"fullname":"Bob","age":42}`, nil) fmt.Println("check got with fullname then age:", ok) ok = t.JSON(got, ` // This should be the JSON representation of a struct { // A person: "fullname": "Bob", // The name of this person "age": 42 /* The age of this person: - 42 of course - to demonstrate a multi-lines comment */ }`, nil) fmt.Println("check got with nicely formatted and commented JSON:", ok) ok = t.JSON(got, `{"fullname":"Bob","age":42,"gender":"male"}`, nil) fmt.Println("check got with gender field:", ok) ok = t.JSON(got, `{"fullname":"Bob"}`, nil) fmt.Println("check got with fullname only:", ok) ok = t.JSON(true, `true`, nil) fmt.Println("check boolean got is true:", ok) ok = t.JSON(42, `42`, nil) fmt.Println("check numeric got is 42:", ok) got = nil ok = t.JSON(got, `null`, nil) fmt.Println("check nil got is null:", ok) }
Output: check got with age then fullname: true check got with fullname then age: true check got with nicely formatted and commented JSON: true check got with gender field: false check got with fullname only: false check boolean got is true: true check numeric got is 42: true check nil got is null: true
Example (Embedding) ¶
package main import ( "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := td.NewT(&testing.T{}) got := &struct { Fullname string `json:"fullname"` Age int `json:"age"` }{ Fullname: "Bob Foobar", Age: 42, } ok := t.JSON(got, `{"age": NotZero(), "fullname": NotEmpty()}`, nil) fmt.Println("check got with simple operators:", ok) ok = t.JSON(got, `{"age": $^NotZero, "fullname": $^NotEmpty}`, nil) fmt.Println("check got with operator shortcuts:", ok) ok = t.JSON(got, ` { "age": Between(40, 42, "]]"), // in ]40; 42] "fullname": All( HasPrefix("Bob"), HasSuffix("bar") // ← comma is optional here ) }`, nil) fmt.Println("check got with complex operators:", ok) ok = t.JSON(got, ` { "age": Between(40, 42, "]["), // in ]40; 42[ → 42 excluded "fullname": All( HasPrefix("Bob"), HasSuffix("bar"), ) }`, nil) fmt.Println("check got with complex operators:", ok) ok = t.JSON(got, ` { "age": Between($1, $2, $3), // in ]40; 42] "fullname": All( HasPrefix($4), HasSuffix("bar") // ← comma is optional here ) }`, []any{40, 42, td.BoundsOutIn, "Bob"}) fmt.Println("check got with complex operators, w/placeholder args:", ok) }
Output: check got with simple operators: true check got with operator shortcuts: true check got with complex operators: true check got with complex operators: false check got with complex operators, w/placeholder args: true
Example (File) ¶
package main import ( "fmt" "os" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := td.NewT(&testing.T{}) got := &struct { Fullname string `json:"fullname"` Age int `json:"age"` Gender string `json:"gender"` }{ Fullname: "Bob Foobar", Age: 42, Gender: "male", } tmpDir, err := os.MkdirTemp("", "") if err != nil { t.Fatal(err) } defer os.RemoveAll(tmpDir) // clean up filename := tmpDir + "/test.json" if err = os.WriteFile(filename, []byte(` { "fullname": "$name", "age": "$age", "gender": "$gender" }`), 0644); err != nil { t.Fatal(err) } // OK let's test with this file ok := t.JSON(got, filename, []any{td.Tag("name", td.HasPrefix("Bob")), td.Tag("age", td.Between(40, 45)), td.Tag("gender", td.Re(`^(male|female)\z`))}) fmt.Println("Full match from file name:", ok) // When the file is already open file, err := os.Open(filename) if err != nil { t.Fatal(err) } ok = t.JSON(got, file, []any{td.Tag("name", td.HasPrefix("Bob")), td.Tag("age", td.Between(40, 45)), td.Tag("gender", td.Re(`^(male|female)\z`))}) fmt.Println("Full match from io.Reader:", ok) }
Output: Full match from file name: true Full match from io.Reader: true
Example (Placeholders) ¶
package main import ( "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := td.NewT(&testing.T{}) type Person struct { Fullname string `json:"fullname"` Age int `json:"age"` Children []*Person `json:"children,omitempty"` } got := &Person{ Fullname: "Bob Foobar", Age: 42, } ok := t.JSON(got, `{"age": $1, "fullname": $2}`, []any{42, "Bob Foobar"}) fmt.Println("check got with numeric placeholders without operators:", ok) ok = t.JSON(got, `{"age": $1, "fullname": $2}`, []any{td.Between(40, 45), td.HasSuffix("Foobar")}) fmt.Println("check got with numeric placeholders:", ok) ok = t.JSON(got, `{"age": "$1", "fullname": "$2"}`, []any{td.Between(40, 45), td.HasSuffix("Foobar")}) fmt.Println("check got with double-quoted numeric placeholders:", ok) ok = t.JSON(got, `{"age": $age, "fullname": $name}`, []any{td.Tag("age", td.Between(40, 45)), td.Tag("name", td.HasSuffix("Foobar"))}) fmt.Println("check got with named placeholders:", ok) got.Children = []*Person{ {Fullname: "Alice", Age: 28}, {Fullname: "Brian", Age: 22}, } ok = t.JSON(got, `{"age": $age, "fullname": $name, "children": $children}`, []any{td.Tag("age", td.Between(40, 45)), td.Tag("name", td.HasSuffix("Foobar")), td.Tag("children", td.Bag( &Person{Fullname: "Brian", Age: 22}, &Person{Fullname: "Alice", Age: 28}, ))}) fmt.Println("check got w/named placeholders, and children w/go structs:", ok) ok = t.JSON(got, `{"age": Between($1, $2), "fullname": HasSuffix($suffix), "children": Len(2)}`, []any{40, 45, td.Tag("suffix", "Foobar")}) fmt.Println("check got w/num & named placeholders:", ok) }
Output: check got with numeric placeholders without operators: true check got with numeric placeholders: true check got with double-quoted numeric placeholders: true check got with named placeholders: true check got w/named placeholders, and children w/go structs: true check got w/num & named placeholders: true
Example (RawStrings) ¶
package main import ( "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := td.NewT(&testing.T{}) type details struct { Address string `json:"address"` Car string `json:"car"` } got := &struct { Fullname string `json:"fullname"` Age int `json:"age"` Details details `json:"details"` }{ Fullname: "Foo Bar", Age: 42, Details: details{ Address: "something", Car: "Peugeot", }, } ok := t.JSON(got, ` { "fullname": HasPrefix("Foo"), "age": Between(41, 43), "details": SuperMapOf({ "address": NotEmpty, // () are optional when no parameters "car": Any("Peugeot", "Tesla", "Jeep") // any of these }) }`, nil) fmt.Println("Original:", ok) ok = t.JSON(got, ` { "fullname": "$^HasPrefix(\"Foo\")", "age": "$^Between(41, 43)", "details": "$^SuperMapOf({\n\"address\": NotEmpty,\n\"car\": Any(\"Peugeot\", \"Tesla\", \"Jeep\")\n})" }`, nil) fmt.Println("JSON compliant:", ok) ok = t.JSON(got, ` { "fullname": "$^HasPrefix(\"Foo\")", "age": "$^Between(41, 43)", "details": "$^SuperMapOf({ \"address\": NotEmpty, // () are optional when no parameters \"car\": Any(\"Peugeot\", \"Tesla\", \"Jeep\") // any of these })" }`, nil) fmt.Println("JSON multilines strings:", ok) ok = t.JSON(got, ` { "fullname": "$^HasPrefix(r<Foo>)", "age": "$^Between(41, 43)", "details": "$^SuperMapOf({ r<address>: NotEmpty, // () are optional when no parameters r<car>: Any(r<Peugeot>, r<Tesla>, r<Jeep>) // any of these })" }`, nil) fmt.Println("Raw strings:", ok) }
Output: Original: true JSON compliant: true JSON multilines strings: true Raw strings: true
func (*T) JSONPointer ¶ added in v1.8.0
JSONPointer is a shortcut for:
t.Cmp(got, td.JSONPointer(ptr, expectedValue), args...)
See JSONPointer for details.
Returns true if the test is OK, false if it fails.
args... are optional and allow to name the test. This name is used in case of failure to qualify the test. If len(args) > 1 and the first item of args is a string and contains a '%' rune then fmt.Fprintf is used to compose the name, else args are passed to fmt.Fprint. Do not forget it is the name of the test, not the reason of a potential failure.
Example (Has_hasnt) ¶
package main import ( "encoding/json" "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := td.NewT(&testing.T{}) got := json.RawMessage(` { "name": "Bob", "age": 42, "children": [ { "name": "Alice", "age": 16 }, { "name": "Britt", "age": 21, "children": [ { "name": "John", "age": 1 } ] } ] }`) // Has Bob some children? ok := t.JSONPointer(got, "/children", td.Len(td.Gt(0))) fmt.Println("Bob has at least one child:", ok) // But checking "children" exists is enough here ok = t.JSONPointer(got, "/children/0/children", td.Ignore()) fmt.Println("Alice has children:", ok) ok = t.JSONPointer(got, "/children/1/children", td.Ignore()) fmt.Println("Britt has children:", ok) // The reverse can be checked too ok = t.Cmp(got, td.Not(td.JSONPointer("/children/0/children", td.Ignore()))) fmt.Println("Alice hasn't children:", ok) ok = t.Cmp(got, td.Not(td.JSONPointer("/children/1/children", td.Ignore()))) fmt.Println("Britt hasn't children:", ok) }
Output: Bob has at least one child: true Alice has children: false Britt has children: true Alice hasn't children: true Britt hasn't children: false
Example (Rfc6901) ¶
package main import ( "encoding/json" "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := td.NewT(&testing.T{}) got := json.RawMessage(` { "foo": ["bar", "baz"], "": 0, "a/b": 1, "c%d": 2, "e^f": 3, "g|h": 4, "i\\j": 5, "k\"l": 6, " ": 7, "m~n": 8 }`) expected := map[string]any{ "foo": []any{"bar", "baz"}, "": 0, "a/b": 1, "c%d": 2, "e^f": 3, "g|h": 4, `i\j`: 5, `k"l`: 6, " ": 7, "m~n": 8, } ok := t.JSONPointer(got, "", expected) fmt.Println("Empty JSON pointer means all:", ok) ok = t.JSONPointer(got, `/foo`, []any{"bar", "baz"}) fmt.Println("Extract `foo` key:", ok) ok = t.JSONPointer(got, `/foo/0`, "bar") fmt.Println("First item of `foo` key slice:", ok) ok = t.JSONPointer(got, `/`, 0) fmt.Println("Empty key:", ok) ok = t.JSONPointer(got, `/a~1b`, 1) fmt.Println("Slash has to be escaped using `~1`:", ok) ok = t.JSONPointer(got, `/c%d`, 2) fmt.Println("% in key:", ok) ok = t.JSONPointer(got, `/e^f`, 3) fmt.Println("^ in key:", ok) ok = t.JSONPointer(got, `/g|h`, 4) fmt.Println("| in key:", ok) ok = t.JSONPointer(got, `/i\j`, 5) fmt.Println("Backslash in key:", ok) ok = t.JSONPointer(got, `/k"l`, 6) fmt.Println("Double-quote in key:", ok) ok = t.JSONPointer(got, `/ `, 7) fmt.Println("Space key:", ok) ok = t.JSONPointer(got, `/m~0n`, 8) fmt.Println("Tilde has to be escaped using `~0`:", ok) }
Output: Empty JSON pointer means all: true Extract `foo` key: true First item of `foo` key slice: true Empty key: true Slash has to be escaped using `~1`: true % in key: true ^ in key: true | in key: true Backslash in key: true Double-quote in key: true Space key: true Tilde has to be escaped using `~0`: true
Example (Struct) ¶
package main import ( "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := td.NewT(&testing.T{}) // Without json tags, encoding/json uses public fields name type Item struct { Name string Value int64 Next *Item } got := Item{ Name: "first", Value: 1, Next: &Item{ Name: "second", Value: 2, Next: &Item{ Name: "third", Value: 3, }, }, } ok := t.JSONPointer(got, "/Next/Next/Name", "third") fmt.Println("3rd item name is `third`:", ok) ok = t.JSONPointer(got, "/Next/Next/Value", td.Gte(int64(3))) fmt.Println("3rd item value is greater or equal than 3:", ok) ok = t.JSONPointer(got, "/Next", td.JSONPointer("/Next", td.JSONPointer("/Value", td.Gte(int64(3))))) fmt.Println("3rd item value is still greater or equal than 3:", ok) ok = t.JSONPointer(got, "/Next/Next/Next/Name", td.Ignore()) fmt.Println("4th item exists and has a name:", ok) // Struct comparison work with or without pointer: &Item{…} works too ok = t.JSONPointer(got, "/Next/Next", Item{ Name: "third", Value: 3, }) fmt.Println("3rd item full comparison:", ok) }
Output: 3rd item name is `third`: true 3rd item value is greater or equal than 3: true 3rd item value is still greater or equal than 3: true 4th item exists and has a name: false 3rd item full comparison: true
func (*T) Keys ¶
Keys is a shortcut for:
t.Cmp(got, td.Keys(val), args...)
See Keys for details.
Returns true if the test is OK, false if it fails.
args... are optional and allow to name the test. This name is used in case of failure to qualify the test. If len(args) > 1 and the first item of args is a string and contains a '%' rune then fmt.Fprintf is used to compose the name, else args are passed to fmt.Fprint. Do not forget it is the name of the test, not the reason of a potential failure.
Example ¶
package main import ( "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := td.NewT(&testing.T{}) got := map[string]int{"foo": 1, "bar": 2, "zip": 3} // Keys tests keys in an ordered manner ok := t.Keys(got, []string{"bar", "foo", "zip"}) fmt.Println("All sorted keys are found:", ok) // If the expected keys are not ordered, it fails ok = t.Keys(got, []string{"zip", "bar", "foo"}) fmt.Println("All unsorted keys are found:", ok) // To circumvent that, one can use Bag operator ok = t.Keys(got, td.Bag("zip", "bar", "foo")) fmt.Println("All unsorted keys are found, with the help of Bag operator:", ok) // Check that each key is 3 bytes long ok = t.Keys(got, td.ArrayEach(td.Len(3))) fmt.Println("Each key is 3 bytes long:", ok) }
Output: All sorted keys are found: true All unsorted keys are found: false All unsorted keys are found, with the help of Bag operator: true Each key is 3 bytes long: true
func (*T) Last ¶ added in v1.13.0
Last is a shortcut for:
t.Cmp(got, td.Last(filter, expectedValue), args...)
See Last for details.
Returns true if the test is OK, false if it fails.
args... are optional and allow to name the test. This name is used in case of failure to qualify the test. If len(args) > 1 and the first item of args is a string and contains a '%' rune then fmt.Fprintf is used to compose the name, else args are passed to fmt.Fprint. Do not forget it is the name of the test, not the reason of a potential failure.
Example (Classic) ¶
package main import ( "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := td.NewT(&testing.T{}) got := []int{-3, -2, -1, 0, 1, 2, 3} ok := t.Last(got, td.Lt(0), -1) fmt.Println("last negative number is -1:", ok) isEven := func(x int) bool { return x%2 == 0 } ok = t.Last(got, isEven, 2) fmt.Println("last even number is 2:", ok) ok = t.Last(got, isEven, td.Gt(0)) fmt.Println("last even number is > 0:", ok) ok = t.Last(got, isEven, td.Code(isEven)) fmt.Println("last even number is well even:", ok) }
Output: last negative number is -1: true last even number is 2: true last even number is > 0: true last even number is well even: true
Example (Empty) ¶
package main import ( "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := td.NewT(&testing.T{}) ok := t.Last(([]int)(nil), td.Gt(0), td.Gt(0)) fmt.Println("last in nil slice:", ok) ok = t.Last([]int{}, td.Gt(0), td.Gt(0)) fmt.Println("last in empty slice:", ok) ok = t.Last(&[]int{}, td.Gt(0), td.Gt(0)) fmt.Println("last in empty pointed slice:", ok) ok = t.Last([0]int{}, td.Gt(0), td.Gt(0)) fmt.Println("last in empty array:", ok) }
Output: last in nil slice: false last in empty slice: false last in empty pointed slice: false last in empty array: false
Example (Struct) ¶
package main import ( "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := td.NewT(&testing.T{}) type Person struct { Fullname string `json:"fullname"` Age int `json:"age"` } got := []*Person{ { Fullname: "Bob Foobar", Age: 42, }, { Fullname: "Alice Bingo", Age: 37, }, } ok := t.Last(got, td.Smuggle("Age", td.Gt(30)), td.Smuggle("Fullname", "Alice Bingo")) fmt.Println("last person.Age > 30 → Alice:", ok) ok = t.Last(got, td.JSONPointer("/age", td.Gt(30)), td.SuperJSONOf(`{"fullname":"Alice Bingo"}`)) fmt.Println("last person.Age > 30 → Alice, using JSON:", ok) ok = t.Last(got, td.JSONPointer("/age", td.Gt(30)), td.JSONPointer("/fullname", td.HasPrefix("Alice"))) fmt.Println("first person.Age > 30 → Alice, using JSONPointer:", ok) }
Output: last person.Age > 30 → Alice: true last person.Age > 30 → Alice, using JSON: true first person.Age > 30 → Alice, using JSONPointer: true
func (*T) Len ¶
Len is a shortcut for:
t.Cmp(got, td.Len(expectedLen), args...)
See Len for details.
Returns true if the test is OK, false if it fails.
args... are optional and allow to name the test. This name is used in case of failure to qualify the test. If len(args) > 1 and the first item of args is a string and contains a '%' rune then fmt.Fprintf is used to compose the name, else args are passed to fmt.Fprint. Do not forget it is the name of the test, not the reason of a potential failure.
Example (Map) ¶
package main import ( "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := td.NewT(&testing.T{}) got := map[int]bool{11: true, 22: false, 33: false} ok := t.Len(got, 3, "checks %v len is 3", got) fmt.Println(ok) ok = t.Len(got, 0, "checks %v len is 0", got) fmt.Println(ok) got = nil ok = t.Len(got, 0, "checks %v len is 0", got) fmt.Println(ok) }
Output: true false true
Example (OperatorMap) ¶
package main import ( "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := td.NewT(&testing.T{}) got := map[int]bool{11: true, 22: false, 33: false} ok := t.Len(got, td.Between(3, 8), "checks %v len is in [3 .. 8]", got) fmt.Println(ok) ok = t.Len(got, td.Gte(3), "checks %v len is ≥ 3", got) fmt.Println(ok) }
Output: true true
Example (OperatorSlice) ¶
package main import ( "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := td.NewT(&testing.T{}) got := []int{11, 22, 33} ok := t.Len(got, td.Between(3, 8), "checks %v len is in [3 .. 8]", got) fmt.Println(ok) ok = t.Len(got, td.Lt(5), "checks %v len is < 5", got) fmt.Println(ok) }
Output: true true
Example (Slice) ¶
package main import ( "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := td.NewT(&testing.T{}) got := []int{11, 22, 33} ok := t.Len(got, 3, "checks %v len is 3", got) fmt.Println(ok) ok = t.Len(got, 0, "checks %v len is 0", got) fmt.Println(ok) got = nil ok = t.Len(got, 0, "checks %v len is 0", got) fmt.Println(ok) }
Output: true false true
func (*T) LogTrace ¶ added in v1.11.0
LogTrace uses t.TB.Log() to log a stack trace.
args... are optional and allow to prefix the trace by a message. If empty, this message defaults to "Stack trace:\n". If this message does not end with a "\n", one is automatically added. If len(args) > 1 and the first item of args is a string and contains a '%' rune then fmt.Fprintf is used to compose the name, else args are passed to fmt.Fprint.
See also T.ErrorTrace and T.FatalTrace.
func (*T) Lt ¶
Lt is a shortcut for:
t.Cmp(got, td.Lt(maxExpectedValue), args...)
See Lt for details.
Returns true if the test is OK, false if it fails.
args... are optional and allow to name the test. This name is used in case of failure to qualify the test. If len(args) > 1 and the first item of args is a string and contains a '%' rune then fmt.Fprintf is used to compose the name, else args are passed to fmt.Fprint. Do not forget it is the name of the test, not the reason of a potential failure.
Example (Int) ¶
package main import ( "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := td.NewT(&testing.T{}) got := 156 ok := t.Lt(got, 157, "checks %v is < 157", got) fmt.Println(ok) ok = t.Lt(got, 156, "checks %v is < 156", got) fmt.Println(ok) }
Output: true false
Example (String) ¶
package main import ( "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := td.NewT(&testing.T{}) got := "abc" ok := t.Lt(got, "abd", `checks "%v" is < "abd"`, got) fmt.Println(ok) ok = t.Lt(got, "abc", `checks "%v" is < "abc"`, got) fmt.Println(ok) }
Output: true false
func (*T) Lte ¶
Lte is a shortcut for:
t.Cmp(got, td.Lte(maxExpectedValue), args...)
See Lte for details.
Returns true if the test is OK, false if it fails.
args... are optional and allow to name the test. This name is used in case of failure to qualify the test. If len(args) > 1 and the first item of args is a string and contains a '%' rune then fmt.Fprintf is used to compose the name, else args are passed to fmt.Fprint. Do not forget it is the name of the test, not the reason of a potential failure.
Example (Int) ¶
package main import ( "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := td.NewT(&testing.T{}) got := 156 ok := t.Lte(got, 156, "checks %v is ≤ 156", got) fmt.Println(ok) ok = t.Lte(got, 157, "checks %v is ≤ 157", got) fmt.Println(ok) ok = t.Lte(got, 155, "checks %v is ≤ 155", got) fmt.Println(ok) }
Output: true true false
Example (String) ¶
package main import ( "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := td.NewT(&testing.T{}) got := "abc" ok := t.Lte(got, "abc", `checks "%v" is ≤ "abc"`, got) fmt.Println(ok) ok = t.Lte(got, "abd", `checks "%v" is ≤ "abd"`, got) fmt.Println(ok) ok = t.Lte(got, "abb", `checks "%v" is ≤ "abb"`, got) fmt.Println(ok) }
Output: true true false
func (*T) Map ¶
func (t *T) Map(got, model any, expectedEntries MapEntries, args ...any) bool
Map is a shortcut for:
t.Cmp(got, td.Map(model, expectedEntries), args...)
See Map for details.
Returns true if the test is OK, false if it fails.
args... are optional and allow to name the test. This name is used in case of failure to qualify the test. If len(args) > 1 and the first item of args is a string and contains a '%' rune then fmt.Fprintf is used to compose the name, else args are passed to fmt.Fprint. Do not forget it is the name of the test, not the reason of a potential failure.
Example (Map) ¶
package main import ( "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := td.NewT(&testing.T{}) got := map[string]int{"foo": 12, "bar": 42, "zip": 89} ok := t.Map(got, map[string]int{"bar": 42}, td.MapEntries{"foo": td.Lt(15), "zip": td.Ignore()}, "checks map %v", got) fmt.Println(ok) ok = t.Map(got, map[string]int{}, td.MapEntries{"bar": 42, "foo": td.Lt(15), "zip": td.Ignore()}, "checks map %v", got) fmt.Println(ok) ok = t.Map(got, (map[string]int)(nil), td.MapEntries{"bar": 42, "foo": td.Lt(15), "zip": td.Ignore()}, "checks map %v", got) fmt.Println(ok) }
Output: true true true
Example (TypedMap) ¶
package main import ( "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := td.NewT(&testing.T{}) type MyMap map[string]int got := MyMap{"foo": 12, "bar": 42, "zip": 89} ok := t.Map(got, MyMap{"bar": 42}, td.MapEntries{"foo": td.Lt(15), "zip": td.Ignore()}, "checks typed map %v", got) fmt.Println(ok) ok = t.Map(&got, &MyMap{"bar": 42}, td.MapEntries{"foo": td.Lt(15), "zip": td.Ignore()}, "checks pointer on typed map %v", got) fmt.Println(ok) ok = t.Map(&got, &MyMap{}, td.MapEntries{"bar": 42, "foo": td.Lt(15), "zip": td.Ignore()}, "checks pointer on typed map %v", got) fmt.Println(ok) ok = t.Map(&got, (*MyMap)(nil), td.MapEntries{"bar": 42, "foo": td.Lt(15), "zip": td.Ignore()}, "checks pointer on typed map %v", got) fmt.Println(ok) }
Output: true true true true
func (*T) MapEach ¶
MapEach is a shortcut for:
t.Cmp(got, td.MapEach(expectedValue), args...)
See MapEach for details.
Returns true if the test is OK, false if it fails.
args... are optional and allow to name the test. This name is used in case of failure to qualify the test. If len(args) > 1 and the first item of args is a string and contains a '%' rune then fmt.Fprintf is used to compose the name, else args are passed to fmt.Fprint. Do not forget it is the name of the test, not the reason of a potential failure.
Example (Map) ¶
package main import ( "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := td.NewT(&testing.T{}) got := map[string]int{"foo": 12, "bar": 42, "zip": 89} ok := t.MapEach(got, td.Between(10, 90), "checks each value of map %v is in [10 .. 90]", got) fmt.Println(ok) }
Output: true
Example (TypedMap) ¶
package main import ( "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := td.NewT(&testing.T{}) type MyMap map[string]int got := MyMap{"foo": 12, "bar": 42, "zip": 89} ok := t.MapEach(got, td.Between(10, 90), "checks each value of typed map %v is in [10 .. 90]", got) fmt.Println(ok) ok = t.MapEach(&got, td.Between(10, 90), "checks each value of typed map pointer %v is in [10 .. 90]", got) fmt.Println(ok) }
Output: true true
func (*T) N ¶
N is a shortcut for:
t.Cmp(got, td.N(num, tolerance), args...)
See N for details.
N optional parameter tolerance is here mandatory. 0 value should be passed to mimic its absence in original N call.
Returns true if the test is OK, false if it fails.
args... are optional and allow to name the test. This name is used in case of failure to qualify the test. If len(args) > 1 and the first item of args is a string and contains a '%' rune then fmt.Fprintf is used to compose the name, else args are passed to fmt.Fprint. Do not forget it is the name of the test, not the reason of a potential failure.
Example ¶
package main import ( "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := td.NewT(&testing.T{}) got := 1.12345 ok := t.N(got, 1.1234, 0.00006, "checks %v = 1.1234 ± 0.00006", got) fmt.Println(ok) }
Output: true
func (*T) NaN ¶
NaN is a shortcut for:
t.Cmp(got, td.NaN(), args...)
See NaN for details.
Returns true if the test is OK, false if it fails.
args... are optional and allow to name the test. This name is used in case of failure to qualify the test. If len(args) > 1 and the first item of args is a string and contains a '%' rune then fmt.Fprintf is used to compose the name, else args are passed to fmt.Fprint. Do not forget it is the name of the test, not the reason of a potential failure.
Example (Float32) ¶
package main import ( "fmt" "math" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := td.NewT(&testing.T{}) got := float32(math.NaN()) ok := t.NaN(got, "checks %v is not-a-number", got) fmt.Println("float32(math.NaN()) is float32 not-a-number:", ok) got = 12 ok = t.NaN(got, "checks %v is not-a-number", got) fmt.Println("float32(12) is float32 not-a-number:", ok) }
Output: float32(math.NaN()) is float32 not-a-number: true float32(12) is float32 not-a-number: false
Example (Float64) ¶
package main import ( "fmt" "math" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := td.NewT(&testing.T{}) got := math.NaN() ok := t.NaN(got, "checks %v is not-a-number", got) fmt.Println("math.NaN() is not-a-number:", ok) got = 12 ok = t.NaN(got, "checks %v is not-a-number", got) fmt.Println("float64(12) is not-a-number:", ok) // math.NaN() is not-a-number: true // float64(12) is not-a-number: false }
Output:
func (*T) Nil ¶
Nil is a shortcut for:
t.Cmp(got, td.Nil(), args...)
See Nil for details.
Returns true if the test is OK, false if it fails.
args... are optional and allow to name the test. This name is used in case of failure to qualify the test. If len(args) > 1 and the first item of args is a string and contains a '%' rune then fmt.Fprintf is used to compose the name, else args are passed to fmt.Fprint. Do not forget it is the name of the test, not the reason of a potential failure.
Example ¶
package main import ( "bytes" "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := td.NewT(&testing.T{}) var got fmt.Stringer // interface // nil value can be compared directly with nil, no need of Nil() here ok := t.Cmp(got, nil) fmt.Println(ok) // But it works with Nil() anyway ok = t.Nil(got) fmt.Println(ok) got = (*bytes.Buffer)(nil) // In the case of an interface containing a nil pointer, comparing // with nil fails, as the interface is not nil ok = t.Cmp(got, nil) fmt.Println(ok) // In this case Nil() succeed ok = t.Nil(got) fmt.Println(ok) }
Output: true true false true
func (*T) None ¶
None is a shortcut for:
t.Cmp(got, td.None(notExpectedValues...), args...)
See None for details.
Returns true if the test is OK, false if it fails.
args... are optional and allow to name the test. This name is used in case of failure to qualify the test. If len(args) > 1 and the first item of args is a string and contains a '%' rune then fmt.Fprintf is used to compose the name, else args are passed to fmt.Fprint. Do not forget it is the name of the test, not the reason of a potential failure.
Example ¶
package main import ( "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := td.NewT(&testing.T{}) got := 18 ok := t.None(got, []any{0, 10, 20, 30, td.Between(100, 199)}, "checks %v is non-null, and ≠ 10, 20 & 30, and not in [100-199]", got) fmt.Println(ok) got = 20 ok = t.None(got, []any{0, 10, 20, 30, td.Between(100, 199)}, "checks %v is non-null, and ≠ 10, 20 & 30, and not in [100-199]", got) fmt.Println(ok) got = 142 ok = t.None(got, []any{0, 10, 20, 30, td.Between(100, 199)}, "checks %v is non-null, and ≠ 10, 20 & 30, and not in [100-199]", got) fmt.Println(ok) prime := td.Flatten([]int{1, 2, 3, 5, 7, 11, 13}) even := td.Flatten([]int{2, 4, 6, 8, 10, 12, 14}) for _, got := range [...]int{9, 3, 8, 15} { ok = t.None(got, []any{prime, even, td.Gt(14)}, "checks %v is not prime number, nor an even number and not > 14") fmt.Printf("%d → %t\n", got, ok) } }
Output: true false false 9 → true 3 → false 8 → false 15 → false
func (*T) Not ¶
Not is a shortcut for:
t.Cmp(got, td.Not(notExpected), args...)
See Not for details.
Returns true if the test is OK, false if it fails.
args... are optional and allow to name the test. This name is used in case of failure to qualify the test. If len(args) > 1 and the first item of args is a string and contains a '%' rune then fmt.Fprintf is used to compose the name, else args are passed to fmt.Fprint. Do not forget it is the name of the test, not the reason of a potential failure.
Example ¶
package main import ( "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := td.NewT(&testing.T{}) got := 42 ok := t.Not(got, 0, "checks %v is non-null", got) fmt.Println(ok) ok = t.Not(got, td.Between(10, 30), "checks %v is not in [10 .. 30]", got) fmt.Println(ok) got = 0 ok = t.Not(got, 0, "checks %v is non-null", got) fmt.Println(ok) }
Output: true true false
func (*T) NotAny ¶
NotAny is a shortcut for:
t.Cmp(got, td.NotAny(notExpectedItems...), args...)
See NotAny for details.
Returns true if the test is OK, false if it fails.
args... are optional and allow to name the test. This name is used in case of failure to qualify the test. If len(args) > 1 and the first item of args is a string and contains a '%' rune then fmt.Fprintf is used to compose the name, else args are passed to fmt.Fprint. Do not forget it is the name of the test, not the reason of a potential failure.
Example ¶
package main import ( "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := td.NewT(&testing.T{}) got := []int{4, 5, 9, 42} ok := t.NotAny(got, []any{3, 6, 8, 41, 43}, "checks %v contains no item listed in NotAny()", got) fmt.Println(ok) ok = t.NotAny(got, []any{3, 6, 8, 42, 43}, "checks %v contains no item listed in NotAny()", got) fmt.Println(ok) // When expected is already a non-[]any slice, it cannot be // flattened directly using notExpected... without copying it to a new // []any slice, then use td.Flatten! notExpected := []int{3, 6, 8, 41, 43} ok = t.NotAny(got, []any{td.Flatten(notExpected)}, "checks %v contains no item listed in notExpected", got) fmt.Println(ok) }
Output: true false true
func (*T) NotEmpty ¶
NotEmpty is a shortcut for:
t.Cmp(got, td.NotEmpty(), args...)
See NotEmpty for details.
Returns true if the test is OK, false if it fails.
args... are optional and allow to name the test. This name is used in case of failure to qualify the test. If len(args) > 1 and the first item of args is a string and contains a '%' rune then fmt.Fprintf is used to compose the name, else args are passed to fmt.Fprint. Do not forget it is the name of the test, not the reason of a potential failure.
Example ¶
package main import ( "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := td.NewT(&testing.T{}) ok := t.NotEmpty(nil) // fails, as nil is considered empty fmt.Println(ok) ok = t.NotEmpty("foobar") fmt.Println(ok) // Fails as 0 is a number, so not empty. Use NotZero() instead ok = t.NotEmpty(0) fmt.Println(ok) ok = t.NotEmpty(map[string]int{"foobar": 42}) fmt.Println(ok) ok = t.NotEmpty([]int{1}) fmt.Println(ok) ok = t.NotEmpty([3]int{}) // succeeds, NotEmpty() is not NotZero()! fmt.Println(ok) }
Output: false true false true true true
Example (Pointers) ¶
package main import ( "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := td.NewT(&testing.T{}) type MySlice []int ok := t.NotEmpty(MySlice{12}) fmt.Println(ok) ok = t.NotEmpty(&MySlice{12}) // Ptr() not needed fmt.Println(ok) l1 := &MySlice{12} l2 := &l1 l3 := &l2 ok = t.NotEmpty(&l3) fmt.Println(ok) // Works the same for array, map, channel and string // But not for others types as: type MyStruct struct { Value int } ok = t.NotEmpty(&MyStruct{}) // fails, use NotZero() instead fmt.Println(ok) }
Output: true true true false
func (*T) NotNaN ¶
NotNaN is a shortcut for:
t.Cmp(got, td.NotNaN(), args...)
See NotNaN for details.
Returns true if the test is OK, false if it fails.
args... are optional and allow to name the test. This name is used in case of failure to qualify the test. If len(args) > 1 and the first item of args is a string and contains a '%' rune then fmt.Fprintf is used to compose the name, else args are passed to fmt.Fprint. Do not forget it is the name of the test, not the reason of a potential failure.
Example (Float32) ¶
package main import ( "fmt" "math" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := td.NewT(&testing.T{}) got := float32(math.NaN()) ok := t.NotNaN(got, "checks %v is not-a-number", got) fmt.Println("float32(math.NaN()) is NOT float32 not-a-number:", ok) got = 12 ok = t.NotNaN(got, "checks %v is not-a-number", got) fmt.Println("float32(12) is NOT float32 not-a-number:", ok) }
Output: float32(math.NaN()) is NOT float32 not-a-number: false float32(12) is NOT float32 not-a-number: true
Example (Float64) ¶
package main import ( "fmt" "math" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := td.NewT(&testing.T{}) got := math.NaN() ok := t.NotNaN(got, "checks %v is not-a-number", got) fmt.Println("math.NaN() is not-a-number:", ok) got = 12 ok = t.NotNaN(got, "checks %v is not-a-number", got) fmt.Println("float64(12) is not-a-number:", ok) // math.NaN() is NOT not-a-number: false // float64(12) is NOT not-a-number: true }
Output:
func (*T) NotNil ¶
NotNil is a shortcut for:
t.Cmp(got, td.NotNil(), args...)
See NotNil for details.
Returns true if the test is OK, false if it fails.
args... are optional and allow to name the test. This name is used in case of failure to qualify the test. If len(args) > 1 and the first item of args is a string and contains a '%' rune then fmt.Fprintf is used to compose the name, else args are passed to fmt.Fprint. Do not forget it is the name of the test, not the reason of a potential failure.
Example ¶
package main import ( "bytes" "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := td.NewT(&testing.T{}) var got fmt.Stringer = &bytes.Buffer{} // nil value can be compared directly with Not(nil), no need of NotNil() here ok := t.Cmp(got, td.Not(nil)) fmt.Println(ok) // But it works with NotNil() anyway ok = t.NotNil(got) fmt.Println(ok) got = (*bytes.Buffer)(nil) // In the case of an interface containing a nil pointer, comparing // with Not(nil) succeeds, as the interface is not nil ok = t.Cmp(got, td.Not(nil)) fmt.Println(ok) // In this case NotNil() fails ok = t.NotNil(got) fmt.Println(ok) }
Output: true true true false
func (*T) NotZero ¶
NotZero is a shortcut for:
t.Cmp(got, td.NotZero(), args...)
See NotZero for details.
Returns true if the test is OK, false if it fails.
args... are optional and allow to name the test. This name is used in case of failure to qualify the test. If len(args) > 1 and the first item of args is a string and contains a '%' rune then fmt.Fprintf is used to compose the name, else args are passed to fmt.Fprint. Do not forget it is the name of the test, not the reason of a potential failure.
Example ¶
package main import ( "bytes" "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := td.NewT(&testing.T{}) ok := t.NotZero(0) // fails fmt.Println(ok) ok = t.NotZero(float64(0)) // fails fmt.Println(ok) ok = t.NotZero(12) fmt.Println(ok) ok = t.NotZero((map[string]int)(nil)) // fails, as nil fmt.Println(ok) ok = t.NotZero(map[string]int{}) // succeeds, as not nil fmt.Println(ok) ok = t.NotZero(([]int)(nil)) // fails, as nil fmt.Println(ok) ok = t.NotZero([]int{}) // succeeds, as not nil fmt.Println(ok) ok = t.NotZero([3]int{}) // fails fmt.Println(ok) ok = t.NotZero([3]int{0, 1}) // succeeds, DATA[1] is not 0 fmt.Println(ok) ok = t.NotZero(bytes.Buffer{}) // fails fmt.Println(ok) ok = t.NotZero(&bytes.Buffer{}) // succeeds, as pointer not nil fmt.Println(ok) ok = t.Cmp(&bytes.Buffer{}, td.Ptr(td.NotZero())) // fails as deref by Ptr() fmt.Println(ok) }
Output: false false true false true false true false true false true false
func (*T) PPtr ¶
PPtr is a shortcut for:
t.Cmp(got, td.PPtr(val), args...)
See PPtr for details.
Returns true if the test is OK, false if it fails.
args... are optional and allow to name the test. This name is used in case of failure to qualify the test. If len(args) > 1 and the first item of args is a string and contains a '%' rune then fmt.Fprintf is used to compose the name, else args are passed to fmt.Fprint. Do not forget it is the name of the test, not the reason of a potential failure.
Example ¶
package main import ( "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := td.NewT(&testing.T{}) num := 12 got := &num ok := t.PPtr(&got, 12) fmt.Println(ok) ok = t.PPtr(&got, td.Between(4, 15)) fmt.Println(ok) }
Output: true true
func (*T) Parallel ¶ added in v1.10.0
func (t *T) Parallel()
Parallel marks this test as runnable in parallel with other parallel tests. If t.TB implements Parallel(), as *testing.T does, it is usually used to mark top-level tests and/or subtests as safe for parallel execution:
func TestCreateRecord(tt *testing.T) { t := td.NewT(tt) t.Parallel() t.Run("no error", func(t *td.T) { t.Parallel() // ... })
If t.TB does not implement Parallel(), this method is a no-op.
func (*T) Ptr ¶
Ptr is a shortcut for:
t.Cmp(got, td.Ptr(val), args...)
See Ptr for details.
Returns true if the test is OK, false if it fails.
args... are optional and allow to name the test. This name is used in case of failure to qualify the test. If len(args) > 1 and the first item of args is a string and contains a '%' rune then fmt.Fprintf is used to compose the name, else args are passed to fmt.Fprint. Do not forget it is the name of the test, not the reason of a potential failure.
Example ¶
package main import ( "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := td.NewT(&testing.T{}) got := 12 ok := t.Ptr(&got, 12) fmt.Println(ok) ok = t.Ptr(&got, td.Between(4, 15)) fmt.Println(ok) }
Output: true true
func (*T) Re ¶
Re is a shortcut for:
t.Cmp(got, td.Re(reg, capture), args...)
See Re for details.
Re optional parameter capture is here mandatory. nil value should be passed to mimic its absence in original Re call.
Returns true if the test is OK, false if it fails.
args... are optional and allow to name the test. This name is used in case of failure to qualify the test. If len(args) > 1 and the first item of args is a string and contains a '%' rune then fmt.Fprintf is used to compose the name, else args are passed to fmt.Fprint. Do not forget it is the name of the test, not the reason of a potential failure.
Example ¶
package main import ( "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := td.NewT(&testing.T{}) got := "foo bar" ok := t.Re(got, "(zip|bar)$", nil, "checks value %s", got) fmt.Println(ok) got = "bar foo" ok = t.Re(got, "(zip|bar)$", nil, "checks value %s", got) fmt.Println(ok) }
Output: true false
Example (Capture) ¶
package main import ( "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := td.NewT(&testing.T{}) got := "foo bar biz" ok := t.Re(got, `^(\w+) (\w+) (\w+)$`, td.Set("biz", "foo", "bar"), "checks value %s", got) fmt.Println(ok) got = "foo bar! biz" ok = t.Re(got, `^(\w+) (\w+) (\w+)$`, td.Set("biz", "foo", "bar"), "checks value %s", got) fmt.Println(ok) }
Output: true false
Example (Compiled) ¶
package main import ( "fmt" "regexp" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := td.NewT(&testing.T{}) expected := regexp.MustCompile("(zip|bar)$") got := "foo bar" ok := t.Re(got, expected, nil, "checks value %s", got) fmt.Println(ok) got = "bar foo" ok = t.Re(got, expected, nil, "checks value %s", got) fmt.Println(ok) }
Output: true false
Example (CompiledCapture) ¶
package main import ( "fmt" "regexp" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := td.NewT(&testing.T{}) expected := regexp.MustCompile(`^(\w+) (\w+) (\w+)$`) got := "foo bar biz" ok := t.Re(got, expected, td.Set("biz", "foo", "bar"), "checks value %s", got) fmt.Println(ok) got = "foo bar! biz" ok = t.Re(got, expected, td.Set("biz", "foo", "bar"), "checks value %s", got) fmt.Println(ok) }
Output: true false
Example (CompiledError) ¶
package main import ( "errors" "fmt" "regexp" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := td.NewT(&testing.T{}) expected := regexp.MustCompile("(zip|bar)$") got := errors.New("foo bar") ok := t.Re(got, expected, nil, "checks value %s", got) fmt.Println(ok) }
Output: true
Example (CompiledStringer) ¶
package main import ( "bytes" "fmt" "regexp" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := td.NewT(&testing.T{}) expected := regexp.MustCompile("(zip|bar)$") // bytes.Buffer implements fmt.Stringer got := bytes.NewBufferString("foo bar") ok := t.Re(got, expected, nil, "checks value %s", got) fmt.Println(ok) }
Output: true
Example (Error) ¶
package main import ( "errors" "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := td.NewT(&testing.T{}) got := errors.New("foo bar") ok := t.Re(got, "(zip|bar)$", nil, "checks value %s", got) fmt.Println(ok) }
Output: true
Example (Stringer) ¶
package main import ( "bytes" "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := td.NewT(&testing.T{}) // bytes.Buffer implements fmt.Stringer got := bytes.NewBufferString("foo bar") ok := t.Re(got, "(zip|bar)$", nil, "checks value %s", got) fmt.Println(ok) }
Output: true
func (*T) ReAll ¶
ReAll is a shortcut for:
t.Cmp(got, td.ReAll(reg, capture), args...)
See ReAll for details.
Returns true if the test is OK, false if it fails.
args... are optional and allow to name the test. This name is used in case of failure to qualify the test. If len(args) > 1 and the first item of args is a string and contains a '%' rune then fmt.Fprintf is used to compose the name, else args are passed to fmt.Fprint. Do not forget it is the name of the test, not the reason of a potential failure.
Example (Capture) ¶
package main import ( "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := td.NewT(&testing.T{}) got := "foo bar biz" ok := t.ReAll(got, `(\w+)`, td.Set("biz", "foo", "bar"), "checks value %s", got) fmt.Println(ok) // Matches, but all catured groups do not match Set got = "foo BAR biz" ok = t.ReAll(got, `(\w+)`, td.Set("biz", "foo", "bar"), "checks value %s", got) fmt.Println(ok) }
Output: true false
Example (CaptureComplex) ¶
package main import ( "fmt" "strconv" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := td.NewT(&testing.T{}) got := "11 45 23 56 85 96" ok := t.ReAll(got, `(\d+)`, td.ArrayEach(td.Code(func(num string) bool { n, err := strconv.Atoi(num) return err == nil && n > 10 && n < 100 })), "checks value %s", got) fmt.Println(ok) // Matches, but 11 is not greater than 20 ok = t.ReAll(got, `(\d+)`, td.ArrayEach(td.Code(func(num string) bool { n, err := strconv.Atoi(num) return err == nil && n > 20 && n < 100 })), "checks value %s", got) fmt.Println(ok) }
Output: true false
Example (CompiledCapture) ¶
package main import ( "fmt" "regexp" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := td.NewT(&testing.T{}) expected := regexp.MustCompile(`(\w+)`) got := "foo bar biz" ok := t.ReAll(got, expected, td.Set("biz", "foo", "bar"), "checks value %s", got) fmt.Println(ok) // Matches, but all catured groups do not match Set got = "foo BAR biz" ok = t.ReAll(got, expected, td.Set("biz", "foo", "bar"), "checks value %s", got) fmt.Println(ok) }
Output: true false
Example (CompiledCaptureComplex) ¶
package main import ( "fmt" "regexp" "strconv" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := td.NewT(&testing.T{}) expected := regexp.MustCompile(`(\d+)`) got := "11 45 23 56 85 96" ok := t.ReAll(got, expected, td.ArrayEach(td.Code(func(num string) bool { n, err := strconv.Atoi(num) return err == nil && n > 10 && n < 100 })), "checks value %s", got) fmt.Println(ok) // Matches, but 11 is not greater than 20 ok = t.ReAll(got, expected, td.ArrayEach(td.Code(func(num string) bool { n, err := strconv.Atoi(num) return err == nil && n > 20 && n < 100 })), "checks value %s", got) fmt.Println(ok) }
Output: true false
func (*T) Recv ¶ added in v1.13.0
Recv is a shortcut for:
t.Cmp(got, td.Recv(expectedValue, timeout), args...)
See Recv for details.
Recv optional parameter timeout is here mandatory. 0 value should be passed to mimic its absence in original Recv call.
Returns true if the test is OK, false if it fails.
args... are optional and allow to name the test. This name is used in case of failure to qualify the test. If len(args) > 1 and the first item of args is a string and contains a '%' rune then fmt.Fprintf is used to compose the name, else args are passed to fmt.Fprint. Do not forget it is the name of the test, not the reason of a potential failure.
Example (Basic) ¶
package main import ( "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := td.NewT(&testing.T{}) got := make(chan int, 3) ok := t.Recv(got, td.RecvNothing, 0) fmt.Println("nothing to receive:", ok) got <- 1 got <- 2 got <- 3 close(got) ok = t.Recv(got, 1, 0) fmt.Println("1st receive is 1:", ok) ok = t.Cmp(got, td.All( td.Recv(2), td.Recv(td.Between(3, 4)), td.Recv(td.RecvClosed), )) fmt.Println("next receives are 2, 3 then closed:", ok) ok = t.Recv(got, td.RecvNothing, 0) fmt.Println("nothing to receive:", ok) }
Output: nothing to receive: true 1st receive is 1: true next receives are 2, 3 then closed: true nothing to receive: false
Example (ChannelPointer) ¶
package main import ( "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := td.NewT(&testing.T{}) got := make(chan int, 3) ok := t.Recv(got, td.RecvNothing, 0) fmt.Println("nothing to receive:", ok) got <- 1 got <- 2 got <- 3 close(got) ok = t.Recv(&got, 1, 0) fmt.Println("1st receive is 1:", ok) ok = t.Cmp(&got, td.All( td.Recv(2), td.Recv(td.Between(3, 4)), td.Recv(td.RecvClosed), )) fmt.Println("next receives are 2, 3 then closed:", ok) ok = t.Recv(got, td.RecvNothing, 0) fmt.Println("nothing to receive:", ok) }
Output: nothing to receive: true 1st receive is 1: true next receives are 2, 3 then closed: true nothing to receive: false
Example (NilChannel) ¶
package main import ( "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := td.NewT(&testing.T{}) var ch chan int ok := t.Recv(ch, td.RecvNothing, 0) fmt.Println("nothing to receive from nil channel:", ok) ok = t.Recv(ch, 42, 0) fmt.Println("something to receive from nil channel:", ok) ok = t.Recv(ch, td.RecvClosed, 0) fmt.Println("is a nil channel closed:", ok) }
Output: nothing to receive from nil channel: true something to receive from nil channel: false is a nil channel closed: false
Example (WithTimeout) ¶
package main import ( "fmt" "testing" "time" "github.com/maxatome/go-testdeep/td" ) func main() { t := td.NewT(&testing.T{}) got := make(chan int, 1) tick := make(chan struct{}) go func() { // ① <-tick time.Sleep(100 * time.Millisecond) got <- 0 // ② <-tick time.Sleep(100 * time.Millisecond) got <- 1 // ③ <-tick time.Sleep(100 * time.Millisecond) close(got) }() t.Recv(got, td.RecvNothing, 0) // ① tick <- struct{}{} ok := t.Recv(got, td.RecvNothing, 0) fmt.Println("① RecvNothing:", ok) ok = t.Recv(got, 0, 150*time.Millisecond) fmt.Println("① receive 0 w/150ms timeout:", ok) ok = t.Recv(got, td.RecvNothing, 0) fmt.Println("① RecvNothing:", ok) // ② tick <- struct{}{} ok = t.Recv(got, td.RecvNothing, 0) fmt.Println("② RecvNothing:", ok) ok = t.Recv(got, 1, 150*time.Millisecond) fmt.Println("② receive 1 w/150ms timeout:", ok) ok = t.Recv(got, td.RecvNothing, 0) fmt.Println("② RecvNothing:", ok) // ③ tick <- struct{}{} ok = t.Recv(got, td.RecvNothing, 0) fmt.Println("③ RecvNothing:", ok) ok = t.Recv(got, td.RecvClosed, 150*time.Millisecond) fmt.Println("③ check closed w/150ms timeout:", ok) }
Output: ① RecvNothing: true ① receive 0 w/150ms timeout: true ① RecvNothing: true ② RecvNothing: true ② receive 1 w/150ms timeout: true ② RecvNothing: true ③ RecvNothing: true ③ check closed w/150ms timeout: true
func (*T) Require ¶ added in v1.13.0
Require returns a new *T instance inheriting the t config but with FailureIsFatal flag set to true.
It returns a new instance of *T so does not alter the original t
It is a shortcut for:
t.FailureIsFatal(true)
See also T.FailureIsFatal and T.Assert.
func (*T) ResetAnchors ¶
func (t *T) ResetAnchors()
ResetAnchors frees all operators anchored with T.Anchor method. Unless operators anchoring persistence has been enabled with T.SetAnchorsPersist, there is no need to call this method. Anchored operators are automatically freed after each Cmp, CmpDeeply and CmpPanic call (or others methods calling them behind the scene).
See also T.Anchor, T.AnchorsPersistTemporarily, T.DoAnchorsPersist, T.SetAnchorsPersist and AddAnchorableStructType.
func (*T) RootName ¶
RootName changes the name of the got data. By default it is "DATA". For an HTTP response body, it could be "BODY" for example.
It returns a new instance of *T so does not alter the original t and is used as follows:
t.RootName("RECORD"). Struct(record, &Record{ Name: "Bob", Age: 23, }, td.StructFields{ "Id": td.NotZero(), "CreatedAt": td.Between(before, time.Now()), }, "Newly created record")
In case of error for the field Age, the failure message will contain:
RECORD.Age: values differ
Which is more readable than the generic:
DATA.Age: values differ
If "" is passed the name is set to "DATA", the default value.
func (*T) Run ¶ added in v1.6.0
Run runs f as a subtest of t called name.
If t.TB implement a method with the following signature:
(X) Run(string, func(X)) bool
it calls it with a function of its own in which it creates a new instance of *T on the fly before calling f with it.
So if t.TB is a *testing.T or a *testing.B (which is in normal cases), let's quote the testing.T.Run & testing.B.Run documentation: f is called in a separate goroutine and blocks until f returns or calls t.Parallel to become a parallel test. Run reports whether f succeeded (or at least did not fail before calling t.Parallel). Run may be called simultaneously from multiple goroutines, but all such calls must return before the outer test function for t returns.
If this Run() method is not found, it simply logs name then executes f using a new *T instance in the current goroutine. Note that it is only done for convenience.
The t param of f inherits the configuration of the self-reference.
See also T.RunAssertRequire.
func (*T) RunAssertRequire ¶ added in v1.7.0
RunAssertRequire runs f as a subtest of t called name.
If t.TB implement a method with the following signature:
(X) Run(string, func(X)) bool
it calls it with a function of its own in which it creates two new instances of *T using AssertRequire on the fly before calling f with them.
So if t.TB is a *testing.T or a *testing.B (which is in normal cases), let's quote the testing.T.Run & testing.B.Run documentation: f is called in a separate goroutine and blocks until f returns or calls t.Parallel to become a parallel test. Run reports whether f succeeded (or at least did not fail before calling t.Parallel). Run may be called simultaneously from multiple goroutines, but all such calls must return before the outer test function for t returns.
If this Run() method is not found, it simply logs name then executes f using two new instances of *T (built with AssertRequire) in the current goroutine. Note that it is only done for convenience.
The assert and require params of f inherit the configuration of the self-reference, except that a failure is never fatal using assert and always fatal using require.
See also T.Run.
func (*T) SStruct ¶
func (t *T) SStruct(got, model any, expectedFields StructFields, args ...any) bool
SStruct is a shortcut for:
t.Cmp(got, td.SStruct(model, expectedFields), args...)
See SStruct for details.
SStruct optional parameter expectedFields is here mandatory. nil value should be passed to mimic its absence in original SStruct call.
Returns true if the test is OK, false if it fails.
args... are optional and allow to name the test. This name is used in case of failure to qualify the test. If len(args) > 1 and the first item of args is a string and contains a '%' rune then fmt.Fprintf is used to compose the name, else args are passed to fmt.Fprint. Do not forget it is the name of the test, not the reason of a potential failure.
Example ¶
package main import ( "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := td.NewT(&testing.T{}) type Person struct { Name string Age int NumChildren int } got := Person{ Name: "Foobar", Age: 42, NumChildren: 0, } // NumChildren is not listed in expected fields so it must be zero ok := t.SStruct(got, Person{Name: "Foobar"}, td.StructFields{ "Age": td.Between(40, 50), }, "checks %v is the right Person") fmt.Println("Foobar is between 40 & 50:", ok) // Model can be empty got.NumChildren = 3 ok = t.SStruct(got, Person{}, td.StructFields{ "Name": "Foobar", "Age": td.Between(40, 50), "NumChildren": td.Not(0), }, "checks %v is the right Person") fmt.Println("Foobar has some children:", ok) // Works with pointers too ok = t.SStruct(&got, &Person{}, td.StructFields{ "Name": "Foobar", "Age": td.Between(40, 50), "NumChildren": td.Not(0), }, "checks %v is the right Person") fmt.Println("Foobar has some children (using pointer):", ok) // Model does not need to be instanciated ok = t.SStruct(&got, (*Person)(nil), td.StructFields{ "Name": "Foobar", "Age": td.Between(40, 50), "NumChildren": td.Not(0), }, "checks %v is the right Person") fmt.Println("Foobar has some children (using nil model):", ok) }
Output: Foobar is between 40 & 50: true Foobar has some children: true Foobar has some children (using pointer): true Foobar has some children (using nil model): true
Example (Overwrite_model) ¶
package main import ( "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := td.NewT(&testing.T{}) type Person struct { Name string Age int NumChildren int } got := Person{ Name: "Foobar", Age: 42, NumChildren: 3, } ok := t.SStruct(got, Person{ Name: "Foobar", Age: 53, }, td.StructFields{ ">Age": td.Between(40, 50), // ">" to overwrite Age:53 in model "NumChildren": td.Gt(2), }, "checks %v is the right Person") fmt.Println("Foobar is between 40 & 50:", ok) ok = t.SStruct(got, Person{ Name: "Foobar", Age: 53, }, td.StructFields{ "> Age": td.Between(40, 50), // same, ">" can be followed by spaces "NumChildren": td.Gt(2), }, "checks %v is the right Person") fmt.Println("Foobar is between 40 & 50:", ok) }
Output: Foobar is between 40 & 50: true Foobar is between 40 & 50: true
Example (Patterns) ¶
package main import ( "fmt" "testing" "time" "github.com/maxatome/go-testdeep/td" ) func main() { t := td.NewT(&testing.T{}) type Person struct { Firstname string Lastname string Surname string Nickname string CreatedAt time.Time UpdatedAt time.Time DeletedAt *time.Time id int64 secret string } now := time.Now() got := Person{ Firstname: "Maxime", Lastname: "Foo", Surname: "Max", Nickname: "max", CreatedAt: now, UpdatedAt: now, DeletedAt: nil, // not deleted yet id: 2345, secret: "5ecr3T", } ok := t.SStruct(got, Person{Lastname: "Foo"}, td.StructFields{ `DeletedAt`: nil, `= *name`: td.Re(`^(?i)max`), // shell pattern, matches all names except Lastname as in model `=~ At\z`: td.Lte(time.Now()), // regexp, matches CreatedAt & UpdatedAt `! [A-Z]*`: td.Ignore(), // private fields }, "mix shell & regexp patterns") fmt.Println("Patterns match only remaining fields:", ok) ok = t.SStruct(got, Person{Lastname: "Foo"}, td.StructFields{ `DeletedAt`: nil, `1 = *name`: td.Re(`^(?i)max`), // shell pattern, matches all names except Lastname as in model `2 =~ At\z`: td.Lte(time.Now()), // regexp, matches CreatedAt & UpdatedAt `3 !~ ^[A-Z]`: td.Ignore(), // private fields }, "ordered patterns") fmt.Println("Ordered patterns match only remaining fields:", ok) }
Output: Patterns match only remaining fields: true Ordered patterns match only remaining fields: true
func (*T) Set ¶
Set is a shortcut for:
t.Cmp(got, td.Set(expectedItems...), args...)
See Set for details.
Returns true if the test is OK, false if it fails.
args... are optional and allow to name the test. This name is used in case of failure to qualify the test. If len(args) > 1 and the first item of args is a string and contains a '%' rune then fmt.Fprintf is used to compose the name, else args are passed to fmt.Fprint. Do not forget it is the name of the test, not the reason of a potential failure.
Example ¶
package main import ( "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := td.NewT(&testing.T{}) got := []int{1, 3, 5, 8, 8, 1, 2} // Matches as all items are present, ignoring duplicates ok := t.Set(got, []any{1, 2, 3, 5, 8}, "checks all items are present, in any order") fmt.Println(ok) // Duplicates are ignored in a Set ok = t.Set(got, []any{1, 2, 2, 2, 2, 2, 3, 5, 8}, "checks all items are present, in any order") fmt.Println(ok) // Tries its best to not raise an error when a value can be matched // by several Set entries ok = t.Set(got, []any{td.Between(1, 4), 3, td.Between(2, 10)}, "checks all items are present, in any order") fmt.Println(ok) // When expected is already a non-[]any slice, it cannot be // flattened directly using expected... without copying it to a new // []any slice, then use td.Flatten! expected := []int{1, 2, 3, 5, 8} ok = t.Set(got, []any{td.Flatten(expected)}, "checks all expected items are present, in any order") fmt.Println(ok) }
Output: true true true true
func (*T) SetAnchorsPersist ¶
SetAnchorsPersist allows to enable or disable anchors persistence.
See also T.Anchor, T.AnchorsPersistTemporarily, T.DoAnchorsPersist, T.ResetAnchors and AddAnchorableStructType.
func (*T) Shallow ¶
Shallow is a shortcut for:
t.Cmp(got, td.Shallow(expectedPtr), args...)
See Shallow for details.
Returns true if the test is OK, false if it fails.
args... are optional and allow to name the test. This name is used in case of failure to qualify the test. If len(args) > 1 and the first item of args is a string and contains a '%' rune then fmt.Fprintf is used to compose the name, else args are passed to fmt.Fprint. Do not forget it is the name of the test, not the reason of a potential failure.
Example ¶
package main import ( "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := td.NewT(&testing.T{}) type MyStruct struct { Value int } data := MyStruct{Value: 12} got := &data ok := t.Shallow(got, &data, "checks pointers only, not contents") fmt.Println(ok) // Same contents, but not same pointer ok = t.Shallow(got, &MyStruct{Value: 12}, "checks pointers only, not contents") fmt.Println(ok) }
Output: true false
Example (Slice) ¶
package main import ( "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := td.NewT(&testing.T{}) back := []int{1, 2, 3, 1, 2, 3} a := back[:3] b := back[3:] ok := t.Shallow(a, back) fmt.Println("are ≠ but share the same area:", ok) ok = t.Shallow(b, back) fmt.Println("are = but do not point to same area:", ok) }
Output: are ≠ but share the same area: true are = but do not point to same area: false
Example (String) ¶
package main import ( "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := td.NewT(&testing.T{}) back := "foobarfoobar" a := back[:6] b := back[6:] ok := t.Shallow(a, back) fmt.Println("are ≠ but share the same area:", ok) ok = t.Shallow(b, a) fmt.Println("are = but do not point to same area:", ok) }
Output: are ≠ but share the same area: true are = but do not point to same area: false
func (*T) Slice ¶
func (t *T) Slice(got, model any, expectedEntries ArrayEntries, args ...any) bool
Slice is a shortcut for:
t.Cmp(got, td.Slice(model, expectedEntries), args...)
See Slice for details.
Returns true if the test is OK, false if it fails.
args... are optional and allow to name the test. This name is used in case of failure to qualify the test. If len(args) > 1 and the first item of args is a string and contains a '%' rune then fmt.Fprintf is used to compose the name, else args are passed to fmt.Fprint. Do not forget it is the name of the test, not the reason of a potential failure.
Example (Slice) ¶
package main import ( "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := td.NewT(&testing.T{}) got := []int{42, 58, 26} ok := t.Slice(got, []int{42}, td.ArrayEntries{1: 58, 2: td.Ignore()}, "checks slice %v", got) fmt.Println(ok) ok = t.Slice(got, []int{}, td.ArrayEntries{0: 42, 1: 58, 2: td.Ignore()}, "checks slice %v", got) fmt.Println(ok) ok = t.Slice(got, ([]int)(nil), td.ArrayEntries{0: 42, 1: 58, 2: td.Ignore()}, "checks slice %v", got) fmt.Println(ok) }
Output: true true true
Example (TypedSlice) ¶
package main import ( "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := td.NewT(&testing.T{}) type MySlice []int got := MySlice{42, 58, 26} ok := t.Slice(got, MySlice{42}, td.ArrayEntries{1: 58, 2: td.Ignore()}, "checks typed slice %v", got) fmt.Println(ok) ok = t.Slice(&got, &MySlice{42}, td.ArrayEntries{1: 58, 2: td.Ignore()}, "checks pointer on typed slice %v", got) fmt.Println(ok) ok = t.Slice(&got, &MySlice{}, td.ArrayEntries{0: 42, 1: 58, 2: td.Ignore()}, "checks pointer on typed slice %v", got) fmt.Println(ok) ok = t.Slice(&got, (*MySlice)(nil), td.ArrayEntries{0: 42, 1: 58, 2: td.Ignore()}, "checks pointer on typed slice %v", got) fmt.Println(ok) }
Output: true true true true
func (*T) Smuggle ¶
Smuggle is a shortcut for:
t.Cmp(got, td.Smuggle(fn, expectedValue), args...)
See Smuggle for details.
Returns true if the test is OK, false if it fails.
args... are optional and allow to name the test. This name is used in case of failure to qualify the test. If len(args) > 1 and the first item of args is a string and contains a '%' rune then fmt.Fprintf is used to compose the name, else args are passed to fmt.Fprint. Do not forget it is the name of the test, not the reason of a potential failure.
Example (Auto_unmarshal) ¶
package main import ( "encoding/json" "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := td.NewT(&testing.T{}) // Automatically json.Unmarshal to compare got := []byte(`{"a":1,"b":2}`) ok := t.Smuggle(got, func(b json.RawMessage) (r map[string]int, err error) { err = json.Unmarshal(b, &r) return }, map[string]int{ "a": 1, "b": 2, }) fmt.Println("JSON contents is OK:", ok) }
Output: JSON contents is OK: true
Example (Cast) ¶
package main import ( "bytes" "encoding/json" "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := td.NewT(&testing.T{}) // A string containing JSON got := `{ "foo": 123 }` // Automatically cast a string to a json.RawMessage so td.JSON can operate ok := t.Smuggle(got, json.RawMessage{}, td.JSON(`{"foo":123}`)) fmt.Println("JSON contents in string is OK:", ok) // Automatically read from io.Reader to a json.RawMessage ok = t.Smuggle(bytes.NewReader([]byte(got)), json.RawMessage{}, td.JSON(`{"foo":123}`)) fmt.Println("JSON contents just read is OK:", ok) }
Output: JSON contents in string is OK: true JSON contents just read is OK: true
Example (Complex) ¶
package main import ( "fmt" "testing" "time" "github.com/maxatome/go-testdeep/td" ) func main() { t := td.NewT(&testing.T{}) // No end date but a start date and a duration type StartDuration struct { StartDate time.Time Duration time.Duration } // Checks that end date is between 17th and 19th February both at 0h // for each of these durations in hours for _, duration := range []time.Duration{48 * time.Hour, 72 * time.Hour, 96 * time.Hour} { got := StartDuration{ StartDate: time.Date(2018, time.February, 14, 12, 13, 14, 0, time.UTC), Duration: duration, } // Simplest way, but in case of Between() failure, error will be bound // to DATA<smuggled>, not very clear... ok := t.Smuggle(got, func(sd StartDuration) time.Time { return sd.StartDate.Add(sd.Duration) }, td.Between( time.Date(2018, time.February, 17, 0, 0, 0, 0, time.UTC), time.Date(2018, time.February, 19, 0, 0, 0, 0, time.UTC))) fmt.Println(ok) // Name the computed value "ComputedEndDate" to render a Between() failure // more understandable, so error will be bound to DATA.ComputedEndDate ok = t.Smuggle(got, func(sd StartDuration) td.SmuggledGot { return td.SmuggledGot{ Name: "ComputedEndDate", Got: sd.StartDate.Add(sd.Duration), } }, td.Between( time.Date(2018, time.February, 17, 0, 0, 0, 0, time.UTC), time.Date(2018, time.February, 19, 0, 0, 0, 0, time.UTC))) fmt.Println(ok) } }
Output: false false true true true true
Example (Convert) ¶
package main import ( "fmt" "strconv" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := td.NewT(&testing.T{}) got := int64(123) ok := t.Smuggle(got, func(n int64) int { return int(n) }, 123, "checks int64 got against an int value") fmt.Println(ok) ok = t.Smuggle("123", func(numStr string) (int, bool) { n, err := strconv.Atoi(numStr) return n, err == nil }, td.Between(120, 130), "checks that number in %#v is in [120 .. 130]") fmt.Println(ok) ok = t.Smuggle("123", func(numStr string) (int, bool, string) { n, err := strconv.Atoi(numStr) if err != nil { return 0, false, "string must contain a number" } return n, true, "" }, td.Between(120, 130), "checks that number in %#v is in [120 .. 130]") fmt.Println(ok) ok = t.Smuggle("123", func(numStr string) (int, error) { //nolint: gocritic return strconv.Atoi(numStr) }, td.Between(120, 130), "checks that number in %#v is in [120 .. 130]") fmt.Println(ok) // Short version :) ok = t.Smuggle("123", strconv.Atoi, td.Between(120, 130), "checks that number in %#v is in [120 .. 130]") fmt.Println(ok) }
Output: true true true true true
Example (Field_path) ¶
package main import ( "errors" "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := td.NewT(&testing.T{}) type Body struct { Name string Value any } type Request struct { Body *Body } type Transaction struct { Request } type ValueNum struct { Num int } got := &Transaction{ Request: Request{ Body: &Body{ Name: "test", Value: &ValueNum{Num: 123}, }, }, } // Want to check whether Num is between 100 and 200? ok := t.Smuggle(got, func(t *Transaction) (int, error) { if t.Request.Body == nil || t.Request.Body.Value == nil { return 0, errors.New("Request.Body or Request.Body.Value is nil") } if v, ok := t.Request.Body.Value.(*ValueNum); ok && v != nil { return v.Num, nil } return 0, errors.New("Request.Body.Value isn't *ValueNum or nil") }, td.Between(100, 200)) fmt.Println("check Num by hand:", ok) // Same, but automagically generated... ok = t.Smuggle(got, "Request.Body.Value.Num", td.Between(100, 200)) fmt.Println("check Num using a fields-path:", ok) // And as Request is an anonymous field, can be simplified further // as it can be omitted ok = t.Smuggle(got, "Body.Value.Num", td.Between(100, 200)) fmt.Println("check Num using an other fields-path:", ok) // Note that maps and array/slices are supported got.Request.Body.Value = map[string]any{ "foo": []any{ 3: map[int]string{666: "bar"}, }, } ok = t.Smuggle(got, "Body.Value[foo][3][666]", "bar") fmt.Println("check fields-path including maps/slices:", ok) }
Output: check Num by hand: true check Num using a fields-path: true check Num using an other fields-path: true check fields-path including maps/slices: true
Example (Interface) ¶
package main import ( "fmt" "testing" "time" "github.com/maxatome/go-testdeep/td" ) func main() { t := td.NewT(&testing.T{}) gotTime, err := time.Parse(time.RFC3339, "2018-05-23T12:13:14Z") if err != nil { t.Fatal(err) } // Do not check the struct itself, but its stringified form ok := t.Smuggle(gotTime, func(s fmt.Stringer) string { return s.String() }, "2018-05-23 12:13:14 +0000 UTC") fmt.Println("stringified time.Time OK:", ok) // If got does not implement the fmt.Stringer interface, it fails // without calling the Smuggle func type MyTime time.Time ok = t.Smuggle(MyTime(gotTime), func(s fmt.Stringer) string { fmt.Println("Smuggle func called!") return s.String() }, "2018-05-23 12:13:14 +0000 UTC") fmt.Println("stringified MyTime OK:", ok) }
Output: stringified time.Time OK: true stringified MyTime OK: false
Example (Lax) ¶
package main import ( "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := td.NewT(&testing.T{}) // got is an int16 and Smuggle func input is an int64: it is OK got := int(123) ok := t.Smuggle(got, func(n int64) uint32 { return uint32(n) }, uint32(123)) fmt.Println("got int16(123) → smuggle via int64 → uint32(123):", ok) }
Output: got int16(123) → smuggle via int64 → uint32(123): true
func (*T) String ¶
String is a shortcut for:
t.Cmp(got, td.String(expected), args...)
See String for details.
Returns true if the test is OK, false if it fails.
args... are optional and allow to name the test. This name is used in case of failure to qualify the test. If len(args) > 1 and the first item of args is a string and contains a '%' rune then fmt.Fprintf is used to compose the name, else args are passed to fmt.Fprint. Do not forget it is the name of the test, not the reason of a potential failure.
Example ¶
package main import ( "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := td.NewT(&testing.T{}) got := "foobar" ok := t.String(got, "foobar", "checks %s", got) fmt.Println("using string:", ok) ok = t.Cmp([]byte(got), td.String("foobar"), "checks %s", got) fmt.Println("using []byte:", ok) }
Output: using string: true using []byte: true
Example (Error) ¶
package main import ( "errors" "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := td.NewT(&testing.T{}) got := errors.New("foobar") ok := t.String(got, "foobar", "checks %s", got) fmt.Println(ok) }
Output: true
Example (Stringer) ¶
package main import ( "bytes" "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := td.NewT(&testing.T{}) // bytes.Buffer implements fmt.Stringer got := bytes.NewBufferString("foobar") ok := t.String(got, "foobar", "checks %s", got) fmt.Println(ok) }
Output: true
func (*T) Struct ¶
func (t *T) Struct(got, model any, expectedFields StructFields, args ...any) bool
Struct is a shortcut for:
t.Cmp(got, td.Struct(model, expectedFields), args...)
See Struct for details.
Struct optional parameter expectedFields is here mandatory. nil value should be passed to mimic its absence in original Struct call.
Returns true if the test is OK, false if it fails.
args... are optional and allow to name the test. This name is used in case of failure to qualify the test. If len(args) > 1 and the first item of args is a string and contains a '%' rune then fmt.Fprintf is used to compose the name, else args are passed to fmt.Fprint. Do not forget it is the name of the test, not the reason of a potential failure.
Example ¶
package main import ( "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := td.NewT(&testing.T{}) type Person struct { Name string Age int NumChildren int } got := Person{ Name: "Foobar", Age: 42, NumChildren: 3, } // As NumChildren is zero in Struct() call, it is not checked ok := t.Struct(got, Person{Name: "Foobar"}, td.StructFields{ "Age": td.Between(40, 50), }, "checks %v is the right Person") fmt.Println("Foobar is between 40 & 50:", ok) // Model can be empty ok = t.Struct(got, Person{}, td.StructFields{ "Name": "Foobar", "Age": td.Between(40, 50), "NumChildren": td.Not(0), }, "checks %v is the right Person") fmt.Println("Foobar has some children:", ok) // Works with pointers too ok = t.Struct(&got, &Person{}, td.StructFields{ "Name": "Foobar", "Age": td.Between(40, 50), "NumChildren": td.Not(0), }, "checks %v is the right Person") fmt.Println("Foobar has some children (using pointer):", ok) // Model does not need to be instanciated ok = t.Struct(&got, (*Person)(nil), td.StructFields{ "Name": "Foobar", "Age": td.Between(40, 50), "NumChildren": td.Not(0), }, "checks %v is the right Person") fmt.Println("Foobar has some children (using nil model):", ok) }
Output: Foobar is between 40 & 50: true Foobar has some children: true Foobar has some children (using pointer): true Foobar has some children (using nil model): true
Example (Overwrite_model) ¶
package main import ( "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := td.NewT(&testing.T{}) type Person struct { Name string Age int NumChildren int } got := Person{ Name: "Foobar", Age: 42, NumChildren: 3, } ok := t.Struct(got, Person{ Name: "Foobar", Age: 53, }, td.StructFields{ ">Age": td.Between(40, 50), // ">" to overwrite Age:53 in model "NumChildren": td.Gt(2), }, "checks %v is the right Person") fmt.Println("Foobar is between 40 & 50:", ok) ok = t.Struct(got, Person{ Name: "Foobar", Age: 53, }, td.StructFields{ "> Age": td.Between(40, 50), // same, ">" can be followed by spaces "NumChildren": td.Gt(2), }, "checks %v is the right Person") fmt.Println("Foobar is between 40 & 50:", ok) }
Output: Foobar is between 40 & 50: true Foobar is between 40 & 50: true
Example (Patterns) ¶
package main import ( "fmt" "testing" "time" "github.com/maxatome/go-testdeep/td" ) func main() { t := td.NewT(&testing.T{}) type Person struct { Firstname string Lastname string Surname string Nickname string CreatedAt time.Time UpdatedAt time.Time DeletedAt *time.Time } now := time.Now() got := Person{ Firstname: "Maxime", Lastname: "Foo", Surname: "Max", Nickname: "max", CreatedAt: now, UpdatedAt: now, DeletedAt: nil, // not deleted yet } ok := t.Struct(got, Person{Lastname: "Foo"}, td.StructFields{ `DeletedAt`: nil, `= *name`: td.Re(`^(?i)max`), // shell pattern, matches all names except Lastname as in model `=~ At\z`: td.Lte(time.Now()), // regexp, matches CreatedAt & UpdatedAt }, "mix shell & regexp patterns") fmt.Println("Patterns match only remaining fields:", ok) ok = t.Struct(got, Person{Lastname: "Foo"}, td.StructFields{ `DeletedAt`: nil, `1 = *name`: td.Re(`^(?i)max`), // shell pattern, matches all names except Lastname as in model `2 =~ At\z`: td.Lte(time.Now()), // regexp, matches CreatedAt & UpdatedAt }, "ordered patterns") fmt.Println("Ordered patterns match only remaining fields:", ok) }
Output: Patterns match only remaining fields: true Ordered patterns match only remaining fields: true
func (*T) SubBagOf ¶
SubBagOf is a shortcut for:
t.Cmp(got, td.SubBagOf(expectedItems...), args...)
See SubBagOf for details.
Returns true if the test is OK, false if it fails.
args... are optional and allow to name the test. This name is used in case of failure to qualify the test. If len(args) > 1 and the first item of args is a string and contains a '%' rune then fmt.Fprintf is used to compose the name, else args are passed to fmt.Fprint. Do not forget it is the name of the test, not the reason of a potential failure.
Example ¶
package main import ( "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := td.NewT(&testing.T{}) got := []int{1, 3, 5, 8, 8, 1, 2} ok := t.SubBagOf(got, []any{0, 0, 1, 1, 2, 2, 3, 3, 5, 5, 8, 8, 9, 9}, "checks at least all items are present, in any order") fmt.Println(ok) // got contains one 8 too many ok = t.SubBagOf(got, []any{0, 0, 1, 1, 2, 2, 3, 3, 5, 5, 8, 9, 9}, "checks at least all items are present, in any order") fmt.Println(ok) got = []int{1, 3, 5, 2} ok = t.SubBagOf(got, []any{td.Between(0, 3), td.Between(0, 3), td.Between(0, 3), td.Between(0, 3), td.Gt(4), td.Gt(4)}, "checks at least all items match, in any order with TestDeep operators") fmt.Println(ok) // When expected is already a non-[]any slice, it cannot be // flattened directly using expected... without copying it to a new // []any slice, then use td.Flatten! expected := []int{1, 2, 3, 5, 9, 8} ok = t.SubBagOf(got, []any{td.Flatten(expected)}, "checks at least all expected items are present, in any order") fmt.Println(ok) }
Output: true false true true
func (*T) SubJSONOf ¶
SubJSONOf is a shortcut for:
t.Cmp(got, td.SubJSONOf(expectedJSON, params...), args...)
See SubJSONOf for details.
Returns true if the test is OK, false if it fails.
args... are optional and allow to name the test. This name is used in case of failure to qualify the test. If len(args) > 1 and the first item of args is a string and contains a '%' rune then fmt.Fprintf is used to compose the name, else args are passed to fmt.Fprint. Do not forget it is the name of the test, not the reason of a potential failure.
Example (Basic) ¶
package main import ( "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := td.NewT(&testing.T{}) got := &struct { Fullname string `json:"fullname"` Age int `json:"age"` }{ Fullname: "Bob", Age: 42, } ok := t.SubJSONOf(got, `{"age":42,"fullname":"Bob","gender":"male"}`, nil) fmt.Println("check got with age then fullname:", ok) ok = t.SubJSONOf(got, `{"fullname":"Bob","age":42,"gender":"male"}`, nil) fmt.Println("check got with fullname then age:", ok) ok = t.SubJSONOf(got, ` // This should be the JSON representation of a struct { // A person: "fullname": "Bob", // The name of this person "age": 42, /* The age of this person: - 42 of course - to demonstrate a multi-lines comment */ "gender": "male" // This field is ignored as SubJSONOf }`, nil) fmt.Println("check got with nicely formatted and commented JSON:", ok) ok = t.SubJSONOf(got, `{"fullname":"Bob","gender":"male"}`, nil) fmt.Println("check got without age field:", ok) }
Output: check got with age then fullname: true check got with fullname then age: true check got with nicely formatted and commented JSON: true check got without age field: false
Example (File) ¶
package main import ( "fmt" "os" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := td.NewT(&testing.T{}) got := &struct { Fullname string `json:"fullname"` Age int `json:"age"` Gender string `json:"gender"` }{ Fullname: "Bob Foobar", Age: 42, Gender: "male", } tmpDir, err := os.MkdirTemp("", "") if err != nil { t.Fatal(err) } defer os.RemoveAll(tmpDir) // clean up filename := tmpDir + "/test.json" if err = os.WriteFile(filename, []byte(` { "fullname": "$name", "age": "$age", "gender": "$gender", "details": { "city": "TestCity", "zip": 666 } }`), 0644); err != nil { t.Fatal(err) } // OK let's test with this file ok := t.SubJSONOf(got, filename, []any{td.Tag("name", td.HasPrefix("Bob")), td.Tag("age", td.Between(40, 45)), td.Tag("gender", td.Re(`^(male|female)\z`))}) fmt.Println("Full match from file name:", ok) // When the file is already open file, err := os.Open(filename) if err != nil { t.Fatal(err) } ok = t.SubJSONOf(got, file, []any{td.Tag("name", td.HasPrefix("Bob")), td.Tag("age", td.Between(40, 45)), td.Tag("gender", td.Re(`^(male|female)\z`))}) fmt.Println("Full match from io.Reader:", ok) }
Output: Full match from file name: true Full match from io.Reader: true
Example (Placeholders) ¶
package main import ( "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := td.NewT(&testing.T{}) got := &struct { Fullname string `json:"fullname"` Age int `json:"age"` }{ Fullname: "Bob Foobar", Age: 42, } ok := t.SubJSONOf(got, `{"age": $1, "fullname": $2, "gender": $3}`, []any{42, "Bob Foobar", "male"}) fmt.Println("check got with numeric placeholders without operators:", ok) ok = t.SubJSONOf(got, `{"age": $1, "fullname": $2, "gender": $3}`, []any{td.Between(40, 45), td.HasSuffix("Foobar"), td.NotEmpty()}) fmt.Println("check got with numeric placeholders:", ok) ok = t.SubJSONOf(got, `{"age": "$1", "fullname": "$2", "gender": "$3"}`, []any{td.Between(40, 45), td.HasSuffix("Foobar"), td.NotEmpty()}) fmt.Println("check got with double-quoted numeric placeholders:", ok) ok = t.SubJSONOf(got, `{"age": $age, "fullname": $name, "gender": $gender}`, []any{td.Tag("age", td.Between(40, 45)), td.Tag("name", td.HasSuffix("Foobar")), td.Tag("gender", td.NotEmpty())}) fmt.Println("check got with named placeholders:", ok) ok = t.SubJSONOf(got, `{"age": $^NotZero, "fullname": $^NotEmpty, "gender": $^NotEmpty}`, nil) fmt.Println("check got with operator shortcuts:", ok) }
Output: check got with numeric placeholders without operators: true check got with numeric placeholders: true check got with double-quoted numeric placeholders: true check got with named placeholders: true check got with operator shortcuts: true
func (*T) SubMapOf ¶
func (t *T) SubMapOf(got, model any, expectedEntries MapEntries, args ...any) bool
SubMapOf is a shortcut for:
t.Cmp(got, td.SubMapOf(model, expectedEntries), args...)
See SubMapOf for details.
Returns true if the test is OK, false if it fails.
args... are optional and allow to name the test. This name is used in case of failure to qualify the test. If len(args) > 1 and the first item of args is a string and contains a '%' rune then fmt.Fprintf is used to compose the name, else args are passed to fmt.Fprint. Do not forget it is the name of the test, not the reason of a potential failure.
Example (Map) ¶
package main import ( "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := td.NewT(&testing.T{}) got := map[string]int{"foo": 12, "bar": 42} ok := t.SubMapOf(got, map[string]int{"bar": 42}, td.MapEntries{"foo": td.Lt(15), "zip": 666}, "checks map %v is included in expected keys/values", got) fmt.Println(ok) }
Output: true
Example (TypedMap) ¶
package main import ( "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := td.NewT(&testing.T{}) type MyMap map[string]int got := MyMap{"foo": 12, "bar": 42} ok := t.SubMapOf(got, MyMap{"bar": 42}, td.MapEntries{"foo": td.Lt(15), "zip": 666}, "checks typed map %v is included in expected keys/values", got) fmt.Println(ok) ok = t.SubMapOf(&got, &MyMap{"bar": 42}, td.MapEntries{"foo": td.Lt(15), "zip": 666}, "checks pointed typed map %v is included in expected keys/values", got) fmt.Println(ok) }
Output: true true
func (*T) SubSetOf ¶
SubSetOf is a shortcut for:
t.Cmp(got, td.SubSetOf(expectedItems...), args...)
See SubSetOf for details.
Returns true if the test is OK, false if it fails.
args... are optional and allow to name the test. This name is used in case of failure to qualify the test. If len(args) > 1 and the first item of args is a string and contains a '%' rune then fmt.Fprintf is used to compose the name, else args are passed to fmt.Fprint. Do not forget it is the name of the test, not the reason of a potential failure.
Example ¶
package main import ( "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := td.NewT(&testing.T{}) got := []int{1, 3, 5, 8, 8, 1, 2} // Matches as all items are expected, ignoring duplicates ok := t.SubSetOf(got, []any{1, 2, 3, 4, 5, 6, 7, 8}, "checks at least all items are present, in any order, ignoring duplicates") fmt.Println(ok) // Tries its best to not raise an error when a value can be matched // by several SubSetOf entries ok = t.SubSetOf(got, []any{td.Between(1, 4), 3, td.Between(2, 10), td.Gt(100)}, "checks at least all items are present, in any order, ignoring duplicates") fmt.Println(ok) // When expected is already a non-[]any slice, it cannot be // flattened directly using expected... without copying it to a new // []any slice, then use td.Flatten! expected := []int{1, 2, 3, 4, 5, 6, 7, 8} ok = t.SubSetOf(got, []any{td.Flatten(expected)}, "checks at least all expected items are present, in any order, ignoring duplicates") fmt.Println(ok) }
Output: true true true
func (*T) SuperBagOf ¶
SuperBagOf is a shortcut for:
t.Cmp(got, td.SuperBagOf(expectedItems...), args...)
See SuperBagOf for details.
Returns true if the test is OK, false if it fails.
args... are optional and allow to name the test. This name is used in case of failure to qualify the test. If len(args) > 1 and the first item of args is a string and contains a '%' rune then fmt.Fprintf is used to compose the name, else args are passed to fmt.Fprint. Do not forget it is the name of the test, not the reason of a potential failure.
Example ¶
package main import ( "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := td.NewT(&testing.T{}) got := []int{1, 3, 5, 8, 8, 1, 2} ok := t.SuperBagOf(got, []any{8, 5, 8}, "checks the items are present, in any order") fmt.Println(ok) ok = t.SuperBagOf(got, []any{td.Gt(5), td.Lte(2)}, "checks at least 2 items of %v match", got) fmt.Println(ok) // When expected is already a non-[]any slice, it cannot be // flattened directly using expected... without copying it to a new // []any slice, then use td.Flatten! expected := []int{8, 5, 8} ok = t.SuperBagOf(got, []any{td.Flatten(expected)}, "checks the expected items are present, in any order") fmt.Println(ok) }
Output: true true true
func (*T) SuperJSONOf ¶
SuperJSONOf is a shortcut for:
t.Cmp(got, td.SuperJSONOf(expectedJSON, params...), args...)
See SuperJSONOf for details.
Returns true if the test is OK, false if it fails.
args... are optional and allow to name the test. This name is used in case of failure to qualify the test. If len(args) > 1 and the first item of args is a string and contains a '%' rune then fmt.Fprintf is used to compose the name, else args are passed to fmt.Fprint. Do not forget it is the name of the test, not the reason of a potential failure.
Example (Basic) ¶
package main import ( "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := td.NewT(&testing.T{}) got := &struct { Fullname string `json:"fullname"` Age int `json:"age"` Gender string `json:"gender"` City string `json:"city"` Zip int `json:"zip"` }{ Fullname: "Bob", Age: 42, Gender: "male", City: "TestCity", Zip: 666, } ok := t.SuperJSONOf(got, `{"age":42,"fullname":"Bob","gender":"male"}`, nil) fmt.Println("check got with age then fullname:", ok) ok = t.SuperJSONOf(got, `{"fullname":"Bob","age":42,"gender":"male"}`, nil) fmt.Println("check got with fullname then age:", ok) ok = t.SuperJSONOf(got, ` // This should be the JSON representation of a struct { // A person: "fullname": "Bob", // The name of this person "age": 42, /* The age of this person: - 42 of course - to demonstrate a multi-lines comment */ "gender": "male" // The gender! }`, nil) fmt.Println("check got with nicely formatted and commented JSON:", ok) ok = t.SuperJSONOf(got, `{"fullname":"Bob","gender":"male","details":{}}`, nil) fmt.Println("check got with details field:", ok) }
Output: check got with age then fullname: true check got with fullname then age: true check got with nicely formatted and commented JSON: true check got with details field: false
Example (File) ¶
package main import ( "fmt" "os" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := td.NewT(&testing.T{}) got := &struct { Fullname string `json:"fullname"` Age int `json:"age"` Gender string `json:"gender"` City string `json:"city"` Zip int `json:"zip"` }{ Fullname: "Bob Foobar", Age: 42, Gender: "male", City: "TestCity", Zip: 666, } tmpDir, err := os.MkdirTemp("", "") if err != nil { t.Fatal(err) } defer os.RemoveAll(tmpDir) // clean up filename := tmpDir + "/test.json" if err = os.WriteFile(filename, []byte(` { "fullname": "$name", "age": "$age", "gender": "$gender" }`), 0644); err != nil { t.Fatal(err) } // OK let's test with this file ok := t.SuperJSONOf(got, filename, []any{td.Tag("name", td.HasPrefix("Bob")), td.Tag("age", td.Between(40, 45)), td.Tag("gender", td.Re(`^(male|female)\z`))}) fmt.Println("Full match from file name:", ok) // When the file is already open file, err := os.Open(filename) if err != nil { t.Fatal(err) } ok = t.SuperJSONOf(got, file, []any{td.Tag("name", td.HasPrefix("Bob")), td.Tag("age", td.Between(40, 45)), td.Tag("gender", td.Re(`^(male|female)\z`))}) fmt.Println("Full match from io.Reader:", ok) }
Output: Full match from file name: true Full match from io.Reader: true
Example (Placeholders) ¶
package main import ( "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := td.NewT(&testing.T{}) got := &struct { Fullname string `json:"fullname"` Age int `json:"age"` Gender string `json:"gender"` City string `json:"city"` Zip int `json:"zip"` }{ Fullname: "Bob Foobar", Age: 42, Gender: "male", City: "TestCity", Zip: 666, } ok := t.SuperJSONOf(got, `{"age": $1, "fullname": $2, "gender": $3}`, []any{42, "Bob Foobar", "male"}) fmt.Println("check got with numeric placeholders without operators:", ok) ok = t.SuperJSONOf(got, `{"age": $1, "fullname": $2, "gender": $3}`, []any{td.Between(40, 45), td.HasSuffix("Foobar"), td.NotEmpty()}) fmt.Println("check got with numeric placeholders:", ok) ok = t.SuperJSONOf(got, `{"age": "$1", "fullname": "$2", "gender": "$3"}`, []any{td.Between(40, 45), td.HasSuffix("Foobar"), td.NotEmpty()}) fmt.Println("check got with double-quoted numeric placeholders:", ok) ok = t.SuperJSONOf(got, `{"age": $age, "fullname": $name, "gender": $gender}`, []any{td.Tag("age", td.Between(40, 45)), td.Tag("name", td.HasSuffix("Foobar")), td.Tag("gender", td.NotEmpty())}) fmt.Println("check got with named placeholders:", ok) ok = t.SuperJSONOf(got, `{"age": $^NotZero, "fullname": $^NotEmpty, "gender": $^NotEmpty}`, nil) fmt.Println("check got with operator shortcuts:", ok) }
Output: check got with numeric placeholders without operators: true check got with numeric placeholders: true check got with double-quoted numeric placeholders: true check got with named placeholders: true check got with operator shortcuts: true
func (*T) SuperMapOf ¶
func (t *T) SuperMapOf(got, model any, expectedEntries MapEntries, args ...any) bool
SuperMapOf is a shortcut for:
t.Cmp(got, td.SuperMapOf(model, expectedEntries), args...)
See SuperMapOf for details.
Returns true if the test is OK, false if it fails.
args... are optional and allow to name the test. This name is used in case of failure to qualify the test. If len(args) > 1 and the first item of args is a string and contains a '%' rune then fmt.Fprintf is used to compose the name, else args are passed to fmt.Fprint. Do not forget it is the name of the test, not the reason of a potential failure.
Example (Map) ¶
package main import ( "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := td.NewT(&testing.T{}) got := map[string]int{"foo": 12, "bar": 42, "zip": 89} ok := t.SuperMapOf(got, map[string]int{"bar": 42}, td.MapEntries{"foo": td.Lt(15)}, "checks map %v contains at leat all expected keys/values", got) fmt.Println(ok) }
Output: true
Example (TypedMap) ¶
package main import ( "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := td.NewT(&testing.T{}) type MyMap map[string]int got := MyMap{"foo": 12, "bar": 42, "zip": 89} ok := t.SuperMapOf(got, MyMap{"bar": 42}, td.MapEntries{"foo": td.Lt(15)}, "checks typed map %v contains at leat all expected keys/values", got) fmt.Println(ok) ok = t.SuperMapOf(&got, &MyMap{"bar": 42}, td.MapEntries{"foo": td.Lt(15)}, "checks pointed typed map %v contains at leat all expected keys/values", got) fmt.Println(ok) }
Output: true true
func (*T) SuperSetOf ¶
SuperSetOf is a shortcut for:
t.Cmp(got, td.SuperSetOf(expectedItems...), args...)
See SuperSetOf for details.
Returns true if the test is OK, false if it fails.
args... are optional and allow to name the test. This name is used in case of failure to qualify the test. If len(args) > 1 and the first item of args is a string and contains a '%' rune then fmt.Fprintf is used to compose the name, else args are passed to fmt.Fprint. Do not forget it is the name of the test, not the reason of a potential failure.
Example ¶
package main import ( "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := td.NewT(&testing.T{}) got := []int{1, 3, 5, 8, 8, 1, 2} ok := t.SuperSetOf(got, []any{1, 2, 3}, "checks the items are present, in any order and ignoring duplicates") fmt.Println(ok) ok = t.SuperSetOf(got, []any{td.Gt(5), td.Lte(2)}, "checks at least 2 items of %v match ignoring duplicates", got) fmt.Println(ok) // When expected is already a non-[]any slice, it cannot be // flattened directly using expected... without copying it to a new // []any slice, then use td.Flatten! expected := []int{1, 2, 3} ok = t.SuperSetOf(got, []any{td.Flatten(expected)}, "checks the expected items are present, in any order and ignoring duplicates") fmt.Println(ok) }
Output: true true true
func (*T) SuperSliceOf ¶ added in v1.10.0
func (t *T) SuperSliceOf(got, model any, expectedEntries ArrayEntries, args ...any) bool
SuperSliceOf is a shortcut for:
t.Cmp(got, td.SuperSliceOf(model, expectedEntries), args...)
See SuperSliceOf for details.
Returns true if the test is OK, false if it fails.
args... are optional and allow to name the test. This name is used in case of failure to qualify the test. If len(args) > 1 and the first item of args is a string and contains a '%' rune then fmt.Fprintf is used to compose the name, else args are passed to fmt.Fprint. Do not forget it is the name of the test, not the reason of a potential failure.
Example (Array) ¶
package main import ( "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := td.NewT(&testing.T{}) got := [4]int{42, 58, 26, 666} ok := t.SuperSliceOf(got, [4]int{1: 58}, td.ArrayEntries{3: td.Gt(660)}, "checks array %v", got) fmt.Println("Only check items #1 & #3:", ok) ok = t.SuperSliceOf(got, [4]int{}, td.ArrayEntries{0: 42, 3: td.Between(660, 670)}, "checks array %v", got) fmt.Println("Only check items #0 & #3:", ok) ok = t.SuperSliceOf(&got, &[4]int{}, td.ArrayEntries{0: 42, 3: td.Between(660, 670)}, "checks array %v", got) fmt.Println("Only check items #0 & #3 of an array pointer:", ok) ok = t.SuperSliceOf(&got, (*[4]int)(nil), td.ArrayEntries{0: 42, 3: td.Between(660, 670)}, "checks array %v", got) fmt.Println("Only check items #0 & #3 of an array pointer, using nil model:", ok) }
Output: Only check items #1 & #3: true Only check items #0 & #3: true Only check items #0 & #3 of an array pointer: true Only check items #0 & #3 of an array pointer, using nil model: true
Example (Slice) ¶
package main import ( "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := td.NewT(&testing.T{}) got := []int{42, 58, 26, 666} ok := t.SuperSliceOf(got, []int{1: 58}, td.ArrayEntries{3: td.Gt(660)}, "checks array %v", got) fmt.Println("Only check items #1 & #3:", ok) ok = t.SuperSliceOf(got, []int{}, td.ArrayEntries{0: 42, 3: td.Between(660, 670)}, "checks array %v", got) fmt.Println("Only check items #0 & #3:", ok) ok = t.SuperSliceOf(&got, &[]int{}, td.ArrayEntries{0: 42, 3: td.Between(660, 670)}, "checks array %v", got) fmt.Println("Only check items #0 & #3 of a slice pointer:", ok) ok = t.SuperSliceOf(&got, (*[]int)(nil), td.ArrayEntries{0: 42, 3: td.Between(660, 670)}, "checks array %v", got) fmt.Println("Only check items #0 & #3 of a slice pointer, using nil model:", ok) }
Output: Only check items #1 & #3: true Only check items #0 & #3: true Only check items #0 & #3 of a slice pointer: true Only check items #0 & #3 of a slice pointer, using nil model: true
Example (TypedArray) ¶
package main import ( "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := td.NewT(&testing.T{}) type MyArray [4]int got := MyArray{42, 58, 26, 666} ok := t.SuperSliceOf(got, MyArray{1: 58}, td.ArrayEntries{3: td.Gt(660)}, "checks typed array %v", got) fmt.Println("Only check items #1 & #3:", ok) ok = t.SuperSliceOf(got, MyArray{}, td.ArrayEntries{0: 42, 3: td.Between(660, 670)}, "checks array %v", got) fmt.Println("Only check items #0 & #3:", ok) ok = t.SuperSliceOf(&got, &MyArray{}, td.ArrayEntries{0: 42, 3: td.Between(660, 670)}, "checks array %v", got) fmt.Println("Only check items #0 & #3 of an array pointer:", ok) ok = t.SuperSliceOf(&got, (*MyArray)(nil), td.ArrayEntries{0: 42, 3: td.Between(660, 670)}, "checks array %v", got) fmt.Println("Only check items #0 & #3 of an array pointer, using nil model:", ok) }
Output: Only check items #1 & #3: true Only check items #0 & #3: true Only check items #0 & #3 of an array pointer: true Only check items #0 & #3 of an array pointer, using nil model: true
Example (TypedSlice) ¶
package main import ( "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := td.NewT(&testing.T{}) type MySlice []int got := MySlice{42, 58, 26, 666} ok := t.SuperSliceOf(got, MySlice{1: 58}, td.ArrayEntries{3: td.Gt(660)}, "checks typed array %v", got) fmt.Println("Only check items #1 & #3:", ok) ok = t.SuperSliceOf(got, MySlice{}, td.ArrayEntries{0: 42, 3: td.Between(660, 670)}, "checks array %v", got) fmt.Println("Only check items #0 & #3:", ok) ok = t.SuperSliceOf(&got, &MySlice{}, td.ArrayEntries{0: 42, 3: td.Between(660, 670)}, "checks array %v", got) fmt.Println("Only check items #0 & #3 of a slice pointer:", ok) ok = t.SuperSliceOf(&got, (*MySlice)(nil), td.ArrayEntries{0: 42, 3: td.Between(660, 670)}, "checks array %v", got) fmt.Println("Only check items #0 & #3 of a slice pointer, using nil model:", ok) }
Output: Only check items #1 & #3: true Only check items #0 & #3: true Only check items #0 & #3 of a slice pointer: true Only check items #0 & #3 of a slice pointer, using nil model: true
func (*T) TestDeepInGotOK ¶ added in v1.13.0
TestDeepInGotOK tells go-testdeep to not panic when a TestDeep operator is found on got side. By default it is forbidden because most of the time it is a mistake to compare (expected, got) instead of official (got, expected).
It returns a new instance of *T so does not alter the original t.
Note that t.TestDeepInGotOK() acts as t.TestDeepInGotOK(true).
func (*T) True ¶
True is shortcut for:
t.Cmp(got, true, args...)
Returns true if the test is OK, false if it fails.
t.True(IsAvailable(x), "x should be available")
args... are optional and allow to name the test. This name is used in case of failure to qualify the test. If len(args) > 1 and the first item of args is a string and contains a '%' rune then fmt.Fprintf is used to compose the name, else args are passed to fmt.Fprint. Do not forget it is the name of the test, not the reason of a potential failure.
See also T.False.
Example ¶
package main import ( "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := td.NewT(&testing.T{}) got := true ok := t.True(got, "check that got is true!") fmt.Println(ok) got = false ok = t.True(got, "check that got is true!") fmt.Println(ok) }
Output: true false
func (*T) TruncTime ¶
TruncTime is a shortcut for:
t.Cmp(got, td.TruncTime(expectedTime, trunc), args...)
See TruncTime for details.
TruncTime optional parameter trunc is here mandatory. 0 value should be passed to mimic its absence in original TruncTime call.
Returns true if the test is OK, false if it fails.
args... are optional and allow to name the test. This name is used in case of failure to qualify the test. If len(args) > 1 and the first item of args is a string and contains a '%' rune then fmt.Fprintf is used to compose the name, else args are passed to fmt.Fprint. Do not forget it is the name of the test, not the reason of a potential failure.
Example ¶
package main import ( "fmt" "testing" "time" "github.com/maxatome/go-testdeep/td" ) func main() { t := td.NewT(&testing.T{}) dateToTime := func(str string) time.Time { t, err := time.Parse(time.RFC3339Nano, str) if err != nil { panic(err) } return t } got := dateToTime("2018-05-01T12:45:53.123456789Z") // Compare dates ignoring nanoseconds and monotonic parts expected := dateToTime("2018-05-01T12:45:53Z") ok := t.TruncTime(got, expected, time.Second, "checks date %v, truncated to the second", got) fmt.Println(ok) // Compare dates ignoring time and so monotonic parts expected = dateToTime("2018-05-01T11:22:33.444444444Z") ok = t.TruncTime(got, expected, 24*time.Hour, "checks date %v, truncated to the day", got) fmt.Println(ok) // Compare dates exactly but ignoring monotonic part expected = dateToTime("2018-05-01T12:45:53.123456789Z") ok = t.TruncTime(got, expected, 0, "checks date %v ignoring monotonic part", got) fmt.Println(ok) }
Output: true true true
func (*T) UseEqual ¶
UseEqual tells go-testdeep to delegate the comparison of items whose type is one of types to their Equal() method.
The signature this method should be:
(A) Equal(B) bool
with B assignable to A.
See time.Time.Equal as an example of accepted Equal() method.
It always returns a new instance of *T so does not alter the original t.
t = t.UseEqual(time.Time{}, net.IP{})
types items can also be reflect.Type items. In this case, the target type is the one reflected by the reflect.Type.
t = t.UseEqual(reflect.TypeOf(time.Time{}), reflect.typeOf(net.IP{}))
As a special case, calling t.UseEqual() or t.UseEqual(true) returns an instance using the Equal() method globally, for all types owning an Equal() method. Other types fall back to the default comparison mechanism. t.UseEqual(false) returns an instance not using Equal() method anymore, except for types already recorded using a previous UseEqual call.
func (*T) Values ¶
Values is a shortcut for:
t.Cmp(got, td.Values(val), args...)
See Values for details.
Returns true if the test is OK, false if it fails.
args... are optional and allow to name the test. This name is used in case of failure to qualify the test. If len(args) > 1 and the first item of args is a string and contains a '%' rune then fmt.Fprintf is used to compose the name, else args are passed to fmt.Fprint. Do not forget it is the name of the test, not the reason of a potential failure.
Example ¶
package main import ( "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := td.NewT(&testing.T{}) got := map[string]int{"foo": 1, "bar": 2, "zip": 3} // Values tests values in an ordered manner ok := t.Values(got, []int{1, 2, 3}) fmt.Println("All sorted values are found:", ok) // If the expected values are not ordered, it fails ok = t.Values(got, []int{3, 1, 2}) fmt.Println("All unsorted values are found:", ok) // To circumvent that, one can use Bag operator ok = t.Values(got, td.Bag(3, 1, 2)) fmt.Println("All unsorted values are found, with the help of Bag operator:", ok) // Check that each value is between 1 and 3 ok = t.Values(got, td.ArrayEach(td.Between(1, 3))) fmt.Println("Each value is between 1 and 3:", ok) }
Output: All sorted values are found: true All unsorted values are found: false All unsorted values are found, with the help of Bag operator: true Each value is between 1 and 3: true
func (*T) WithCmpHooks ¶ added in v1.8.0
WithCmpHooks returns a new *T instance with new Cmp hooks recorded using functions passed in fns.
Each function in fns has to be a function with the following possible signatures:
func (got A, expected A) bool func (got A, expected A) error
First arg is always got, and second is always expected.
A cannot be an interface. This restriction can be removed in the future, if really needed.
This function is called as soon as possible each time the type A is encountered for got while expected type is assignable to A.
When it returns a bool, false means A is not equal to B.
When it returns a non-nil error (meaning got ≠ expected), its content is used to tell the reason of the failure.
Cmp hooks are checked before UseEqual feature.
Cmp hooks are run just after Smuggle hooks.
func TestCmpHook(tt *testing.T) { t := td.NewT(tt) // Test reflect.Value contents instead of default field/field t = t.WithCmpHooks(func (got, expected reflect.Value) bool { return td.EqDeeply(got.Interface(), expected.Interface()) }) a, b := 1, 1 t.Cmp(reflect.ValueOf(&a), reflect.ValueOf(&b)) // succeeds // Test reflect.Type correctly instead of default field/field t = t.WithCmpHooks(func (got, expected reflect.Type) bool { return got == expected }) // Test time.Time via its Equal() method instead of default // field/field (note it bypasses the UseEqual flag) t = t.WithCmpHooks((time.Time).Equal) date, _ := time.Parse(time.RFC3339, "2020-09-08T22:13:54+02:00") t.Cmp(date, date.UTC()) // succeeds // Several hooks can be declared at once t = t.WithCmpHooks( func (got, expected reflect.Value) bool { return td.EqDeeply(got.Interface(), expected.Interface()) }, func (got, expected reflect.Type) bool { return got == expected }, (time.Time).Equal, ) }
There is no way to add or remove hooks of an existing *T instance, only to create a new *T instance with this method or T.WithSmuggleHooks to add some.
WithCmpHooks calls t.Fatal if an item of fns is not a function or if its signature does not match the expected ones.
See also T.WithSmuggleHooks.
func (*T) WithSmuggleHooks ¶ added in v1.8.0
WithSmuggleHooks returns a new *T instance with new Smuggle hooks recorded using functions passed in fns.
Each function in fns has to be a function with the following possible signatures:
func (got A) B func (got A) (B, error)
A cannot be an interface. This restriction can be removed in the future, if really needed.
B cannot be an interface. If you have a use case, we can talk about it.
This function is called as soon as possible each time the type A is encountered for got.
The B value returned replaces the got value for subsequent tests. Smuggle hooks are NOT run again for this returned value to avoid easy infinite loop recursion.
When it returns non-nil error (meaning something wrong happened during the conversion of A to B), it raises a global error and its content is used to tell the reason of the failure.
Smuggle hooks are run just before Cmp hooks.
func TestSmuggleHook(tt *testing.T) { t := td.NewT(tt) // Each encountered int is changed to a bool t = t.WithSmuggleHooks(func (got int) bool { return got != 0 }) t.Cmp(map[string]int{"ok": 1, "no": 0}, map[string]bool{"ok", true, "no", false}) // succeeds // Each encountered string is converted to int t = t.WithSmuggleHooks(strconv.Atoi) t.Cmp("123", 123) // succeeds // Several hooks can be declared at once t = t.WithSmuggleHooks( func (got int) bool { return got != 0 }, strconv.Atoi, ) }
There is no way to add or remove hooks of an existing *T instance, only create a new *T instance with this method or T.WithCmpHooks to add some.
WithSmuggleHooks calls t.Fatal if an item of fns is not a function or if its signature does not match the expected ones.
See also T.WithCmpHooks.
func (*T) Zero ¶
Zero is a shortcut for:
t.Cmp(got, td.Zero(), args...)
See Zero for details.
Returns true if the test is OK, false if it fails.
args... are optional and allow to name the test. This name is used in case of failure to qualify the test. If len(args) > 1 and the first item of args is a string and contains a '%' rune then fmt.Fprintf is used to compose the name, else args are passed to fmt.Fprint. Do not forget it is the name of the test, not the reason of a potential failure.
Example ¶
package main import ( "bytes" "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := td.NewT(&testing.T{}) ok := t.Zero(0) fmt.Println(ok) ok = t.Zero(float64(0)) fmt.Println(ok) ok = t.Zero(12) // fails, as 12 is not 0 :) fmt.Println(ok) ok = t.Zero((map[string]int)(nil)) fmt.Println(ok) ok = t.Zero(map[string]int{}) // fails, as not nil fmt.Println(ok) ok = t.Zero(([]int)(nil)) fmt.Println(ok) ok = t.Zero([]int{}) // fails, as not nil fmt.Println(ok) ok = t.Zero([3]int{}) fmt.Println(ok) ok = t.Zero([3]int{0, 1}) // fails, DATA[1] is not 0 fmt.Println(ok) ok = t.Zero(bytes.Buffer{}) fmt.Println(ok) ok = t.Zero(&bytes.Buffer{}) // fails, as pointer not nil fmt.Println(ok) ok = t.Cmp(&bytes.Buffer{}, td.Ptr(td.Zero())) // OK with the help of Ptr() fmt.Println(ok) }
Output: true true false true false true false true false true false true
type TestDeep ¶
type TestDeep interface { types.TestDeepStringer location.GetLocationer // Match checks got against the operator. It returns nil if it matches. Match(ctx ctxerr.Context, got reflect.Value) *ctxerr.Error // HandleInvalid returns true if the operator is able to handle // untyped nil value. Otherwise the untyped nil value is handled // generically. HandleInvalid() bool // TypeBehind returns the type handled by the operator or nil if it // is not known. tdhttp helper uses it to know how to unmarshal HTTP // responses bodies before comparing them using the operator. TypeBehind() reflect.Type // Error returns nil if the operator is operational, the // corresponding error otherwise. Error() error // contains filtered or unexported methods }
TestDeep is the representation of a go-testdeep operator. It is not intended to be used directly, but through Cmp* functions.
func All ¶
All operator compares data against several expected values. During a match, all of them have to match to succeed. Consider it as a "AND" logical operator.
td.Cmp(t, "foobar", td.All( td.Len(6), td.HasPrefix("fo"), td.HasSuffix("ar"), )) // succeeds
Note Flatten function can be used to group or reuse some values or operators and so avoid boring and inefficient copies:
stringOps := td.Flatten([]td.TestDeep{td.HasPrefix("fo"), td.HasSuffix("ar")}) td.Cmp(t, "foobar", td.All( td.Len(6), stringOps, )) // succeeds
One can do the same with All operator itself:
stringOps := td.All(td.HasPrefix("fo"), td.HasSuffix("ar")) td.Cmp(t, "foobar", td.All( td.Len(6), stringOps, )) // succeeds
but if an error occurs in the nested All, the report is a bit more complex to read due to the nested level. Flatten does not create a new level, its slice is just flattened in the All parameters.
TypeBehind method can return a non-nil reflect.Type if all items known non-interface types are equal, or if only interface types are found (mostly issued from Isa) and they are equal.
Example ¶
package main import ( "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := &testing.T{} got := "foo/bar" // Checks got string against: // "o/b" regexp *AND* "bar" suffix *AND* exact "foo/bar" string ok := td.Cmp(t, got, td.All(td.Re("o/b"), td.HasSuffix("bar"), "foo/bar"), "checks value %s", got) fmt.Println(ok) // Checks got string against: // "o/b" regexp *AND* "bar" suffix *AND* exact "fooX/Ybar" string ok = td.Cmp(t, got, td.All(td.Re("o/b"), td.HasSuffix("bar"), "fooX/Ybar"), "checks value %s", got) fmt.Println(ok) // When some operators or values have to be reused and mixed between // several calls, Flatten can be used to avoid boring and // inefficient []any copies: regOps := td.Flatten([]td.TestDeep{td.Re("o/b"), td.Re(`^fo`), td.Re(`ar$`)}) ok = td.Cmp(t, got, td.All(td.HasPrefix("foo"), regOps, td.HasSuffix("bar")), "checks all operators against value %s", got) fmt.Println(ok) }
Output: true false true
func Any ¶
Any operator compares data against several expected values. During a match, at least one of them has to match to succeed. Consider it as a "OR" logical operator.
td.Cmp(t, "foo", td.Any("bar", "foo", "zip")) // succeeds td.Cmp(t, "foo", td.Any( td.Len(4), td.HasPrefix("f"), td.HasSuffix("z"), )) // succeeds coz "f" prefix
Note Flatten function can be used to group or reuse some values or operators and so avoid boring and inefficient copies:
stringOps := td.Flatten([]td.TestDeep{td.HasPrefix("f"), td.HasSuffix("z")}) td.Cmp(t, "foobar", td.All( td.Len(4), stringOps, )) // succeeds coz "f" prefix
TypeBehind method can return a non-nil reflect.Type if all items known non-interface types are equal, or if only interface types are found (mostly issued from Isa()) and they are equal.
Example ¶
package main import ( "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := &testing.T{} got := "foo/bar" // Checks got string against: // "zip" regexp *OR* "bar" suffix ok := td.Cmp(t, got, td.Any(td.Re("zip"), td.HasSuffix("bar")), "checks value %s", got) fmt.Println(ok) // Checks got string against: // "zip" regexp *OR* "foo" suffix ok = td.Cmp(t, got, td.Any(td.Re("zip"), td.HasSuffix("foo")), "checks value %s", got) fmt.Println(ok) // When some operators or values have to be reused and mixed between // several calls, Flatten can be used to avoid boring and // inefficient []any copies: regOps := td.Flatten([]td.TestDeep{td.Re("a/c"), td.Re(`^xx`), td.Re(`ar$`)}) ok = td.Cmp(t, got, td.Any(td.HasPrefix("xxx"), regOps, td.HasSuffix("zip")), "check at least one operator matches value %s", got) fmt.Println(ok) }
Output: true false true
func Array ¶
func Array(model any, expectedEntries ArrayEntries) TestDeep
Array operator compares the contents of an array or a pointer on an array against the values of model and the values of expectedEntries. Entries with zero values of model are ignored if the same entry is present in expectedEntries, otherwise they are taken into account. An entry cannot be present in both model and expectedEntries, except if it is a zero-value in model. At the end, all entries are checked. To check only some entries of an array, see SuperSliceOf operator.
model must be the same type as compared data.
expectedEntries can be nil, if no zero entries are expected and no TestDeep operators are involved.
got := [3]int{12, 14, 17} td.Cmp(t, got, td.Array([3]int{0, 14}, td.ArrayEntries{0: 12, 2: 17})) // succeeds td.Cmp(t, &got, td.Array(&[3]int{0, 14}, td.ArrayEntries{0: td.Gt(10), 2: td.Gt(15)})) // succeeds
TypeBehind method returns the reflect.Type of model.
See also Slice and SuperSliceOf.
Example (Array) ¶
package main import ( "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := &testing.T{} got := [3]int{42, 58, 26} ok := td.Cmp(t, got, td.Array([3]int{42}, td.ArrayEntries{1: 58, 2: td.Ignore()}), "checks array %v", got) fmt.Println("Simple array:", ok) ok = td.Cmp(t, &got, td.Array(&[3]int{42}, td.ArrayEntries{1: 58, 2: td.Ignore()}), "checks array %v", got) fmt.Println("Array pointer:", ok) ok = td.Cmp(t, &got, td.Array((*[3]int)(nil), td.ArrayEntries{0: 42, 1: 58, 2: td.Ignore()}), "checks array %v", got) fmt.Println("Array pointer, nil model:", ok) }
Output: Simple array: true Array pointer: true Array pointer, nil model: true
Example (TypedArray) ¶
package main import ( "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := &testing.T{} type MyArray [3]int got := MyArray{42, 58, 26} ok := td.Cmp(t, got, td.Array(MyArray{42}, td.ArrayEntries{1: 58, 2: td.Ignore()}), "checks typed array %v", got) fmt.Println("Typed array:", ok) ok = td.Cmp(t, &got, td.Array(&MyArray{42}, td.ArrayEntries{1: 58, 2: td.Ignore()}), "checks pointer on typed array %v", got) fmt.Println("Pointer on a typed array:", ok) ok = td.Cmp(t, &got, td.Array(&MyArray{}, td.ArrayEntries{0: 42, 1: 58, 2: td.Ignore()}), "checks pointer on typed array %v", got) fmt.Println("Pointer on a typed array, empty model:", ok) ok = td.Cmp(t, &got, td.Array((*MyArray)(nil), td.ArrayEntries{0: 42, 1: 58, 2: td.Ignore()}), "checks pointer on typed array %v", got) fmt.Println("Pointer on a typed array, nil model:", ok) }
Output: Typed array: true Pointer on a typed array: true Pointer on a typed array, empty model: true Pointer on a typed array, nil model: true
func ArrayEach ¶
ArrayEach operator has to be applied on arrays or slices or on pointers on array/slice. It compares each item of data array/slice against expectedValue. During a match, all items have to match to succeed.
got := [3]string{"foo", "bar", "biz"} td.Cmp(t, got, td.ArrayEach(td.Len(3))) // succeeds td.Cmp(t, got, td.ArrayEach(td.HasPrefix("b"))) // fails coz "foo"
Works on slices as well:
got := []Person{ {Name: "Bob", Age: 42}, {Name: "Alice", Age: 24}, } td.Cmp(t, got, td.ArrayEach( td.Struct(Person{}, td.StructFields{ Age: td.Between(20, 45), })), ) // succeeds, each Person has Age field between 20 and 45
Example (Array) ¶
package main import ( "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := &testing.T{} got := [3]int{42, 58, 26} ok := td.Cmp(t, got, td.ArrayEach(td.Between(25, 60)), "checks each item of array %v is in [25 .. 60]", got) fmt.Println(ok) }
Output: true
Example (Slice) ¶
package main import ( "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := &testing.T{} got := []int{42, 58, 26} ok := td.Cmp(t, got, td.ArrayEach(td.Between(25, 60)), "checks each item of slice %v is in [25 .. 60]", got) fmt.Println(ok) }
Output: true
Example (TypedArray) ¶
package main import ( "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := &testing.T{} type MyArray [3]int got := MyArray{42, 58, 26} ok := td.Cmp(t, got, td.ArrayEach(td.Between(25, 60)), "checks each item of typed array %v is in [25 .. 60]", got) fmt.Println(ok) ok = td.Cmp(t, &got, td.ArrayEach(td.Between(25, 60)), "checks each item of typed array pointer %v is in [25 .. 60]", got) fmt.Println(ok) }
Output: true true
Example (TypedSlice) ¶
package main import ( "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := &testing.T{} type MySlice []int got := MySlice{42, 58, 26} ok := td.Cmp(t, got, td.ArrayEach(td.Between(25, 60)), "checks each item of typed slice %v is in [25 .. 60]", got) fmt.Println(ok) ok = td.Cmp(t, &got, td.ArrayEach(td.Between(25, 60)), "checks each item of typed slice pointer %v is in [25 .. 60]", got) fmt.Println(ok) }
Output: true true
func Bag ¶
Bag operator compares the contents of an array or a slice (or a pointer on array/slice) without taking care of the order of items.
During a match, each expected item should match in the compared array/slice, and each array/slice item should be matched by an expected item to succeed.
td.Cmp(t, []int{1, 1, 2}, td.Bag(1, 1, 2)) // succeeds td.Cmp(t, []int{1, 1, 2}, td.Bag(1, 2, 1)) // succeeds td.Cmp(t, []int{1, 1, 2}, td.Bag(2, 1, 1)) // succeeds td.Cmp(t, []int{1, 1, 2}, td.Bag(1, 2)) // fails, one 1 is missing td.Cmp(t, []int{1, 1, 2}, td.Bag(1, 2, 1, 3)) // fails, 3 is missing // works with slices/arrays of any type td.Cmp(t, personSlice, td.Bag( Person{Name: "Bob", Age: 32}, Person{Name: "Alice", Age: 26}, ))
To flatten a non-[]any slice/array, use Flatten function and so avoid boring and inefficient copies:
expected := []int{1, 2, 1} td.Cmp(t, []int{1, 1, 2}, td.Bag(td.Flatten(expected))) // succeeds // = td.Cmp(t, []int{1, 1, 2}, td.Bag(1, 2, 1)) exp1 := []int{5, 1, 1} exp2 := []int{8, 42, 3} td.Cmp(t, []int{1, 5, 1, 8, 42, 3, 3}, td.Bag(td.Flatten(exp1), 3, td.Flatten(exp2))) // succeeds // = td.Cmp(t, []int{1, 5, 1, 8, 42, 3, 3}, td.Bag(5, 1, 1, 3, 8, 42, 3))
TypeBehind method can return a non-nil reflect.Type if all items known non-interface types are equal, or if only interface types are found (mostly issued from Isa()) and they are equal.
See also SubBagOf, SuperBagOf and Set.
Example ¶
package main import ( "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := &testing.T{} got := []int{1, 3, 5, 8, 8, 1, 2} // Matches as all items are present ok := td.Cmp(t, got, td.Bag(1, 1, 2, 3, 5, 8, 8), "checks all items are present, in any order") fmt.Println(ok) // Does not match as got contains 2 times 1 and 8, and these // duplicates are not expected ok = td.Cmp(t, got, td.Bag(1, 2, 3, 5, 8), "checks all items are present, in any order") fmt.Println(ok) got = []int{1, 3, 5, 8, 2} // Duplicates of 1 and 8 are expected but not present in got ok = td.Cmp(t, got, td.Bag(1, 1, 2, 3, 5, 8, 8), "checks all items are present, in any order") fmt.Println(ok) // Matches as all items are present ok = td.Cmp(t, got, td.Bag(1, 2, 3, 5, td.Gt(7)), "checks all items are present, in any order") fmt.Println(ok) // When expected is already a non-[]any slice, it cannot be // flattened directly using expected... without copying it to a new // []any slice, then use td.Flatten! expected := []int{1, 2, 3, 5} ok = td.Cmp(t, got, td.Bag(td.Flatten(expected), td.Gt(7)), "checks all expected items are present, in any order") fmt.Println(ok) }
Output: true false false true true
func Between ¶
func Between(from, to any, bounds ...BoundsKind) TestDeep
Between operator checks that data is between from and to. from and to can be any numeric, string, time.Time (or assignable) value or implement at least one of the two following methods:
func (a T) Less(b T) bool // returns true if a < b func (a T) Compare(b T) int // returns -1 if a < b, 1 if a > b, 0 if a == b
from and to must be the same type as the compared value, except if BeLax config flag is true. time.Duration type is accepted as to when from is time.Time or convertible. bounds allows to specify whether bounds are included or not:
- BoundsInIn (default): between from and to both included
- BoundsInOut: between from included and to excluded
- BoundsOutIn: between from excluded and to included
- BoundsOutOut: between from and to both excluded
If bounds is missing, it defaults to BoundsInIn.
tc.Cmp(t, 17, td.Between(17, 20)) // succeeds, BoundsInIn by default tc.Cmp(t, 17, td.Between(10, 17, BoundsInOut)) // fails tc.Cmp(t, 17, td.Between(10, 17, BoundsOutIn)) // succeeds tc.Cmp(t, 17, td.Between(17, 20, BoundsOutOut)) // fails tc.Cmp(t, // succeeds netip.MustParse("127.0.0.1"), td.Between(netip.MustParse("127.0.0.0"), netip.MustParse("127.255.255.255")))
TypeBehind method returns the reflect.Type of from.
Example (Int) ¶
package main import ( "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := &testing.T{} got := 156 ok := td.Cmp(t, got, td.Between(154, 156), "checks %v is in [154 .. 156]", got) fmt.Println(ok) // BoundsInIn is implicit ok = td.Cmp(t, got, td.Between(154, 156, td.BoundsInIn), "checks %v is in [154 .. 156]", got) fmt.Println(ok) ok = td.Cmp(t, got, td.Between(154, 156, td.BoundsInOut), "checks %v is in [154 .. 156[", got) fmt.Println(ok) ok = td.Cmp(t, got, td.Between(154, 156, td.BoundsOutIn), "checks %v is in ]154 .. 156]", got) fmt.Println(ok) ok = td.Cmp(t, got, td.Between(154, 156, td.BoundsOutOut), "checks %v is in ]154 .. 156[", got) fmt.Println(ok) }
Output: true true false true false
Example (String) ¶
package main import ( "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := &testing.T{} got := "abc" ok := td.Cmp(t, got, td.Between("aaa", "abc"), `checks "%v" is in ["aaa" .. "abc"]`, got) fmt.Println(ok) // BoundsInIn is implicit ok = td.Cmp(t, got, td.Between("aaa", "abc", td.BoundsInIn), `checks "%v" is in ["aaa" .. "abc"]`, got) fmt.Println(ok) ok = td.Cmp(t, got, td.Between("aaa", "abc", td.BoundsInOut), `checks "%v" is in ["aaa" .. "abc"[`, got) fmt.Println(ok) ok = td.Cmp(t, got, td.Between("aaa", "abc", td.BoundsOutIn), `checks "%v" is in ]"aaa" .. "abc"]`, got) fmt.Println(ok) ok = td.Cmp(t, got, td.Between("aaa", "abc", td.BoundsOutOut), `checks "%v" is in ]"aaa" .. "abc"[`, got) fmt.Println(ok) }
Output: true true false true false
Example (Time) ¶
package main import ( "fmt" "testing" "time" "github.com/maxatome/go-testdeep/td" ) func main() { t := &testing.T{} before := time.Now() occurredAt := time.Now() after := time.Now() ok := td.Cmp(t, occurredAt, td.Between(before, after)) fmt.Println("It occurred between before and after:", ok) type MyTime time.Time ok = td.Cmp(t, MyTime(occurredAt), td.Between(MyTime(before), MyTime(after))) fmt.Println("Same for convertible MyTime type:", ok) ok = td.Cmp(t, MyTime(occurredAt), td.Between(before, after)) fmt.Println("MyTime vs time.Time:", ok) ok = td.Cmp(t, occurredAt, td.Between(before, 10*time.Second)) fmt.Println("Using a time.Duration as TO:", ok) ok = td.Cmp(t, MyTime(occurredAt), td.Between(MyTime(before), 10*time.Second)) fmt.Println("Using MyTime as FROM and time.Duration as TO:", ok) }
Output: It occurred between before and after: true Same for convertible MyTime type: true MyTime vs time.Time: false Using a time.Duration as TO: true Using MyTime as FROM and time.Duration as TO: true
func Cap ¶
Cap is a smuggler operator. It takes data, applies cap() function on it and compares its result to expectedCap. Of course, the compared value must be an array, a channel or a slice.
expectedCap can be an int value:
td.Cmp(t, gotSlice, td.Cap(12))
as well as an other operator:
td.Cmp(t, gotSlice, td.Cap(td.Between(3, 4)))
See also Len.
Example ¶
package main import ( "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := &testing.T{} got := make([]int, 0, 12) ok := td.Cmp(t, got, td.Cap(12), "checks %v capacity is 12", got) fmt.Println(ok) ok = td.Cmp(t, got, td.Cap(0), "checks %v capacity is 0", got) fmt.Println(ok) got = nil ok = td.Cmp(t, got, td.Cap(0), "checks %v capacity is 0", got) fmt.Println(ok) }
Output: true false true
Example (Operator) ¶
package main import ( "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := &testing.T{} got := make([]int, 0, 12) ok := td.Cmp(t, got, td.Cap(td.Between(10, 12)), "checks %v capacity is in [10 .. 12]", got) fmt.Println(ok) ok = td.Cmp(t, got, td.Cap(td.Gt(10)), "checks %v capacity is in [10 .. 12]", got) fmt.Println(ok) }
Output: true true
func Catch ¶
Catch is a smuggler operator. It allows to copy data in target on the fly before comparing it as usual against expectedValue.
target must be a non-nil pointer and data should be assignable to its pointed type. If BeLax config flag is true or called under Lax (and so JSON) operator, data should be convertible to its pointer type.
var id int64 if td.Cmp(t, CreateRecord("test"), td.JSON(`{"id": $1, "name": "test"}`, td.Catch(&id, td.NotZero()))) { t.Logf("Created record ID is %d", id) }
It is really useful when used with JSON operator and/or tdhttp helper.
var id int64 ta := tdhttp.NewTestAPI(t, api.Handler). PostJSON("/item", `{"name":"foo"}`). CmpStatus(http.StatusCreated). CmpJSONBody(td.JSON(`{"id": $1, "name": "foo"}`, td.Catch(&id, td.Gt(0)))) if !ta.Failed() { t.Logf("Created record ID is %d", id) }
If you need to only catch data without comparing it, use Ignore operator as expectedValue as in:
var id int64 if td.Cmp(t, CreateRecord("test"), td.JSON(`{"id": $1, "name": "test"}`, td.Catch(&id, td.Ignore()))) { t.Logf("Created record ID is %d", id) }
TypeBehind method returns the reflect.Type of expectedValue, except if expectedValue is a TestDeep operator. In this case, it delegates TypeBehind() to the operator, but if nil is returned by this call, the dereferenced reflect.Type of target is returned.
Example ¶
package main import ( "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := &testing.T{} got := &struct { Fullname string `json:"fullname"` Age int `json:"age"` }{ Fullname: "Bob", Age: 42, } var age int ok := td.Cmp(t, got, td.JSON(`{"age":$1,"fullname":"Bob"}`, td.Catch(&age, td.Between(40, 45)))) fmt.Println("check got age+fullname:", ok) fmt.Println("caught age:", age) }
Output: check got age+fullname: true caught age: 42
func Code ¶
Code operator allows to check data using a custom function. So fn is a function that must take one parameter whose type must be the same as the type of the compared value.
fn can return a single bool kind value, telling that yes or no the custom test is successful:
td.Cmp(t, gotTime, td.Code(func(date time.Time) bool { return date.Year() == 2018 }))
or two values (bool, string) kinds. The bool value has the same meaning as above, and the string value is used to describe the test when it fails:
td.Cmp(t, gotTime, td.Code(func(date time.Time) (bool, string) { if date.Year() == 2018 { return true, "" } return false, "year must be 2018" }))
or a single error value. If the returned error is nil, the test succeeded, else the error contains the reason of failure:
td.Cmp(t, gotJsonRawMesg, td.Code(func(b json.RawMessage) error { var c map[string]int err := json.Unmarshal(b, &c) if err != nil { return err } if c["test"] != 42 { return fmt.Errorf(`key "test" does not match 42`) } return nil }))
This operator allows to handle any specific comparison not handled by standard operators.
It is not recommended to call Cmp (or any other Cmp* functions or *T methods) inside the body of fn, because of confusion produced by output in case of failure. When the data needs to be transformed before being compared again, Smuggle operator should be used instead.
But in some cases it can be better to handle yourself the comparison than to chain TestDeep operators. In this case, fn can be a function receiving one or two *T as first parameters and returning no values.
When fn expects one *T parameter, it is directly derived from the testing.TB instance passed originally to Cmp (or its derivatives) using NewT:
td.Cmp(t, httpRequest, td.Code(func(t *td.T, r *http.Request) { token, err := DecodeToken(r.Header.Get("X-Token-1")) if t.CmpNoError(err) { t.True(token.OK()) } }))
When fn expects two *T parameters, they are directly derived from the testing.TB instance passed originally to Cmp (or its derivatives) using AssertRequire:
td.Cmp(t, httpRequest, td.Code(func(assert, require *td.T, r *http.Request) { token, err := DecodeToken(r.Header.Get("X-Token-1")) require.CmpNoError(err) assert.True(token.OK()) }))
Note that these forms do not work when there is no initial testing.TB instance, like when using EqDeeplyError or EqDeeply functions, or when the Code operator is called behind the following operators, as they just check if a match occurs without raising an error: Any, Bag, Contains, ContainsKey, None, Not, NotAny, Set, SubBagOf, SubSetOf, SuperBagOf and SuperSetOf.
RootName is inherited but not the current path, but it can be recovered if needed:
got := map[string]int{"foo": 123} td.NewT(t). RootName("PIPO"). Cmp(got, td.Map(map[string]int{}, td.MapEntries{ "foo": td.Code(func(t *td.T, n int) { t.Cmp(n, 124) // inherit only RootName t.RootName(t.Config.OriginalPath()).Cmp(n, 125) // recover current path t.RootName("").Cmp(n, 126) // undo RootName inheritance }), }))
produces the following errors:
--- FAIL: TestCodeCustom (0.00s) td_code_test.go:339: Failed test PIPO: values differ ← inherit only RootName got: 123 expected: 124 td_code_test.go:338: Failed test PIPO["foo"]: values differ ← recover current path got: 123 expected: 125 td_code_test.go:342: Failed test DATA: values differ ← undo RootName inheritance got: 123 expected: 126
TypeBehind method returns the reflect.Type of last parameter of fn.
Example ¶
package main import ( "fmt" "strconv" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := &testing.T{} got := "12" ok := td.Cmp(t, got, td.Code(func(num string) bool { n, err := strconv.Atoi(num) return err == nil && n > 10 && n < 100 }), "checks string `%s` contains a number and this number is in ]10 .. 100[", got) fmt.Println(ok) // Same with failure reason ok = td.Cmp(t, got, td.Code(func(num string) (bool, string) { n, err := strconv.Atoi(num) if err != nil { return false, "not a number" } if n > 10 && n < 100 { return true, "" } return false, "not in ]10 .. 100[" }), "checks string `%s` contains a number and this number is in ]10 .. 100[", got) fmt.Println(ok) // Same with failure reason thanks to error ok = td.Cmp(t, got, td.Code(func(num string) error { n, err := strconv.Atoi(num) if err != nil { return err } if n > 10 && n < 100 { return nil } return fmt.Errorf("%d not in ]10 .. 100[", n) }), "checks string `%s` contains a number and this number is in ]10 .. 100[", got) fmt.Println(ok) }
Output: true true true
Example (Custom) ¶
package main import ( "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := &testing.T{} got := 123 ok := td.Cmp(t, got, td.Code(func(t *td.T, num int) { t.Cmp(num, 123) })) fmt.Println("with one *td.T:", ok) ok = td.Cmp(t, got, td.Code(func(assert, require *td.T, num int) { assert.Cmp(num, 123) require.Cmp(num, 123) })) fmt.Println("with assert & require *td.T:", ok) }
Output: with one *td.T: true with assert & require *td.T: true
func Contains ¶
Contains is a smuggler operator to check if something is contained in another thing. Contains has to be applied on arrays, slices, maps or strings. It tries to be as smarter as possible.
If expectedValue is a TestDeep operator, each item of data array/slice/map/string (rune for strings) is compared to it. The use of a TestDeep operator as expectedValue works only in this way: item per item.
If data is a slice, and expectedValue has the same type, then expectedValue is searched as a sub-slice, otherwise expectedValue is compared to each slice value.
list := []int{12, 34, 28} td.Cmp(t, list, td.Contains(34)) // succeeds td.Cmp(t, list, td.Contains(td.Between(30, 35))) // succeeds too td.Cmp(t, list, td.Contains(35)) // fails td.Cmp(t, list, td.Contains([]int{34, 28})) // succeeds
If data is an array or a map, each value is compared to expectedValue. Map keys are not checked: see ContainsKey to check map keys existence.
hash := map[string]int{"foo": 12, "bar": 34, "zip": 28} td.Cmp(t, hash, td.Contains(34)) // succeeds td.Cmp(t, hash, td.Contains(td.Between(30, 35))) // succeeds too td.Cmp(t, hash, td.Contains(35)) // fails array := [...]int{12, 34, 28} td.Cmp(t, array, td.Contains(34)) // succeeds td.Cmp(t, array, td.Contains(td.Between(30, 35))) // succeeds too td.Cmp(t, array, td.Contains(35)) // fails
If data is a string (or convertible), []byte (or convertible), error or fmt.Stringer interface (error interface is tested before fmt.Stringer), expectedValue can be a string, a []byte, a rune or a byte. In this case, it tests if the got string contains this expected string, []byte, rune or byte.
got := "foo bar" td.Cmp(t, got, td.Contains('o')) // succeeds td.Cmp(t, got, td.Contains(rune('o'))) // succeeds td.Cmp(t, got, td.Contains(td.Between('n', 'p'))) // succeeds td.Cmp(t, got, td.Contains("bar")) // succeeds td.Cmp(t, got, td.Contains([]byte("bar"))) // succeeds td.Cmp(t, []byte("foobar"), td.Contains("ooba")) // succeeds type Foobar string td.Cmp(t, Foobar("foobar"), td.Contains("ooba")) // succeeds err := errors.New("error!") td.Cmp(t, err, td.Contains("ror")) // succeeds bstr := bytes.NewBufferString("fmt.Stringer!") td.Cmp(t, bstr, td.Contains("String")) // succeeds
Pitfall: if you want to check if 2 words are contained in got, don't do:
td.Cmp(t, "foobar", td.Contains(td.All("foo", "bar"))) // Bad!
as TestDeep operator All in Contains operates on each rune, so it does not work as expected, but do::
td.Cmp(t, "foobar", td.All(td.Contains("foo"), td.Contains("bar")))
When Contains(nil) is used, nil is automatically converted to a typed nil on the fly to avoid confusion (if the array/slice/map item type allows it of course.) So all following Cmp calls are equivalent (except the (*byte)(nil) one):
num := 123 list := []*int{&num, nil} td.Cmp(t, list, td.Contains(nil)) // succeeds → (*int)(nil) td.Cmp(t, list, td.Contains((*int)(nil))) // succeeds td.Cmp(t, list, td.Contains(td.Nil())) // succeeds // But... td.Cmp(t, list, td.Contains((*byte)(nil))) // fails: (*byte)(nil) ≠ (*int)(nil)
As well as these ones:
hash := map[string]*int{"foo": nil, "bar": &num} td.Cmp(t, hash, td.Contains(nil)) // succeeds → (*int)(nil) td.Cmp(t, hash, td.Contains((*int)(nil))) // succeeds td.Cmp(t, hash, td.Contains(td.Nil())) // succeeds
See also ContainsKey.
Example (ArraySlice) ¶
package main import ( "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := &testing.T{} ok := td.Cmp(t, [...]int{11, 22, 33, 44}, td.Contains(22)) fmt.Println("array contains 22:", ok) ok = td.Cmp(t, [...]int{11, 22, 33, 44}, td.Contains(td.Between(20, 25))) fmt.Println("array contains at least one item in [20 .. 25]:", ok) ok = td.Cmp(t, []int{11, 22, 33, 44}, td.Contains(22)) fmt.Println("slice contains 22:", ok) ok = td.Cmp(t, []int{11, 22, 33, 44}, td.Contains(td.Between(20, 25))) fmt.Println("slice contains at least one item in [20 .. 25]:", ok) ok = td.Cmp(t, []int{11, 22, 33, 44}, td.Contains([]int{22, 33})) fmt.Println("slice contains the sub-slice [22, 33]:", ok) }
Output: array contains 22: true array contains at least one item in [20 .. 25]: true slice contains 22: true slice contains at least one item in [20 .. 25]: true slice contains the sub-slice [22, 33]: true
Example (Error) ¶
package main import ( "errors" "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := &testing.T{} got := errors.New("foobar") ok := td.Cmp(t, got, td.Contains("oob"), "checks %s", got) fmt.Println("contains `oob` string:", ok) ok = td.Cmp(t, got, td.Contains('b'), "checks %s", got) fmt.Println("contains 'b' rune:", ok) ok = td.Cmp(t, got, td.Contains(byte('a')), "checks %s", got) fmt.Println("contains 'a' byte:", ok) ok = td.Cmp(t, got, td.Contains(td.Between('n', 'p')), "checks %s", got) fmt.Println("contains at least one character ['n' .. 'p']:", ok) }
Output: contains `oob` string: true contains 'b' rune: true contains 'a' byte: true contains at least one character ['n' .. 'p']: true
Example (Map) ¶
package main import ( "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := &testing.T{} ok := td.Cmp(t, map[string]int{"foo": 11, "bar": 22, "zip": 33}, td.Contains(22)) fmt.Println("map contains value 22:", ok) ok = td.Cmp(t, map[string]int{"foo": 11, "bar": 22, "zip": 33}, td.Contains(td.Between(20, 25))) fmt.Println("map contains at least one value in [20 .. 25]:", ok) }
Output: map contains value 22: true map contains at least one value in [20 .. 25]: true
Example (Nil) ¶
package main import ( "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := &testing.T{} num := 123 got := [...]*int{&num, nil} ok := td.Cmp(t, got, td.Contains(nil)) fmt.Println("array contains untyped nil:", ok) ok = td.Cmp(t, got, td.Contains((*int)(nil))) fmt.Println("array contains *int nil:", ok) ok = td.Cmp(t, got, td.Contains(td.Nil())) fmt.Println("array contains Nil():", ok) ok = td.Cmp(t, got, td.Contains((*byte)(nil))) fmt.Println("array contains *byte nil:", ok) // types differ: *byte ≠ *int }
Output: array contains untyped nil: true array contains *int nil: true array contains Nil(): true array contains *byte nil: false
Example (String) ¶
package main import ( "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := &testing.T{} got := "foobar" ok := td.Cmp(t, got, td.Contains("oob"), "checks %s", got) fmt.Println("contains `oob` string:", ok) ok = td.Cmp(t, got, td.Contains([]byte("oob")), "checks %s", got) fmt.Println("contains `oob` []byte:", ok) ok = td.Cmp(t, got, td.Contains('b'), "checks %s", got) fmt.Println("contains 'b' rune:", ok) ok = td.Cmp(t, got, td.Contains(byte('a')), "checks %s", got) fmt.Println("contains 'a' byte:", ok) ok = td.Cmp(t, got, td.Contains(td.Between('n', 'p')), "checks %s", got) fmt.Println("contains at least one character ['n' .. 'p']:", ok) }
Output: contains `oob` string: true contains `oob` []byte: true contains 'b' rune: true contains 'a' byte: true contains at least one character ['n' .. 'p']: true
Example (Stringer) ¶
package main import ( "bytes" "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := &testing.T{} // bytes.Buffer implements fmt.Stringer got := bytes.NewBufferString("foobar") ok := td.Cmp(t, got, td.Contains("oob"), "checks %s", got) fmt.Println("contains `oob` string:", ok) ok = td.Cmp(t, got, td.Contains('b'), "checks %s", got) fmt.Println("contains 'b' rune:", ok) ok = td.Cmp(t, got, td.Contains(byte('a')), "checks %s", got) fmt.Println("contains 'a' byte:", ok) ok = td.Cmp(t, got, td.Contains(td.Between('n', 'p')), "checks %s", got) fmt.Println("contains at least one character ['n' .. 'p']:", ok) }
Output: contains `oob` string: true contains 'b' rune: true contains 'a' byte: true contains at least one character ['n' .. 'p']: true
func ContainsKey ¶
ContainsKey is a smuggler operator and works on maps only. It compares each key of map against expectedValue.
hash := map[string]int{"foo": 12, "bar": 34, "zip": 28} td.Cmp(t, hash, td.ContainsKey("foo")) // succeeds td.Cmp(t, hash, td.ContainsKey(td.HasPrefix("z"))) // succeeds td.Cmp(t, hash, td.ContainsKey(td.HasPrefix("x"))) // fails hnum := map[int]string{1: "foo", 42: "bar"} td.Cmp(t, hash, td.ContainsKey(42)) // succeeds td.Cmp(t, hash, td.ContainsKey(td.Between(40, 45))) // succeeds
When ContainsKey(nil) is used, nil is automatically converted to a typed nil on the fly to avoid confusion (if the map key type allows it of course.) So all following Cmp calls are equivalent (except the (*byte)(nil) one):
num := 123 hnum := map[*int]bool{&num: true, nil: true} td.Cmp(t, hnum, td.ContainsKey(nil)) // succeeds → (*int)(nil) td.Cmp(t, hnum, td.ContainsKey((*int)(nil))) // succeeds td.Cmp(t, hnum, td.ContainsKey(td.Nil())) // succeeds // But... td.Cmp(t, hnum, td.ContainsKey((*byte)(nil))) // fails: (*byte)(nil) ≠ (*int)(nil)
See also Contains.
Example ¶
package main import ( "fmt" "strings" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := &testing.T{} ok := td.Cmp(t, map[string]int{"foo": 11, "bar": 22, "zip": 33}, td.ContainsKey("foo")) fmt.Println(`map contains key "foo":`, ok) ok = td.Cmp(t, map[int]bool{12: true, 24: false, 42: true, 51: false}, td.ContainsKey(td.Between(40, 50))) fmt.Println("map contains at least a key in [40 .. 50]:", ok) ok = td.Cmp(t, map[string]int{"FOO": 11, "bar": 22, "zip": 33}, td.ContainsKey(td.Smuggle(strings.ToLower, "foo"))) fmt.Println(`map contains key "foo" without taking case into account:`, ok) }
Output: map contains key "foo": true map contains at least a key in [40 .. 50]: true map contains key "foo" without taking case into account: true
Example (Nil) ¶
package main import ( "fmt" "testing" "github.com/maxatome/go-testdeep/td" ) func main() { t := &testing.T{} num := 1234 got := map[*int]bool{&num: false, nil: true} ok := td.Cmp(t, got, td.ContainsKey(nil)) fmt.Println("map contains untyped nil key:", ok) ok = td.Cmp(t, got, td.ContainsKey((*int)(nil))) fmt.Println("map contains *int nil key:", ok) ok = td.Cmp(t, got, td.ContainsKey(td.Nil())) fmt.Println("map contains Nil() key:", ok) ok = td.Cmp(t, got, td.ContainsKey((*byte)(nil))) fmt.Println("map contains *byte nil key:", ok) // types differ: *byte ≠ *int }
Output: map contains untyped nil key: true map contains *int nil key: true map contains Nil() key: true map contains *byte nil key: false
func Delay ¶ added in v1.4.0
Delay operator allows to delay the construction of an operator to the time it is used for the first time. Most of the time, it is used with helpers. See the example for a very simple use case.
Example ¶
package main import ( "fmt" "testing" "time" "github.com/maxatome/go-testdeep/td" ) func main() { t := &testing.T{} cmpNow := func(expected td.TestDeep) bool { time.Sleep(time.Microsecond) // imagine a DB insert returning a CreatedAt return td.Cmp(t, time