Skip to content

Commit 8383e2d

Browse files
committed
Initial import
0 parents  commit 8383e2d

File tree

7 files changed

+281
-0
lines changed

7 files changed

+281
-0
lines changed

LICENSE

+33
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
2+
Copyright (c) 2022, Cristian Maglie.
3+
All rights reserved.
4+
5+
Redistribution and use in source and binary forms, with or without
6+
modification, are permitted provided that the following conditions
7+
are met:
8+
9+
1. Redistributions of source code must retain the above copyright
10+
notice, this list of conditions and the following disclaimer.
11+
12+
2. Redistributions in binary form must reproduce the above copyright
13+
notice, this list of conditions and the following disclaimer in
14+
the documentation and/or other materials provided with the
15+
distribution.
16+
17+
3. Neither the name of the copyright holder nor the names of its
18+
contributors may be used to endorse or promote products derived
19+
from this software without specific prior written permission.
20+
21+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22+
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23+
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24+
FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
25+
COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26+
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
27+
BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28+
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
29+
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30+
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
31+
ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32+
POSSIBILITY OF SUCH DAMAGE.
33+

README.md

+40
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
# `go.bug.st/testifyjson/requirejson` - unit-test JSON output in golang.
2+
3+
Package testifyjson is a collection of utilities and helper function for unit testing
4+
JSON output in golang.
5+
6+
It is based on the excellent libraries `github.com/itchyny/gojq` and `github.com/stretchr/testify`. It provides an interface similar to `testify` but with the powerful methods available in `gojq`.
7+
8+
## Examples
9+
10+
```go
11+
import (
12+
"testing"
13+
"go.bug.st/testifyjson/requirejson"
14+
)
15+
16+
func TestJSONQuery(t *testing.T) {
17+
in := []byte(`
18+
{
19+
"id" : 1,
20+
"list" : [
21+
10, 20, 30
22+
],
23+
"emptylist" : []
24+
}
25+
`)
26+
requirejson.Query(t, in, ".list", "[10, 20, 30]")
27+
requirejson.Query(t, in, ".list.[1]", "20")
28+
29+
requirejson.Contains(t, in, `{ "list": [ 30 ] }`)
30+
requirejson.NotContains(t, in, `{ "list": [ 50 ] }`)
31+
32+
in2 := []byte(`[ ]`)
33+
requirejson.Empty(t, in2)
34+
requirejson.Len(t, in2, 0)
35+
36+
in3 := []byte(`[ 10, 20, 30 ]`)
37+
requirejson.NotEmpty(t, in3)
38+
requirejson.Len(t, in3, 3)
39+
}
40+
```

doc.go

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
//
2+
// Copyright 2022 Cristian Maglie. All rights reserved.
3+
// Use of this source code is governed by a BSD-style
4+
// license that can be found in the LICENSE file.
5+
//
6+
7+
package testifyjson

go.mod

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
module go.bug.st/testifyjson
2+
3+
go 1.19
4+
5+
require (
6+
github.com/itchyny/gojq v0.12.8
7+
github.com/stretchr/testify v1.8.0
8+
)
9+
10+
require (
11+
github.com/davecgh/go-spew v1.1.1 // indirect
12+
github.com/itchyny/timefmt-go v0.1.3 // indirect
13+
github.com/pmezard/go-difflib v1.0.0 // indirect
14+
gopkg.in/yaml.v3 v3.0.1 // indirect
15+
)

go.sum

+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
2+
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
3+
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
4+
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
5+
github.com/itchyny/gojq v0.12.8 h1:Zxcwq8w4IeR8JJYEtoG2MWJZUv0RGY6QqJcO1cqV8+A=
6+
github.com/itchyny/gojq v0.12.8/go.mod h1:gE2kZ9fVRU0+JAksaTzjIlgnCa2akU+a1V0WXgJQN5c=
7+
github.com/itchyny/timefmt-go v0.1.3 h1:7M3LGVDsqcd0VZH2U+x393obrzZisp7C0uEe921iRkU=
8+
github.com/itchyny/timefmt-go v0.1.3/go.mod h1:0osSSCQSASBJMsIZnhAaF1C2fCBTJZXrnj37mG8/c+A=
9+
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
10+
github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
11+
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
12+
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
13+
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
14+
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
15+
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
16+
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
17+
github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk=
18+
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
19+
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
20+
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
21+
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
22+
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
23+
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
24+
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
25+
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
26+
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

requirejson/json.go

