美文网首页
自动化断言-JSON

自动化断言-JSON

作者: 臂力棒在想 | 来源:发表于2020-05-23 17:05 被阅读0次

    [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<ValidationMessage> 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

          本文链接:https://www.haomeiwen.com/subject/izrlahtx.html