自动化断言-JSON

[TOC]

JSON对比

JSONassert

基本用法

public void test() throws JSONException {
  String expected = "{\"code\":0,\"data\":{\"items\":[1,2]}}";
  String actual = "{\"code\":0,\"data\":{\"items\":[1,2]},\"msg\":\"hi\"}";
  JSONAssert.assertEquals(expected, actual, JSONCompareMode.LENIENT);
}

JSONCompareMode

总的来说,对比模式就是以下两个检查点的组合:

  1. JSON 是否可扩展(extensible):如:预期{"k1":1},实际{"k1":1,"k2":2},新增了k2,视为扩展
  2. JSON 中的数组是否严格排序(strict array ordering):如:预期{"k1":[1,2,3]},实际{"k1":[2,3,1]},数组顺序不同,视为未严格排序
模式 说明
严格模式(STRICT) 不可扩展, 数组严格排序
宽大模式(LENIENT) 可扩展,数组不用排序
不可扩展模式(NON_EXTENSIBLE) 如字面意思,可忽略数组排序
严格排序模式(STRICT_ORDER) 如字面意思,可扩展

举例

// 预期
{"code":0,"data":{"items":[1,2]}}
// 严格模式:pass
{"code":0,"data":{"items":[1,2]}}
// 宽大模式:pass
{"code":0,"data":{"items":[1,2]},"msg":"hi"}
{"code":0,"data":{"items":[2,1]}}
// 不可扩展模式:pass
{"code":0,"data":{"items":[2,1]}}
// 严格排序模式:pass
{"code":0,"data":{"items":[1,2]},"msg":"hi"}

自定义模式

举例,我们想忽略一些字段 ,假设是 updateTime{"code":0,"data":{"items":{"name":"nana","updateTime":1590206371049}}}

方法一,使用宽大模式或严格排序模式,把预期结果写成{"code":0,"data":{"items":{"name":"nana"}}}

方法二,在以上 4 种模式的基础上,自定义忽略 updateTime 的校验

// demo 1
 public void test() throws JSONException {
     String expected = "{\"code\":0,\"data\":{\"item\":{\"name\":\"nana\",\"updateTime\":1590206371049}}}";
     String actual = "{\"code\":0,\"data\":{\"item\":{\"name\":\"nana\",\"updateTime\":123}}}";
     CustomComparator comparator = new CustomComparator(JSONCompareMode.LENIENT, 
             new Customization("data.item.updateTime", (o1, o2) -> true));
     JSONAssert.assertEquals(expected, actual, comparator);
 }

// demo 2
public void testArray() throws JSONException {
     String expected = "{\"code\":0,\"data\":{\"items\":[{\"name\":\"nana\",\"updateTime\":1590206371049},{\"name\":\"lili\",\"updateTime\":1590206371050}]}}";
     String actual ="{\"code\":0,\"data\":{\"items\":[{\"name\":\"nana\",\"updateTime\":123},{\"name\":\"lili\",\"updateTime\":123}]}}";
     CustomComparator comparator = new CustomComparator(JSONCompareMode.LENIENT,
             new Customization("data.items[*].updateTime", (o1, o2) -> true));
     JSONAssert.assertEquals(expected, actual, comparator);
 }

JSON内容提取

fastjson

基本用法

public void testFastjson() {
    String actual = "{\"code\":0}";
    JSONObject jsonObject = JSONObject.parseObject(actual);
    SoftAssert softAssert = new SoftAssert();
    // key 不存在时,返回null
    softAssert.assertEquals(jsonObject.get("code1"), 0, "fastjson:jsonObject.get");
    // key 不存在时,默认返回 0,导致断言依然会通过,不建议使用
    softAssert.assertEquals(jsonObject.getIntValue("code1"), 0, "fastjson:getIntValue.getIntValue");
    softAssert.assertAll();
}

jackson

基本用法

public void testJackson() throws JsonProcessingException {
    String actual = "{\"code\":0}";
    JsonMapper mapper = new JsonMapper();
    JsonNode jsonNode = mapper.readTree(actual);
    SoftAssert softAssert = new SoftAssert();
    // jsonNode.get:key 不存在时,抛 NullPointerException 异常
    softAssert.assertEquals(jsonNode.get("code1").asInt(), 0, "jackson:jsonNode.get");
    // jsonNode.asInt:key 不存在时,默认返回 0,但也可以设置默认值
    // 如果要用 asXXX 方法,一定要默认值与预期结果不同
    softAssert.assertEquals(jsonNode.at("/code1").asInt(), 0, "jackson:jsonNode.at");
    softAssert.assertEquals(jsonNode.at("/code1").asInt(-1), 0, "jsonNode.at");
    softAssert.assertAll();
}

JsonPath

基本用法

public void testJsonpath() {
    String actual = "{\"code\":0}";
    // JsonPath.read:没有 key 时,抛 PathNotFoundException 异常
    Assert.assertEquals((int) JsonPath.read(actual, "$.code1"), 0, "JsonPath:JsonPath.read");
}

提取 JOSN 字符串总结

  1. 推荐使用 JsonPath,通过路径可以灵活地获取数据,支持正则提取、列表提取,传送门
  2. fastjson 的坑:禁止使用jsonObject.getXXXValue("key"), 原因是 key 不存在时,依然有默认值,默认值和预期结果相同时,就暴露不出问题
  3. jackson 的坑:禁止使用jsonNode.at("path").asXXX(),原因与 fastjson 相同。但 jackson 允许手动设置默认值,假设预期结果是 0,则提取时应手动设置非 0 默认值asInt(-1)

JSON Schema

JSON Schema 是验证 JSON 数据结构的强大工具,简单介绍:Understanding JSON Schema

实现

目前,JSON Schema 已发布的最新草案是 2019-09(Draft 8)。各语言目前实现到了Draft 7。参考:Implementations

  • Java版本实现推荐:json-schema-validator draft-07, -06, -04 Support OpenAPI 3.0 with Jackson parser (Apache License 2.0) - 已封装
    public void testJsonSchema() {
        String actual = "{\"code\":0}";
        String expectedJsonSchema = "{\"type\":\"object\",\"properties\":{\"code\":{\"type\":\"number\",\"const\":1}}}";
        Checker.assertJsonSchema(expectedJsonSchema, actual);
    }
    
    public static void assertJsonSchema(String schema, String instance, String... messages) {
        ObjectMapper mapper = new ObjectMapper();
        String message = "";
        if (messages.length > 0) message = messages[0] + "\n";
        try {
            ObjectNode expected = (ObjectNode) mapper.readTree(schema);
            JsonNode actual = mapper.readTree(instance);
            // 使用 draft-07 规则
            expected.put("$schema", "http://json-schema.org/draft-07/schema#");
            JsonSchemaFactory validatorFactory = JsonSchemaFactory.
                    builder(JsonSchemaFactory.getInstance(SpecVersion.VersionFlag.V7)).
                    objectMapper(mapper).
                    build();
            JsonSchema jsonSchema = validatorFactory.getSchema(expected);
            Set errors = jsonSchema.validate(actual);
            Assert.assertTrue(errors.size() == 0, message + errors.toString());
        } catch (IOException e) {
            Assert.fail(message + "字符串转 JSON 失败");
        }
    }
    
  • Web (Online)推荐:JSON Schema Validator draft-07, -06, -04, -03

你可能感兴趣的:(自动化断言-JSON)