Releases · maxatome/go-testdeep

v1.15.0

Features

  • go-testdeep is now dependency-free!
  • add List operator to compare an array or a slice in strict order (can be seen as a lighter alternative to Array and Slice):
    td.Cmp(t, []int{1, 9, 5}, td.List(1, 9, 5))
    // works with slices/arrays of any type
    td.Cmp(t, personSlice, td.List(
      Person{Name: "Bob", Age: 32},
      Person{Name: "Alice", Age: 26},
    ))
  • add Sort operator to sort an array or slice before comparing its content (can be seen as an alternative to Bag):
    td.Cmp(intSlice, td.Sort(-1, []int{4, 5, 9, 10})) // check intSlice content after sorting it in descending order
    // the key(s) on which the sorting applies can be quite complex
    type A struct{ props map[string]int }
    p12 := A{props: map[string]int{"priority": 12}}
    p23 := A{props: map[string]int{"priority": 23}}
    p34 := A{props: map[string]int{"priority": 34}}
    got := []A{p23, p12, p34}
    td.Cmp(t, got, td.Sort("-props[priority]", []A{p34, p23, p12}))
    // can be used inside JSON operator
    got := map[string][]string{"labels": {"c", "a", "b"}}
    td.Cmp(t, got, td.JSON(`{ "labels": Sort(1, ["a", "b", "c"]) }`))
    // or more complex
    td.Cmp(t, got, td.JSON(`{
      "people": Sort([ "-age", "name" ], [ // sort by age desc, then by name asc
        {"name": "Marcel",  "age": 25},
        {"name": "Brian",   "age": 22},
        {"name": "Alice",   "age": 20},
        {"name": "Bob",     "age": 19},
        {"name": "Stephen", "age": 19},
      ])
    }`))
  • add Sorted operator to check an array or slice is sorted, examples:
    td.Cmp(t, aSlice, td.Sorted()) // checks aSlice is sorted in ascending order
    td.Cmp(t, aStructSlice, td.Sorted("-FieldName")) // checks aStructSlice is sorted in descending order
    // can be used inside JSON operator
    got := map[string][]string{"labels": {"a", "b", "c"}}
    td.Cmp(t, got, td.JSON(`{ "labels": Sorted }`))
  • new Must, Must2 & Must3 functions allowing to do:
    // before
    val, err := myfunction()
    td.Require(t).CmpNoError(err)
    // now
    val := td.Must(myfunction())
  • Smuggle fields-path can now contains method calls, allowing to do td.Smuggle("A.B.C.MethodName().D.E", expected) for example, and reference map[string]... using .key instead of [key] before;
  • Map, SubMapOf, SuperMapOf, Array, Slice & SuperSliceOf expectedEntries parameter is now optional or multiple;
  • Flatten now accepts func(T, X...) V|(V, bool) function signatures as well as an int to produce an int range;
  • regexp or shell pattern fields of Struct & SStruct now appear clearly in failure report;
  • tdhttp.TestAPI: add Clone, DefaultRequestParams & AddDefaultRequestParams methods;
  • tdhttp.TestAPI now sets Host field;
  • tdhttp.NewRequest and all other functions + tdhttp.TestAPI methods building HTTP requests also accept hook as func(*http.Request) error;
  • tdhttp.TestAPI: add DefaultHeader & DefaultHook methods to define header values & automatically called hook for each future requests sent by tdhttp.TestAPI;
  • add tdsynctest helper package, a simple wrapper around testing/synctest powered by go-testdeep;
  • new tdutil.CmpValuesFunc function is able to sort []reflect.Value slices thanks to slices.SortFunc;
  • tdutil.SortableValues now uses T.Compare(T) int method when it is available.

Fixes

  • operators errors are no longer hidden when used inside another operator;
  • tdutil internal tests for go1.25 was broken;
  • Grep, Last & First String() method now shows the expected value;
  • in some cases, tests continued after a failure instead of stopping immediately;
  • Grep: if got is a specific named slice type, then expect this type .

