Checking JSON - hyperskill/hs-test GitHub Wiki
About
The testing library provides a powerful tool to test JSON objects based on static expect
method described in the Presentation error section. To test JSON, you need to write expect(string).asJson().check(template)
. After checking you are guaranteed to have correct JSON because otherwise testing library will throw Wrong answer
automatically.
Creating templates
All the static methods you need are located in org.hyperskill.hstest.testing.expect.json.JsonChecker
class. It is recommended to statically import all you need from there. If you want to use regular expression patterns to check string values, you may want to statically import method compile
as well.
import static java.util.regex.Pattern.compile;
import static org.hyperskill.hstest.testing.expect.json.JsonChecker.*;
You can create templates with the following methods:
any()
matches any object, array, number, or null.isObject(...)
matches JSON objectisArray(...)
matches JSON arrayisString(...)
matches JSON stringisNumber(...)
matches JSON numberisInteger(...)
matches JSON number that can be represented as an integerisDouble(...)
matches JSON number that can be represented as a doubleisBoolean(...)
matches JSON boolisNull()
matches JSON null
All the methods return objects that inherit JsonBaseBuilder
so you can use them to check objects' values and arrays' items.
Building template for an object
isObject(...)
returns JsonObjectBuilder
and can be configured using .value(...)
methods described below.
Available methods to create object template:
isObject()
without configuration matches an empty objectisObject(JsonAnyBuilder unused)
is only used to writeisObject(any())
which matches an object with any key-value pairs. Cannot be configured.
Available methods to configure object template:
.value(String key, int value)
matches object that contains keykey
with valuevalue
.value(String key, double value)
same as 1.value(String key, boolean value)
same as 1.value(String key, String value)
same as 1.value(String key, Pattern valueRegex)
matches object that contains keykey
with string value that matches patternvalueRegex
.value(String key, int... values)
matches object that contains keykey
with value as an array ofvalues
.value(String key, double... value)
same as 6.value(String key, boolean... value)
same as 6.value(String key, String... value)
same as 6.value(Pattern keyRegex, int value)
matches object that contains key that matches patternkeyRegex
with valuevalue
.value(Pattern keyRegex, double value)
same as 10.value(Pattern keyRegex, boolean value)
same as 10.value(Pattern keyRegex, String value)
same as 10.value(Pattern keyRegex, Pattern valueRegex)
matches object that contains key that matches patternkeyRegex
with string value that matches patternvalueRegex
.value(Pattern keyRegex, int... values)
matches object that contains key that matches patternkeyRegex
with value as an array ofvalues
.value(Pattern keyRegex, double... value)
same as 15.value(Pattern keyRegex, boolean... value)
same as 15.value(Pattern keyRegex, String... value)
same as 15.value(StringChecker key, int value)
matches object that contains key that matches checkerkey
with valuevalue
.value(StringChecker key, double value)
same as 19.value(StringChecker key, boolean value)
same as 19.value(StringChecker key, String value)
same as 19.value(StringChecker key, Pattern valueRegex)
matches object that contains key that matches checkerkey
with value that matches patternvalueRegex
.value(StringChecker key, int... values)
matches object that contains key that matches checkerkey
with value as an array ofvalues
.value(StringChecker key, double... values)
same as 24.value(StringChecker key, boolean... values)
same as 24.value(StringChecker key, String... values)
same as 24.value(String key, JsonBaseBuilder value)
matches object that contains keykey
with value that matches templatevalue
.value(Pattern regex, JsonBaseBuilder value)
matches object that contains key that matches patternkeyRegex
with value that matches templatevalue
.value(StringChecker key, JsonBaseBuilder value)
matches object that contains key that matches checkerkey
with value that matches templatevalue
StringChecker
is a functional interface that takes a String
key and returns a boolean. If it returns true, the match is considered successful. For example, checker key -> key.length() < 10
would match a key that has a length fewer than 10 characters.
To test the following JSON object:
{
"date": "2019-10-11",
"time": "20:11",
"cost": 12.32,
"pic": "qwe.png",
"all_years": [2011, 2013, 2014, 2020]
}
You can use the following template:
isObject()
.value("date", compile("\\d{4}-\\d{2}-\\d{2}"))
.value("time", compile("\\d{2}:\\d{2}"))
.value("cost", isNumber())
.value(compile("pic|img"), isString(s -> s.endsWith(".png"))
.value(key -> key.endsWith("years"), 2011, 2013, 2014, 2020)
Building template for an array
isArray(...)
returns JsonArrayBuilder
and can be configured using .item(...)
methods described below.
Available methods to create array template:
isArray()
without configuration matches an empty arrayisArray(int length)
matches an array oflength
elementsisArray(JsonBaseBuilder itemsTemplate)
matches an array whose elements matchitemsTemplate
. Example:isArray(isInteger())
matches an array of any integers,isArray(any())
matches any array.isArray(int length, JsonBaseBuilder itemsTemplate)
matches an array oflength
elements and all of them should matchitemsTemplate
isArray(int... values)
matches an array ofvalues
isArray(double... values)
same as 5isArray(boolean... values)
same as 5isArray(String... values)
same as 5isArray(ArrayLengthChecker lengthChecker)
matches an array whose length matcheslengthChecker
isArray(ArrayLengthChecker lengthChecker, JsonBaseBuilder itemsTemplate)
matches an array whose length matcheslengthChecker
and items matchitemsTemplate
template
ArrayLengthChecker
is a functional interface that takes an integer key and returns a boolean. If it returns true, the match is considered successful. For example, checker len -> len < 10
would match an array with less than 10 elements.
Available methods to configure array template:
.item(int value)
matches array whose next element is equal tovalue
.item(double value)
same as 1.item(boolean value)
same as 1.item(String value)
same as 1.item(Pattern regex)
matches array whose next element is a string that matches patternregex
..item(JsonBaseBuilder value)
matches array whose next element matches templatevalue
. Example:.item(isInteger())
matches any integer item,.item(any())
matches any item..anyOtherItems()
in case you allow any other values in a JSON object besides those that you have already configured. After calling this method you can't configure other values so this method should be last to call.
To test the following JSON array:
[
{
"num": 12,
"why": "flew high"
},
{
"number": 23,
"reason": "flew fast"
},
45,
null,
false
]
You can use the following template:
isArray()
.item(isObject()
.value("num", isInteger(i -> i == 12 || i == 13))
.value(compile("..y"), "flew high"))
.item(isObject()
.value("number", 23)
.value("reason", "flew fast"))
.item(45)
.item(isNull())
.anyOtherItems())
Checking strings
isString(...)
returns `JsonStringBuilder.
Available methods to create string template:
isString()
matches any stringisString(String value)
matches string that is equal tovalue
isString(Pattern regex)
matches string that matches patternregex
isString(StringChecker checker)
matched string that matcheschecker
StringChecker
is a functional interface that takes a String
and returns a boolean. If it returns true, the match is considered successful. For example, checker str -> str.length() < 10
would match a string whose length is less than 10.
Checking numbers
isNumber(...)
returns JsonNumberBuilder
or JsonIntegerBuilder
or JsonDoubleBuilder
based on arguments passed to it.
isInteger(...)
returns JsonIntegerBuilder
isDouble(...)
returns JsonDoubleBuilder
Available methods to create number template:
isNumber()
matches any number, integer or doubleisNumber(int value)
matches integervalue
isNumber(double value)
matches doublevalue
isInteger()
matches any integerisInteger(int value)
same as 2isInteger(IntegerChecker checker)
matches integer that matcheschecker
isDouble()
matches any doubleisDouble(double value)
same as 3isDouble(DoubleChecker checker)
matches double that matcheschecker
IntegerChecker
is a functional interface that takes an integer and returns a boolean. If it returns true, the match is considered successful. For example, checker num -> num < 10
would match an integer that is less than 10.
DoubleChecker
is a functional interface that takes a double and returns a boolean. If it returns true, the match is considered successful. For example, checker num -> num < 0.5
would match a double that is less than 0.5.
Checking boolean
isBoolean(...)
returns JsonBooleanBuilder
.
Available methods to create boolean template:
isBoolean()
matches any booleanisBoolean(boolean value)
matches booleanvalue
isBoolean(BooleanChecker checker)
matches boolean that matcheschecker
BooleanChecker
is a functional interface that takes a boolean and returns a boolean. If it returns true, the match is considered successful. For example bool -> bool == computeOtherBool()
.
Complex example
For a given JSON:
{
"name": "123",
"car": {
"model": "Tesla Roadster",
"year": 2018,
"repairs": [
{
"date": "2019-10-11",
"time": "20:11",
"cost": 12.32,
"pic": "qwe.png",
"all_years": [2011, 2013, 2014, 2020]
},
{
"date": "2019-11-12",
"time": "21:12",
"cost": 34,
"img": "wer.png",
"all_years": [2011, 2013, 2014, 2020]
}
]
},
"rocket": {
"name": "Falcon 9",
"launches": "89+",
"mentioned": [
12, 34, 56
],
"memorable": [
{
"num": 12,
"why": "flew high"
},
{
"number": 23,
"reason": "flew fast"
},
45,
null,
false
]
},
"options": {
"12": null
}
}
You can check it with the following template:
isObject()
.value("name", isString())
.value("car", isObject()
.value("model", "Tesla Roadster")
.value("year", 2018)
.value("repairs", isArray(2,
isObject()
.value("date", compile("\\d{4}-\\d{2}-\\d{2}"))
.value("time", compile("\\d{2}:\\d{2}"))
.value("cost", isNumber())
.value(compile("pic|img"), isString(s -> s.endsWith(".png")))
.value(key -> key.endsWith("years"), 2011, 2013, 2014, 2020))))
.value("rocket", isObject()
.value("name", "Falcon 9")
.value("launches", compile("[0-9]+\\+"))
.value("mentioned", isArray(12, 34, 56))
.value("memorable", isArray()
.item(isObject()
.value("num", isInteger(i -> i == 12 || i == 13))
.value(compile("..y"), "flew high"))
.item(isObject()
.value("number", 23)
.value("reason", "flew fast"))
.item(45)
.item(isNull())
.anyOtherItems()))
.value("options", any())