需求
在 自动化测试过程中,经常需要对API返回结果进行验证。如果结果是一个JSON串,那么预期结果和实际结果比对的常见场景是这样的:
- 可以忽略某些elment/value,如时间戳
- 集合内的元素的出现顺序不保证一致
- 预期结果是实际结果的子集 ,如调用新增接口后查询时只关注刚才新增内容是否存在。
- 上述需求的混合
- 预期结果保存成文件,对比时读入成json对象或者json串。
工具-JsonPath/zson
之前用AssetJ + JsonPath写过一些简单的结果比对,感觉还是需要对语法有一定的熟悉程度要求的。对于普通测试人员来说,有一定的难度。
类似的还有支持xpath语法的工具zson。
工具-JsonUnit
搜索了一下GitHub, 发现了一个Json比较的工具JsonUnit ,看上去比较吸引人,准备后续在项目中试试。
摘录几个feature过来
利用AssertJ实现前述需求
import static net.javacrumbs.jsonunit.assertj.JsonAssertions.assertThatJson;
import static net.javacrumbs.jsonunit.assertj.JsonAssertions.json;
...
// compares two JSON documents (note lenient parsing of expected value)
assertThatJson("{\"a\":1, \"b\":2}").isEqualTo("{b:2, a:1}");
// objects are automatically serialized before comparison
assertThatJson(jsonObject).isEqualTo("{\n\"test\": 1\n}");
// AssertJ map assertions (numbers are converted to BigDecimals)
assertThatJson("{\"a\":1}").isObject().containsEntry("a", BigDecimal.valueOf(1));
// Type placeholders
assertThatJson("{\"a\":1, \"b\": {\"c\" :3}}").isObject().containsValue(json("{\"c\" :\"${json-unit.any-number}\"}"));
// AssertJ array assertion
assertThatJson("{\"a\":[{\"b\": 1}, {\"c\": 1}, {\"d\": 1}]}").node("a").isArray().contains(json("{\"c\": 1}"));
// Can ignore array order
assertThatJson("{\"a\":[{\"b\": 1}, {\"c\": 1}, {\"d\": 1}]}").when(Option.IGNORING_ARRAY_ORDER).node("a").isArray()
.isEqualTo(json("[{\"c\": 1}, {\"b\": 1} ,{\"d\": 1}]"));
// custom matcher
assertThatJson("{\"test\":-1}")
.withConfiguration(c -> c.withMatcher("positive", greaterThan(valueOf(0))))
.isEqualTo("{\"test\": \"${json-unit.matches:positive}\"}");
// and
assertThatJson("{\"test\":{\"a\":1, \"b\":2, \"c\":3}}").and(
a -> a.node("test.a").isEqualTo(1),
a -> a.node("test.b").isEqualTo(2)
);
使用时只要引用json-unit-assertj即可
<dependency>
<groupId>net.javacrumbs.json-unit</groupId>
<artifactId>json-unit-assertj</artifactId>
<version>2.7.0</version>
<scope>test</scope>
</dependency>
比较时的可选项Options
Options
There are multiple options how you can configure the comparison
TREATING_NULL_AS_ABSENT - fields with null values are equivalent to absent fields. For example, this test passes
assertJsonEquals("{\"test\":{\"a\":1}}",
"{\"test\":{\"a\":1, \"b\": null, \"c\": null}}",
when(TREATING_NULL_AS_ABSENT));
IGNORING_ARRAY_ORDER - ignores order in arrays
assertJsonEquals("{\"test\":[1,2,3]}",
"{\"test\":[3,2,1]}",
when(IGNORING_ARRAY_ORDER));
IGNORING_EXTRA_ARRAY_ITEMS - ignores unexpected array items
assertJsonEquals("{\"test\":[1,2,3]}",
"{\"test\":[1,2,3,4]}",
when(IGNORING_EXTRA_ARRAY_ITEMS));
assertJsonEquals("{\"test\":[1,2,3]}",
"{\"test\":[5,5,4,4,3,3,2,2,1,1]}",
when(IGNORING_EXTRA_ARRAY_ITEMS, IGNORING_ARRAY_ORDER));
IGNORING_EXTRA_FIELDS - ignores extra fields in the compared value
assertThatJson("{\"test\":{\"a\":1, \"b\":2, \"c\":3}}")
.when(IGNORING_EXTRA_FIELDS)
.isEqualTo("{\"test\":{\"b\":2}}");
IGNORE_VALUES - ignores values and compares only types
assertJsonEquals("{\"test\":{\"a\":1,\"b\":2,\"c\":3}}",
"{\"test\":{\"a\":3,\"b\":2,\"c\":1}}",
when(IGNORING_VALUES));
It is possible to combine options.
assertJsonEquals("{\"test\":[{\"key\":1},{\"key\":2},{\"key\":3}]}",
"{\"test\":[{\"key\":3},{\"key\":2, \"extraField\":2},{\"key\":1}]}",
when(IGNORING_ARRAY_ORDER, IGNORING_EXTRA_FIELDS));
In Hamcrest assertion you can set the option like this
assertThat("{\"test\":{\"a\":1, \"b\":2, \"c\":3}}",
jsonEquals("{\"test\":{\"b\":2}}").when(IGNORING_EXTRA_FIELDS));
json2xml
这个团队还开发了一个json转为xml的工具,还支持了array/attribute等较为复杂的内容
https://github.com/lukas-krecan/json2xml
由于好多金融系统间的协议使用到了XML,但是xml编写比较麻烦,这个功能还是有潜在的使用价值的。
有需要时可以再关注下。