What's Changed in details

New Contributors

Full Changelog: v1.14.0...v1.15.0

v1.14.0

Features

Fixes

  • in error reports, the origin operator was sometimes missing and sometimes wrong when using JSON, SubJSONOf and SuperJSONOf operators;
  • fix JSON, SubJSONOf and SuperJSONOf panic when using a bad placeholder several times in the same operator.

What's Changed in details

New Contributors

Full Changelog: v1.13.0...v1.14.0

v1.13.0

Features

  • New operators:
    • ErrorIs to check the data is an error and matches a wrapped error (uses errors.Is behind the scene),
    • First to find the first matching item of a slice or an array then compare its content,
    • Grep to reduce a slice or an array before comparing its content,
    • Last to find the last matching item of a slice or an array then compare its content,
    • Recv to read from a channel and test value;
  • rework of JSON parsing:
    • introduction of raw strings,
    • literal \n, \r & \t accepted in strings,
    • $^Operator(params...) is possible,
    • "$^Operator(params...)" is possible,
    • Operator (without parenthesis) is possible,
    • accepts json.RawMessage;
  • anchoring is now possible using td.A and td.Anchor, the generic versions of td.T.A and td.T.Anchor;
  • td.Flatten now accepts an optional function to filter and/or transform the items before flattening them;
  • *td.T gains Assert and Require methods, to be consistent with existing td.Assert and td.Require;
  • introduce td.S function, to produce strings without needing fmt, shorter to write and following the same calling convention as test names (in other words auto-detecting Printf like format);
  • new TestDeepInGotOK config to allow to test go-testdeep operators (is forbidden by default, but useful when go-testdeep is used outside golang tests) including td.T.TestDeepInGotOK method;
  • tdhttp package:

As usual: enjoy! :)

What's changed in details

Full Changelog: v1.12.0...v1.13.0

v1.12.0

New features

  • Struct & SStruct can override model fields in expectedFields, as in:
      td.Cmp(t, got, td.Struct(
        Person{
          Name:     "John Doe",
          Age:      23,
          Children: 4,
        },
        td.StructFields{
          "> Age":     td.Between(40, 45),
          ">Children": 0, // spaces after ">" are optional
        }),
      )
  • Struct & SStruct expectedFields is now optional or multiple. If multiple, all maps are merged from left to right;
  • try to detect wrongly defined hooks in tdsuite @deathiop
    Given the 5 hooks:
  • try to detect possible tdsuite.Run() misuse, warn the user when it is called with a non-pointer suite, and some key methods are only available via a pointer suite;
  • add tdhttp.Options function & tdhttp.TestAPI.Options method;
  • Code can now officially delegate its comparison, using two new kinds of function:
    • func(t *td.T, arg)
    • func(assert, require *td.T, arg)
      this way the usage of *td.T methods is secure. Note that these functions do not return anything;
  • using a *td.T instance as Cmp* first parameter now allows to inherit its configuration.
    td.Cmp(td.Require(t), got, 42)
    is the same as:
    td.Require(t).Cmp(got, 42)

Fixes

Miscellaneous

  • enhance & refactor doc comments with new go 1.19 features;
  • documentation now uses any instead of interface{};
  • fix typos.

As usual: enjoy! :)

v1.11.0

