Skip to content

Commit 35b91d3

Browse files
committed
Added flow-like style API
1 parent 0fe2cc9 commit 35b91d3

File tree

2 files changed

+138
-72
lines changed

2 files changed

+138
-72
lines changed

requirejson/json.go

+109-71
Original file line numberDiff line numberDiff line change
@@ -15,107 +15,145 @@ import (
1515
"github.com/stretchr/testify/require"
1616
)
1717

18+
// JQObject is an object that abstract operations on JSON data.
19+
type JQObject struct {
20+
t *testing.T
21+
data interface{}
22+
}
23+
24+
func (obj *JQObject) String() string {
25+
r, err := json.Marshal(obj.data)
26+
require.NoError(obj.t, err)
27+
return string(r)
28+
}
29+
30+
// Parse creates a new JQObect from the given jsonData.
31+
// If jsonData is not a valid json the test fails.
32+
func Parse(t *testing.T, jsonData []byte, msgAndArgs ...interface{}) *JQObject {
33+
var data interface{}
34+
require.NoError(t, json.Unmarshal(jsonData, &data), msgAndArgs...)
35+
return &JQObject{t: t, data: data}
36+
}
37+
38+
// Query performs a query on the given JQObject and returns the first result. If the query
39+
// produces no result the test will fail.
40+
func (obj *JQObject) Query(jqQuery string) *JQObject {
41+
q, err := gojq.Parse(jqQuery)
42+
require.NoError(obj.t, err)
43+
iter := q.Run(obj.data)
44+
data, ok := iter.Next()
45+
require.True(obj.t, ok)
46+
return &JQObject{t: obj.t, data: data}
47+
}
48+
49+
// MustEqual tests if the JQObject equals jsonExpected. If the check
50+
// is not successful the test will fail. If msgAndArgs are provided they
51+
// will be used to explain the error.
52+
func (obj *JQObject) MustEqual(jsonExpected string, msgAndArgs ...interface{}) {
53+
jsonActual, err := json.Marshal(obj.data)
54+
require.NoError(obj.t, err)
55+
require.JSONEq(obj.t, jsonExpected, string(jsonActual), msgAndArgs...)
56+
}
57+
58+
// MustContain tests if jsonExpected is a subset of the JQObject. If the check
59+
// is not successful the test will fail. If msgAndArgs are provided they
60+
// will be used to explain the error.
61+
func (obj *JQObject) MustContain(jsonExpected string, msgAndArgs ...interface{}) {
62+
v := obj.Query("contains(" + jsonExpected + ")").data
63+
require.IsType(obj.t, true, v)
64+
if !v.(bool) {
65+
msg := fmt.Sprintf("json data does not contain: %s", jsonExpected)
66+
require.FailNow(obj.t, msg, msgAndArgs...)
67+
}
68+
}
69+
70+
// MustNotContain tests if the JQObject does not contain jsonExpected. If the check
71+
// is not successful the test will fail. If msgAndArgs are provided they
72+
// will be used to explain the error.
73+
func (obj *JQObject) MustNotContain(jsonExpected string, msgAndArgs ...interface{}) {
74+
v := obj.Query("contains(" + jsonExpected + ")").data
75+
require.IsType(obj.t, true, v)
76+
if v.(bool) {
77+
msg := fmt.Sprintf("json data contains: %s", jsonExpected)
78+
require.FailNow(obj.t, msg, msgAndArgs...)
79+
}
80+
}
81+
82+
// LengthMustEqualTo tests if the size of JQObject match expectedLen. If the check
83+
// is not successful the test will fail. If msgAndArgs are provided they
84+
// will be used to explain the error.
85+
func (obj *JQObject) LengthMustEqualTo(expectedLen int, msgAndArgs ...interface{}) {
86+
v := obj.Query("length").data
87+
require.IsType(obj.t, expectedLen, v)
88+
if v.(int) != expectedLen {
89+
msg := fmt.Sprintf("json data length does not match: expected=%d, actual=%d", expectedLen, v.(int))
90+
require.FailNow(obj.t, msg, msgAndArgs...)
91+
}
92+
}
93+
94+
// MustBeEmpty test if the size of JQObject is 0. If the check
95+
// is not successful the test will fail. If msgAndArgs are provided they
96+
// will be used to explain the error.
97+
func (obj *JQObject) MustBeEmpty(msgAndArgs ...interface{}) {
98+
v := obj.Query("length").data
99+
require.IsType(obj.t, 0, v)
100+
if v.(int) != 0 {
101+
require.FailNow(obj.t, "json data is not empty", msgAndArgs...)
102+
}
103+
}
104+
105+
// MustNotBeEmpty test if the size of JQObject is not 0. If the check
106+
// is not successful the test will fail. If msgAndArgs are provided they
107+
// will be used to explain the error.
108+
func (obj *JQObject) MustNotBeEmpty(msgAndArgs ...interface{}) {
109+
v := obj.Query("length").data
110+
require.IsType(obj.t, 0, v)
111+
if v.(int) == 0 {
112+
require.FailNow(obj.t, "json data is empty", msgAndArgs...)
113+
}
114+
}
115+
18116
// 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.
117+
// on the given jsonData and the result is compared with jsonExpected.
20118
// If the output doesn't match the test fails. If msgAndArgs are provided they
21119
// will be used to explain the error.
22120
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-
q, err := gojq.Parse(jqQuery)
26-
require.NoError(t, err)
27-
i := q.Run(data)
28-
v, ok := i.Next()
29-
require.True(t, ok)
30-
res, err := json.Marshal(v)
31-
require.NoError(t, err)
32-
require.JSONEq(t, jsonExpected, string(res), msgAndArgs...)
121+
data := Parse(t, jsonData)
122+
v := data.Query(jqQuery)
123+
v.MustEqual(jsonExpected, msgAndArgs...)
33124
}
34125