+122
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
//
2+
// Copyright 2022 Cristian Maglie. All rights reserved.
3+
// Use of this source code is governed by a BSD-style
4+
// license that can be found in the LICENSE file.
5+
//
6+
7+
package requirejson
8+
9+
import (
10+
"encoding/json"
11+
"fmt"
12+
"testing"
13+
14+
"github.com/itchyny/gojq"
15+
"github.com/stretchr/testify/require"
16+
)
17+
18+
// Query performs a test on a given json output. A jq-like query is performed
19+
// on the given jsonData and the result is compared with the expected output.
20+
// If the output doesn't match the test fails. If msgAndArgs are provided they
21+
// will be used to explain the error.
22+
func Query(t *testing.T, jsonData []byte, jqQuery string, jsonExpected string, msgAndArgs ...interface{}) {
23+
var data interface{}
24+
require.NoError(t, json.Unmarshal(jsonData, &data))
25+
var expected interface{}
26+
require.NoError(t, json.Unmarshal([]byte(jsonExpected), &expected))
27+
q, err := gojq.Parse(jqQuery)
28+
require.NoError(t, err)
29+
i := q.Run(data)
30+
v, ok := i.Next()
31+
require.True(t, ok)
32+
require.IsType(t, expected, v)
33+
require.Equal(t, expected, v, msgAndArgs...)
34+
}
35+
36+
// Contains check if the json object is a subset of the jsonData.
37+
// If the output doesn't match the test fails. If msgAndArgs are provided they
38+
// will be used to explain the error.
39+
func Contains(t *testing.T, jsonData []byte, jsonObject string, msgAndArgs ...interface{}) {
40+
var data interface{}
41+
require.NoError(t, json.Unmarshal(jsonData, &data))
42+
q, err := gojq.Parse("contains(" + jsonObject + ")")
43+
require.NoError(t, err)
44+
i := q.Run(data)
45+
v, ok := i.Next()
46+
require.True(t, ok)
47+
require.IsType(t, true, v)
48+
if !v.(bool) {
49+
msg := fmt.Sprintf("json data does not contain: %s", jsonObject)
50+
require.FailNow(t, msg, msgAndArgs...)
51+
}
52+
}
53+
54+
// NotContains check if the json object is NOT a subset of the jsonData.
55+
// If the output match the test fails. If msgAndArgs are provided they
56+
// will be used to explain the error.
57+
func NotContains(t *testing.T, jsonData []byte, jsonObject string, msgAndArgs ...interface{}) {
58+
var data interface{}
59+
require.NoError(t, json.Unmarshal(jsonData, &data))
60+
q, err := gojq.Parse("contains(" + jsonObject + ")")
61+
require.NoError(t, err)
62+
i := q.Run(data)
63+
v, ok := i.Next()
64+
require.True(t, ok)
65+
require.IsType(t, true, v)
66+
if v.(bool) {
67+
msg := fmt.Sprintf("json data contains: %s", jsonObject)
68+
require.FailNow(t, msg, msgAndArgs...)
69+
}
70+
}
71+
72+
// Len check if the size of the json object match the given value.
73+
// If the lenght doesn't match the test fails. If msgAndArgs are provided they
74+
// will be used to explain the error.
75+
func Len(t *testing.T, jsonData []byte, expectedLen int, msgAndArgs ...interface{}) {
76+
var data interface{}
77+
require.NoError(t, json.Unmarshal(jsonData, &data))
78+
q, err := gojq.Parse("length")
79+
require.NoError(t, err)
80+
i := q.Run(data)
81+
v, ok := i.Next()
82+
require.True(t, ok)
83+
require.IsType(t, expectedLen, v)
84+
if v.(int) != expectedLen {
85+
msg := fmt.Sprintf("json data length does not match: expected=%d, actual=%d", expectedLen, v.(int))
86+
require.FailNow(t, msg, msgAndArgs...)
87+
}
88+
}
89+
90+
// Empty check if the size of the json object is zero.
91+
// If the lenght is not zero the test fails. If msgAndArgs are provided they
92+
// will be used to explain the error.
93+
func Empty(t *testing.T, jsonData []byte, msgAndArgs ...interface{}) {
94+
var data interface{}
95+
require.NoError(t, json.Unmarshal(jsonData, &data))
96+
q, err := gojq.Parse("length")
97+
require.NoError(t, err)
98+
i := q.Run(data)
99+
v, ok := i.Next()
100+
require.True(t, ok)
101+
require.IsType(t, 0, v)
102+
if v.(int) != 0 {
103+
require.FailNow(t, "json data is not empty", msgAndArgs...)
104+
}
105+
}
106+
107+
// NotEmpty check if the size of the json object is greater than zero.
108+
// If the lenght is not greater than zero the test fails. If msgAndArgs are provided they
109+
// will be used to explain the error.
110+
func NotEmpty(t *testing.T, jsonData []byte, msgAndArgs ...interface{}) {
111+
var data interface{}
112+
require.NoError(t, json.Unmarshal(jsonData, &data))
113+
q, err := gojq.Parse("length")
114+
require.NoError(t, err)
115+
i := q.Run(data)
116+
v, ok := i.Next()
117+
require.True(t, ok)
118+
require.IsType(t, 0, v)
119+
if v.(int) == 0 {
120+
require.FailNow(t, "json data is empty", msgAndArgs...)
121+
}
122+
}

requirejson/json_test.go

+38
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
//
2+
// Copyright 2022 Cristian Maglie. All rights reserved.
3+
// Use of this source code is governed by a BSD-style
4+
// license that can be found in the LICENSE file.
5+
//
6+
7+
package requirejson_test
8+
9+
import (
10+
"testing"
11+
12+
"go.bug.st/testifyjson/requirejson"
13+
)
14+
15+
func TestJSONQuery(t *testing.T) {
16+
in := []byte(`
17+
{
18+
"id" : 1,
19+
"list" : [
20+
10, 20, 30
21+
],
22+
"emptylist" : []
23+
}
24+
`)
25+
requirejson.Query(t, in, ".list", "[10, 20, 30]")
26+
requirejson.Query(t, in, ".list.[1]", "20")
27+
28+
requirejson.Contains(t, in, `{ "list": [ 30 ] }`)
29+
requirejson.NotContains(t, in, `{ "list": [ 50 ] }`)
30+
31+
in2 := []byte(`[ ]`)
32+
requirejson.Empty(t, in2)
33+
requirejson.Len(t, in2, 0)
34+
35+
in3 := []byte(`[ 10, 20, 30 ]`)
36+
requirejson.NotEmpty(t, in3)
37+
requirejson.Len(t, in3, 3)
38+
}

0 commit comments

Comments
 (0)