New features

  • JSON, SubJSONOf and SuperJSONOf operators:
    • Now:
      td.JSON(`{"foo": $1}`, td.Between(12, 34))
      td.JSON(`{"foo": Between($1, $2)}`, 12, 34)
      act the same way. Since v1.10.1 rework, the second was not working as expected.
      At the same time:
      td.JSON(`{"foo": Between(12, 34, $1)}`, td.BoundsOutIn)
      now works as expected. It was rejected before,
    • int_lit & float_lit numbers, as defined in go specification, are now accepted in JSON, allowing, for example, to embed hexadecimal numbers in expected JSON;
  • Between operator accepts time.Duration as TO when FROM is a time.Time;
  • Between, Gt, Gte, Lt and Lte operators:
    • now handle values implementing Compare or Less methods:
      Compare and/or Less methods signatures follow:
      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
      for example, the new go1.18 net/netip.Addr implements both,
    • failure reports display numbers as elsewhere,
    • some docs have been fixed;
  • Smuggle can now cast values, allowing such things:
    // A string containing JSON
    got := `{ "foo": 123 }`
    td.Cmp(t, got, td.Smuggle(json.RawMessage{}, td.JSON(`{"foo":123}`))
    // or
    td.Cmp(t, got, td.Smuggle((json.RawMessage)(nil), td.JSON(`{"foo":123}`))
  • in report failures, the stack trace was displayed if it contained at least 2 levels. Now it is also displayed if it contains one level BUT the source file path contains a directory. It helps to localize quickly the errors in large repositories;
  • td.T gains 3 new methods to log with a stack trace: LogTrace, ErrorTrace and FatalTrace;
  • handle go1.17 []T*[n]T convertibility edge case;
  • in failure reports (float64) type is now omitted (like (int) and (bool) already were), but the value always contains a "." (except if ±Inf, NaN or with exponent) to distinguish them from int.

Fixes

  • Contains: slice in slice search didn't work at end of slice;
  • JSON, SubJSONOf and SuperJSONOf operators panicked when using All+JSON-operator+embeddedOp+$1;
  • anchoring 2 slices at the same time didn't work.

Miscellaneous

And happy new year!

v1.10.1

Fixes:

  • Len and Cap could not be embedded in JSON, SubJSONOf and SuperJSONOf. Fixed;
  • Bag and Set didn't implement TypeBehind() method. Fixed;
  • JSONPointer expectedValue and JSON placeholders didn't have the same behavior. Now the pointed data is JSON-unmarshaled into a value using the same type of expectedValue (for JSONPointer) or any placeholder (for JSON) before comparison;
  • Catch TypeBehind() method didn't use the type of target when the type behind expectedValue could not be determined. Fixed;
  • (*tdhttp.TestApi).CmpJSONBody(nil) panicked. Fixed;
  • and some typos fixed.

Enjoy!

v1.10.0

New features:

Fixes:

  • multi-lines colored text → color-off \n color-on for each line (@siadat);
  • JSON TypeBehind() method is now delegated when an operator is at the root of the JSON;
  • tdhttp package:
    • chaining of Cmp* methods can lead to skew Failed() result.

Enjoy!

v1.9.2

Fixes:

v1.9.1

Fixes:

  • traces displayed when a Cmp failure occurs, are now properly stripped to ignore testing, td, tdhttp and tdsuite internal calls;
  • JSON unmarshal bug when \uxxxx is first escaping sequence in a string.

v1.9.0

New features:

  • introducing new tdsuite helper, allowing to easily create testing suites, fully integrated with go-testdeep;
  • JSON, SubJSONOf and SuperJSONOf now accept almost all operators embedded directly in JSON thanks to our new custom JSON decoder:
    td.Cmp(t,
      got,
      td.JSON(`{"name": HasPrefix("Bob"), "age": Between(20, 25)}`),
    )
  • Re and ReAll capture parameter can now be a []interface{} (only []string until now);
  • Flatten is now able to flatten maps, can be useful when creating request using tdhttp package;
  • when a Cmp failure occurs outside the root of a test function, a trace of all successive function calls is displayed to help the user to understand/locate the error;
  • in tdhttp helper (aka the ultimate HTTP API tester), tdhttp.TestAPI gains several methods:
    • AutoDumpResponse allows to dump the HTTP response when the first error is encountered after a request,
    • OrDumpResponse dumps the response if at least one previous test failed,
    • Run runs a subtest,
    • T returns the internal instance of *td.T,
    • With creates a new *TestAPI instance.

Fixes:

  • reflect.Interface containing nil could produce wrong comparisons.

Enjoy!