JSON

func JSON(expectedJSON interface{}, params ...interface{}) TestDeep

JSON operator allows to compare the JSON representation of data against expectedJSON. expectedJSON can be a:

  • string containing JSON data like {"fullname":"Bob","age":42}
  • string containing a JSON filename, ending with “.json” (its content is ioutil.ReadFile before unmarshaling)
  • []byte containing JSON data
  • io.Reader stream containing JSON data (is ioutil.ReadAll before unmarshaling)

expectedJSON JSON value can contain placeholders. The params are for any placeholder parameters in expectedJSON. params can contain TestDeep operators as well as raw values. Raw values are first json.Marshal‘ed then json.Unmarshal‘ed in an interface{}. A placeholder can be numeric like $2 or named like $name and always references an item in params.

Numeric placeholders reference the n’th “operators” item (starting at 1). Named placeholders are used with Tag operator as follows:

td.Cmp(t, gotValue,
  td.JSON(`{"fullname": $name, "age": $2, "gender": $3}`,
    td.Tag("name", td.HasPrefix("Foo")), // matches $1 and $name
    td.Between(41, 43),                  // matches only $2
    "male"))                             // matches only $3

Note that placeholders can be double-quoted as in:

td.Cmp(t, gotValue,
  td.JSON(`{"fullname": "$name", "age": "$2", "gender": "$3"}`,
    td.Tag("name", td.HasPrefix("Foo")), // matches $1 and $name
    td.Between(41, 43),                  // matches only $2
    "male"))                             // matches only $3

It makes no difference whatever the underlying type of the replaced item is (= double quoting a placeholder matching a number is not a problem). It is just a matter of taste, double-quoting placeholders can be preferred when the JSON data has to conform to the JSON specification, like when used in a “.json” file.

Note expectedJSON can be a []byte, JSON filename or io.Reader:

td.Cmp(t, gotValue, td.JSON("file.json", td.Between(12, 34)))
td.Cmp(t, gotValue, td.JSON([]byte(`[1, $1, 3]`), td.Between(12, 34)))
td.Cmp(t, gotValue, td.JSON(osFile, td.Between(12, 34)))

A JSON filename ends with “.json”.

To avoid a legit “$” string prefix causes a bad placeholder error, just double it to escape it. Note it is only needed when the “$” is the first character of a string:

td.Cmp(t, gotValue,
  td.JSON(`{"fullname": "$name", "details": "$$info", "age": $2}`,
    td.Tag("name", td.HasPrefix("Foo")), // matches $1 and $name
    td.Between(41, 43)))                 // matches only $2

For the “details” key, the raw value “$info” is expected, no placeholders are involved here.

Note that Lax mode is automatically enabled by JSON operator to simplify numeric tests.

Comments can be embedded in JSON data:

td.Cmp(t, gotValue,
  td.JSON(`
{
  // A guy properties:
  "fullname": "$name",  // The full name of the guy
  "details":  "$$info", // Literally "$info", thanks to "$" escape
  "age":      $2        /* The age of the guy:
                           - placeholder unquoted, but could be without
                             any change
                           - to demonstrate a multi-lines comment */
}`,
    td.Tag("name", td.HasPrefix("Foo")), // matches $1 and $name
    td.Between(41, 43)))                 // matches only $2

Comments, like in go, have 2 forms. To quote the Go language specification:

  • line comments start with the character sequence // and stop at the end of the line.
  • multi-lines comments start with the character sequence /* and stop with the first subsequent character sequence */.

Most operators can be directly embedded in JSON without requiring any placeholder.

td.Cmp(t, gotValue,
  td.JSON(`
{
  "fullname": HasPrefix("Foo"),
  "age":      Between(41, 43),
  "details":  SuperMapOf({
    "address": NotEmpty(),
    "car":     Any("Peugeot", "Tesla", "Jeep") // any of these
  })
}`))

Placeholders can be used anywhere, even in operators parameters as in:

td.Cmp(t, gotValue, td.JSON(`{"fullname": HasPrefix($1)}`, "Zip"))

A few notes about operators embedding:

Operators taking no parameters can also be directly embedded in JSON data using $^OperatorName or “$^OperatorName” notation. They are named shortcut operators (they predate the above operators embedding but they subsist for compatibility):

td.Cmp(t, gotValue, td.JSON(`{"id": $1}`, td.NotZero()))

can be written as:

td.Cmp(t, gotValue, td.JSON(`{"id": $^NotZero}`))

or

td.Cmp(t, gotValue, td.JSON(`{"id": "$^NotZero"}`))

As for placeholders, there is no differences between $^NotZero and “$^NotZero”.

The allowed shortcut operators follow:

TypeBehind method returns the reflect.Type of the expectedJSON json.Unmarshal‘ed. So it can be bool, string, float64, []interface{}, map[string]interface{} or interface{} in case expectedJSON is “null”.

See also JSON godoc.

Examples

Basic example
Placeholders example
Embedding example
File example

CmpJSON shortcut

func CmpJSON(t TestingT, got, expectedJSON interface{}, params []interface{}, args ...interface{}) bool

CmpJSON is a shortcut for:

td.Cmp(t, got, td.JSON(expectedJSON, params...), args...)

See above 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.

See also CmpJSON godoc.

Examples

Basic example
Placeholders example
Embedding example
File example

T.JSON shortcut

func (t *T) JSON(got, expectedJSON interface{}, params []interface{}, args ...interface{}) bool

JSON is a shortcut for:

t.Cmp(got, td.JSON(expectedJSON, params...), args...)

See above 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.

See also T.JSON godoc.

Examples

Basic example
Placeholders example
Embedding example
File example