35126
// Contains check if the json object is a subset of the jsonData.
36127
// If the output doesn't match the test fails. If msgAndArgs are provided they
37128
// will be used to explain the error.
38129
func Contains(t *testing.T, jsonData []byte, jsonObject string, msgAndArgs ...interface{}) {
39-
var data interface{}
40-
require.NoError(t, json.Unmarshal(jsonData, &data))
41-
q, err := gojq.Parse("contains(" + jsonObject + ")")
42-
require.NoError(t, err)
43-
i := q.Run(data)
44-
v, ok := i.Next()
45-
require.True(t, ok)
46-
require.IsType(t, true, v)
47-
if !v.(bool) {
48-
msg := fmt.Sprintf("json data does not contain: %s", jsonObject)
49-
require.FailNow(t, msg, msgAndArgs...)
50-
}
130+
Parse(t, jsonData).MustContain(jsonObject, msgAndArgs...)
51131
}
52132

53133
// NotContains check if the json object is NOT a subset of the jsonData.
54134
// If the output match the test fails. If msgAndArgs are provided they
55135
// will be used to explain the error.
56136
func NotContains(t *testing.T, jsonData []byte, jsonObject string, msgAndArgs ...interface{}) {
57-
var data interface{}
58-
require.NoError(t, json.Unmarshal(jsonData, &data))
59-
q, err := gojq.Parse("contains(" + jsonObject + ")")
60-
require.NoError(t, err)
61-
i := q.Run(data)
62-
v, ok := i.Next()
63-
require.True(t, ok)
64-
require.IsType(t, true, v)
65-
if v.(bool) {
66-
msg := fmt.Sprintf("json data contains: %s", jsonObject)
67-
require.FailNow(t, msg, msgAndArgs...)
68-
}
137+
Parse(t, jsonData).MustNotContain(jsonObject, msgAndArgs...)
69138
}
70139

71140
// Len check if the size of the json object match the given value.
72141
// If the lenght doesn't match the test fails. If msgAndArgs are provided they
73142
// will be used to explain the error.
74143
func Len(t *testing.T, jsonData []byte, expectedLen int, msgAndArgs ...interface{}) {
75-
var data interface{}
76-
require.NoError(t, json.Unmarshal(jsonData, &data))
77-
q, err := gojq.Parse("length")
78-
require.NoError(t, err)
79-
i := q.Run(data)
80-
v, ok := i.Next()
81-
require.True(t, ok)
82-
require.IsType(t, expectedLen, v)
83-
if v.(int) != expectedLen {
84-
msg := fmt.Sprintf("json data length does not match: expected=%d, actual=%d", expectedLen, v.(int))
85-
require.FailNow(t, msg, msgAndArgs...)
86-
}
144+
Parse(t, jsonData).LengthMustEqualTo(expectedLen, msgAndArgs...)
87145
}
88146

89147
// Empty check if the size of the json object is zero.
90148
// If the lenght is not zero the test fails. If msgAndArgs are provided they
91149
// will be used to explain the error.
92150
func Empty(t *testing.T, jsonData []byte, msgAndArgs ...interface{}) {
93-
var data interface{}
94-
require.NoError(t, json.Unmarshal(jsonData, &data))
95-
q, err := gojq.Parse("length")
96-
require.NoError(t, err)
97-
i := q.Run(data)
98-
v, ok := i.Next()
99-
require.True(t, ok)
100-
require.IsType(t, 0, v)
101-
if v.(int) != 0 {
102-
require.FailNow(t, "json data is not empty", msgAndArgs...)
103-
}
151+
Parse(t, jsonData).MustBeEmpty(msgAndArgs...)
104152
}
105153

106-
// NotEmpty check if the size of the json object is greater than zero.
107-
// If the lenght is not greater than zero the test fails. If msgAndArgs are provided they
154+
// NotEmpty check if the size of the json object is not zero.
155+
// If the lenght is zero the test fails. If msgAndArgs are provided they
108156
// will be used to explain the error.
109157
func NotEmpty(t *testing.T, jsonData []byte, msgAndArgs ...interface{}) {
110-
var data interface{}
111-
require.NoError(t, json.Unmarshal(jsonData, &data))
112-
q, err := gojq.Parse("length")
113-
require.NoError(t, err)
114-
i := q.Run(data)
115-
v, ok := i.Next()
116-
require.True(t, ok)
117-
require.IsType(t, 0, v)
118-
if v.(int) == 0 {
119-
require.FailNow(t, "json data is empty", msgAndArgs...)
120-
}
158+
Parse(t, jsonData).MustNotBeEmpty(msgAndArgs...)
121159
}

requirejson/json_test.go

+29-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,35 @@ import (
1212
"go.bug.st/testifyjson/requirejson"
1313
)
1414

15-
func TestJSONQuery(t *testing.T) {
15+
func TestJSONFlowParadigm(t *testing.T) {
16+
in := requirejson.Parse(t, []byte(`
17+
{
18+
"id" : 1,
19+
"list" : [
20+
10, 20, 30
21+
],
22+
"emptylist" : []
23+
}
24+
`))
25+
26+
in.Query(".list").MustEqual("[10, 20, 30]")
27+
in.Query(".list.[1]").MustEqual("20")
28+
29+
in.MustContain(`{ "list": [ 30 ] }`)
30+
in.MustNotContain(`{ "list": [ 50 ] }`)
31+
32+
in.Query(".list | length").MustEqual("3")
33+
34+
in2 := requirejson.Parse(t, []byte(`[ ]`))
35+
in2.MustBeEmpty()
36+
in2.LengthMustEqualTo(0)
37+
38+
in3 := requirejson.Parse(t, []byte(`[ 10, 20, 30 ]`))
39+
in3.MustNotBeEmpty()
40+
in3.LengthMustEqualTo(3)
41+
}
42+
43+
func TestJSONAssertions(t *testing.T) {
1644
in := []byte(`
1745
{
1846
"id" : 1,

0 commit comments

Comments
 (0)