Json是应用非常广泛的数据交换格式,从前端到后端、从web端到App端、异构语言,接口成为桥梁,而Json成为应用最广泛的数据交换格式。在接口开发、测试工作中,为了验证接口处理的正确性,不得不对Json的消息格式、完整性、正确性进行大量的测试校验。

一、Json Schema简介

测试xml用过类似的方法xmllint,如果只是对结果进行基本的检查,利用弱类型语言的特性,使用Python进行反序列化即可验证文件格式是否正确,但如果需要进行类型、数值范围等逻辑测试,通过Json Schema非常适合。Json Schema是定义Json数据约束的一个标准,数据发送方、接收方都可以通过这个约定,进行数据验证保证交换数据的正确性。

Json Schema的优点:

  • 描述已知的数据格式;
  • 提供人类和机器都可以清晰阅读的文档;
  • 为以下场景的数据验证提供帮助;
  • 自动化测试;
  • 确保用户提交的数据质量。

最新的Json Schema版本是draft 07,发布于2018-03-19,draft 08版本也即将于2019年发布。Json Schema常用的一些特性在2013年发布的draft 04版本已基本具备,后续版本更多是一些功能的完善、补强。目前各种编程语言对Json Schema支持最广泛的也是draft 04版本。

二、Json Schema实例

A、Json

Json是JavaScript Object Notation的缩写,是一种简化的数据交换格式。作为互联网服务间进行数据交换最常见的一种交换格式,具有简洁、可读性好等特点。

Json 主要有两种数据结构:

  • object : 是一个无序的「键/值」对集合。一个object 以「{开始」,「}结束」。每个键后跟「:」,「键/值」对间以「,」分隔。

  • array : 是值(value)的有序集合。一个数组以「[开始」,「]结束」。值之间使用「,」分隔。

  • 值(value) 可以是双引号括起来的string、number、true、false、 null、对象(object)或者数组(array),结构可嵌套。大部分现代计算机语言都会以某种形式支持这些数据类型,使得通过Json数据格式在数据结构和编程语言之间交换非常方便。

Json串:

{
    "fruits": [
        "apple",
        "orange",
        "pear"
    ],
    "vegetables": [
        {
            "veggieName": "potato",
            "veggieLike": true,
            "price": 6
        },
        {
            "veggieName": "broccoli",
            "veggieLike": false,
            "price": 8.8
        }
    ]
}

B、Json Schema

Json Schema模式文档本身也是一个多级嵌套的Json格式数据,通过object对象数据格式来定义和约束Json数据的编写规则。

假设对上述Json串进行约束:

  • Json数据是object类型
  • 包含fruits和vegetables两个array类型属性
  • fruits数组为字符类型
  • vegetables数组为object类型
  • vegetables数组中的对象必须包含veggieName、veggieLike、price三个属性
  • 其中price属性是数字类型

则可以编写以下Json Schema进行规制检查:

{
    "type": "object",
    "properties": {
        "fruits": {
            "type": "array",
            "items": {
                "type": "string"
            }
        },
        "vegetables": {
            "type": "array",
            "items": {
                "type": "object",
                "required": [
                    "veggieName",
                    "veggieLike",
                    "price"
                ],
                "properties": {
                    "price": {
                        "type": "number"
                    }
                }
            }
        }
    }
}
现在我也找了很多测试的朋友,做了一个分享技术的交流群,共享了很多我们收集的技术文档和视频教程。
如果你不想再体验自学时找不到资源,没人解答问题,坚持几天便放弃的感受
可以加入我们一起交流。而且还有很多在自动化,性能,安全,测试开发等等方面有一定建树的技术大牛
分享他们的经验,还会分享很多直播讲座和技术沙龙
可以免费学习!划重点!开源的!!!
qq群号:110685036

三、Json数据类型

在Json Schema中使用type关键字来约定数据类型。和Json对应,Json Schema中定义的基本数据类型如下:

  • string
  • Numeric types (integer,number)
  • object
  • array
  • boolean
  • null

A、string

1、约束类型

通过type关键字可约束string类型,即对应的数据对象必须是字符串形式的文本(支持unicode字符)。

{
    "type": "string"
}

2、约束字串长度

还可以通过minLength、 maxLength约束数据值的字串长度。

{
    "type": "string",
    "minLength": 2,
    "maxLength": 3
}

如上约束条件,数据"ab"符合约定,而"abcd"不符合。

3、正则表达式支持

字串约束支持正则表达式描述,使用pattern关键字。

{
    "type": "string",
    "pattern": "^(\\([0-9]{3}\\))?[0-9]{3}-[0-9]{4}$"
}

如上约束条件,字串"(086)010-1234"符合约定,而"(400)13312345678"不符合。

4、格式校验

从draft 04版本开始,通过内建的格式关键字,还可以支持一些通用的固定格式。

1).时间日期格式校验

  • date-time,支持形如,"2018-11-13T20:20:39+00:00",格式的字串
  • time,支持形如,"20:20:39+00:00",格式的字串(draft 07)
  • date,支持形如,"2018-11-13",格式的字串(draft 07)

2). Email格式校验

  • email,支持email格式定义,形如,"isisiwish@com.com"

3).IP格式校验

  • ipv4,验证ipv4格式
  • ipv6,验证ipv6格式

4).验证URI

通过内建的格式关键字,可以方便地完成对一些常见字符类型的格式验证。详细的Format可参见官方文档

B、Numeric

数字类型的type关键字可以取integer和number,integer只能匹配整数、而number可以匹配任何数字(整数或浮点数)。

1、倍数匹配

multipleOf关键字可以校验数据是否为给定条件数据的整数倍。

{
    "type": "number",
    "multipleOf": 10
}

如上定义,数据20、230符合约束,232则不符合。

2、范围匹配

Json Schema提供了4个关键字来支持对数值取值范围的校验。

| 关键字            | 作用    |
|:----------------- |:------  |
| minimum           | 取值 ≥ |
| exclusiveMinimum  | 取值 >  |
| maximum           | 取值 ≤ |
| exclusiveMaximum  | 取值 <  |

按照以下规则,数据0、20、99符合约定,-1、100不符合。

{
    "type": "number",
    "minimum": 0,
    "exclusiveMaximum": 100
}

C、object

object是Json的一种基本结构,约束数据必须为对象类型。

{
    "type": "object"
}

1、properties关键字

properties是描述对象属性约束的关键字,它的取值也必须是个对象,而且也必须符合Json Schema模式。即相当于把对应的校验对象单独拿出,也可使用properties的取值约定进行校验。

{
    "properties": {
        "price": {
            "type": "number"
        }
    }
}

2、propertyNames关键字

propertyNames关键字用于约束属性名称(键名),并且可支持正则表达式。

{
    "type": "object",
    "propertyNames": {
        "pattern": "^[A-Za-z_]*$"
    }
}

3、additionalProperties关键字

additionalProperties关键字和properties关键字配合使用,取值可以是boolean型或者object 。作用是对限制properties定义的属性进行限制。

当additionalProperties取值为boolean型false时,表示除properties中已定义的属性,不允许出现额外的属性。

而当它为object时, 同时需要符合模式描述,用于限制除properties中定义的属性外,额外的属性必须符合该关键字约束。

{
    "type": "object",
    "properties": {
        "number": {
            "type": "number"
        },
        "vegetables": {
            "type": "string"
        },
        "fruits": {
            "type": "string",
            "enum": [
                "pear",
                "watermelon",
                "lemon"
            ]
        }
    },
    "additionalProperties": {
        "type": "string"
    }
}

4、required关键字

required关键字也是配合properties关键字使用,用于约定properties定义中必须包含的属性,取值为一个属性名称的数组列表。

{
    "type": "object",
    "properties": {
        "name": {
            "type": "string"
        },
        "email": {
            "type": "string"
        },
        "address": {
            "type": "string"
        },
        "telephone": {
            "type": "string"
        }
    },
    "required": [
        "name",
        "email"
    ]
}

以上约定,表明对象数据中必须包含name和email两个属性,其他的则不作要求。

5、限定对象长度的关键字

minProperties和maxProperties用来约束object对象长度的关键字,取值需为正整数,且maxProperties应大于minProperties。

{
    "type": "object",
    "minProperties": 2,
    "maxProperties": 4
}

以上约定限制对应的object属性至少有2个,不超过4个。

6、对象数据依赖关键字

dependencies关键字来定义对象属性间的依赖关系。

{
    "type": "object",
    "properties": {
        "name": {
            "type": "string"
        },
        "credit_card": {
            "type": "number"
        },
        "billing_address": {
            "type": "string"
        }
    },
    "required": [
        "name"
    ],
    "dependencies": {
        "credit_card": [
            "billing_address"
        ]
    }
}

以上约束表示,当存在credit_card属性时,也必须存在billing_address属性。

D、array

array是Json的另一种基本数据结构。

1、items关键字

1).约束array类型的主要关键字是items,用于约束数组中每项的取值约束。

{
    "type": "array",
    "items": {
        "type": "number"
    }
}

以上约束了数组每项取值必须为整型。

2).items关键字也可以按数组项顺序逐项对数组进行约定。

{
    "type": "array",
    "items": [
        {
            "type": "number"
        },
        {
            "type": "string",
            "enum": [
                "different",
                "same"
            ]
        },
        {
            "type": "object"
        }
    ]
}

items关键字会对数组中的每一项都进行严格约束,以下Json满足约束条件。

[
    3,
    "different",
    {
        "types": "of values"
    }
]

2、contains关键字(draft 06)

{
    "type": "array",
    "contains": {
        "type": "number"
    }
}

以上约束,会约束数组中只需要包含至少一个符合条件的项即可。

3、additionalItems关键字

additionalItems关键字约束了数组是否允许额外的数组项。

{
    "type": "array",
    "items": [
        {
            "type": "number"
        },
        {
            "type": "string"
        },
        {
            "type": "string",
            "enum": [
                "Street",
                "Avenue",
                "Boulevard"
            ]
        },
        {
            "type": "string",
            "enum": [
                "NW",
                "NE",
                "SW",
                "SE"
            ]
        }
    ],
    "additionalItems": false
}

以上约束,约束数组不允许出现第5个数据项。但符合条件的3项数组是符合定义的。

4、数组长度校验

{
    "type": "array",
    "minItems": 2,
    "maxItems": 3
}

以上约束,约束通过minItems、maxItems可以定义数组的长度,下例定义数组长度至少为2, 不超过3。

5、唯一性校验

uniqueItems是约束数组唯一性的关键字,取值为boolean型,为true时可以约束数组对象中的每一项唯一。

{
    "type": "array",
    "uniqueItems": true
}

E、boolean

布尔型约束通过type关键字约束,取值为true或false。

{
    "type": "boolean"
}

F、null

null型约束通过type关键字约束,取值只可为null。

{
    "type": "null"
}

四、Json Schema通用关键字

A、描述性关键字

描述性关键字在Json Schema中并不会产生实际的约束,但是对于阅读和理解Json Schema中相关约束有非常大的帮助。可以理解为Json Schema对于Json数据的说明文档。

描述性关键字主要包括:

  • title:描述对象的标题
  • Description:对数据进行说明描述
  • default:所描述对象的默认值
  • example:从draft 06支持的关键字,提供当前约束的示例
{
    "title": "Match anything",
    "description": "This is a schema that matches anything.",
    "default": "Default value",
    "examples": [
        "Anything",
        4035
    ]
}

B、枚举关键字

枚举关键字enum是个应用比较广泛的Json Schema关键字,一般用于约束数据在枚举范围内进行取值。

{
    "type": "string",
    "enum": [
        "red",
        "amber",
        "green"
    ]
}

C、const常量关键字

const常量关键字,用于约束数据为固定取值。

{
    "const": "United States of America"
}

D、聚合关键字

聚合关键字是Json Schema中对多个约束进行聚合处理的关键字。

1、allOf

待校验的数据对象满足allOf关键字中给出的所有约束时,才算符合要求。

{
    "allOf": [
        {
            "type": "string"
        },
        {
            "maxLength": 5
        }
    ]
}

2、anyOf

待校验的数据对象满足anyOf关键字中给出的任一约束时,才算符合要求。

{
    "anyOf": [
        {
            "type": "string"
        },
        {
            "type": "number"
        }
    ]
}

3、oneOf

oneOf关键字约束待校验的数据正好符合约束条件中的一项。

{
    "oneOf": [
        {
            "type": "number",
            "multipleOf": 5
        },
        {
            "type": "number",
            "multipleOf": 3
        }
    ]
}

4、not

not关键字约束待校验的数据不是给出的约束条件。

{
    "not": {
        "type": "string"
    }
}

E、条件关键字

从draft 07开始可以支持条件关键字if、then、else可以给出一些约束的互相依赖关系。

{
    "type": "object",
    "properties": {
        "street_address": {
            "type": "string"
        },
        "country": {
            "enum": [
                "United States of America",
                "Canada"
            ]
        }
    },
    "if": {
        "properties": {
            "country": {
                "const": "United States of America"
            }
        }
    },
    "then": {
        "properties": {
            "postal_code": {
                "pattern": "[0-9]{5}(-[0-9]{4})?"
            }
        }
    },
    "else": {
        "properties": {
            "postal_code": {
                "pattern": "[A-Z][0-9][A-Z] [0-9][A-Z][0-9]"
            }
        }
    }
}

以上约束,约束当country是美国时,对应的区号约束条件。

F、结构性关键字

当Json数据量较大,且存在很多雷同的约束时,可以利用结构性关键字来组织多个Json Schema模式文件来组织约束。

definitions关键字可以定义可被引用的约束条件。

{
    "definitions": {
        "address": {
            "type": "object",
            "properties": {
                "street_address": {
                    "type": "string"
                },
                "city": {
                    "type": "string"
                },
                "state": {
                    "type": "string"
                }
            },
            "required": [
                "street_address",
                "city",
                "state"
            ]
        }
    }
}

通过$ref关键字进行引用即可重用一些共用的约束。

{
    "$ref": "#/definitions/address"
}

$ref关键字也可以从其他模式文件中加载引用。

{
    "$ref": "definitions.json#/address"
}

$id关键字可以为一组约束指定一个唯一的id,便于结构化的引用和定义引用跟路径。

{
    "$id": "http://foo.bar/schemas/address.json"
}

五、实例

以下内容来自:http://json-schema.org/learn/getting-started-step-by-step.html

{
  // schema版本(OP)
  "$schema": "http://json-schema.org/draft-07/schema#",
  // schema唯一标识(OP)
  "$id": "http://example.com/product.schema.json",
  // schema标题(OP)
  "title": "Product",
  // schema描述(OP)
  "description": "A product from Acme's catalog",
  // 约束检查对象的类型
  "type": "object",
  // 受约束字段描述
  "properties": {
    // 约束productId类型为整型
    "productId": {
      "description": "The unique identifier for a product",
      "type": "integer"
    },
    // 约束productName类型为string
    "productName": {
      "description": "Name of the product",
      "type": "string"
    },
    // 约束price > 0(不包含0)
    "price": {
      "description": "The price of the product",
      "type": "number",
      "exclusiveMinimum": 0
    },
    // 约束tags为数组类型
    "tags": {
      "description": "Tags for the product",
      "type": "array",
      // 数组内的项类型为string
      "items": {
        "type": "string"
      },
      // 数组至少包含1项
      "minItems": 1,
      // 数组中每项不能重发
      "uniqueItems": true
    },
    "dimensions": {
      "type": "object",
      // 嵌套对象约束检查规则
      "properties": {
        "length": {
          "type": "number"
        },
        "width": {
          "type": "number"
        },
        "height": {
          "type": "number"
        }
      },
      // 必须包含length、width、height
      "required": [ "length", "width", "height" ]
    }
  },
  // 同上
  "required": [ "productId", "productName", "price" ]
}

六、Json Schema在线编辑工具

定义或编写一个Json Schema约束还是一件比较工作量较大也比较复杂的工作,编写中也容易犯一些格式错误,好在工具能帮助节约大量时间。

https://www.jsonschema.net/

通过在线编辑工具,可以很方便地引入引入Json文件,并自动生成Json Schema,然后可视化地完成Json Schema的一些属性定义,极大的简化编写工作量。

七、接口校验中使用Json Schema

Postman在接口测试的脚本扩展上支持了丰富的第三方库,其中就有一个使用Json Schema来进行接口校验的常用库tv4(基于Json Schema的draft 04版本)。

A、编写MockController

@Slf4j
@RestController
public class MyController
{
    @RequestMapping(value = "/testA", produces = "application/json")
    public String testA(HttpServletRequest request)
    {
        String str = "";
        return str;
    }
    
    @RequestMapping(value = "/testB", produces = "application/json")
    public String testB(HttpServletRequest request)
    {
        String str = "";
        return str;
    }
}

1、待测试JsonA

{
    "productId": 1,
    "productName": "An ice sculpture",
    "price": 12.5,
    "tags": [
        "cold",
        "ice"
    ],
    "dimensions": {
        "length": 7,
        "width": 12,
        "height": 9.5
    },
    "warehouseLocation": {
        "latitude": -78.75,
        "longitude": 20.4
    }
}

2、待测试JsonB

{
    "productId": 1,
    "productName": "An ice sculpture",
    "price": "12.50",
    "tags": [
        "cold",
        "ice",
        "ice"
    ],
    "dimensions": {
        "length": 7,
        "height": 9.5
    },
    "warehouseLocation": {
        "latitude": -78.75,
        "longitude": 20.4
    }
}

B、定义Json Schema及tv4校验

var schema = {
    "$schema": "http://json-schema.org/draft-07/schema#",
    "$id": "http://example.com/product.schema.json",
    "title": "Product",
    "description": "A product from Acme's catalog",
    "type": "object",
    "properties": {
        "productId": {
            "description": "The unique identifier for a product",
            "type": "integer"
        },
        "productName": {
            "description": "Name of the product",
            "type": "string"
        },
        "price": {
            "description": "The price of the product",
            "type": "number",
            "exclusiveMinimum": 0
        },
        "tags": {
            "description": "Tags for the product",
            "type": "array",
            "items": {
                "type": "string"
            },
            "minItems": 1,
            "uniqueItems": true
        },
        "dimensions": {
            "type": "object",
            "properties": {
                "length": {
                    "type": "number"
                },
                "width": {
                    "type": "number"
                },
                "height": {
                    "type": "number"
                }
            },
            "required": ["length", "width", "height"]
        },
        "warehouseLocation": {
            "required": ["latitude", "longitude"],
            "type": "object",
            "properties": {
                "latitude": {
                    "type": "number",
                    "minimum": -90,
                    "maximum": 90
                },
                "longitude": {
                    "type": "number",
                    "minimum": -180,
                    "maximum": 180
                }
            }
        }
    },
    "required": ["productId", "productName", "price"]
} 

//tv4.validateMultiple方法可以返回当前schema中所有的不匹配情况

pm.test('Json Schema 校验通过', function () {
    var result = tv4.validateMultiple(pm.response.json(), schema);
    console.log(result.errors) 
    pm.expect(result.valid).to.be.true
});

C、PostMan执行Tests

1、TestA接口

2、TestA接口

Console中可以看到详细的失败原因。

主流语言都提供了对Json Schema的良好支持,可以直接进行集成测试。

最后感谢每一个认真阅读我文章的人,看着粉丝一路的上涨和关注,礼尚往来总是要有的,虽然不是什么很值钱的东西,如果你用得到的话可以直接拿走!

软件测试面试文档

我们学习必然是为了找到高薪的工作,下面这些面试题是来自阿里、腾讯、字节等一线互联网大厂最新的面试资料,并且有字节大佬给出了权威的解答,刷完这一套面试资料相信大家都能找到满意的工作。
 

在这里插入图片描述

GitHub 加速计划 / js / json
17
5
下载
适用于现代 C++ 的 JSON。
最近提交(Master分支:2 个月前 )
960b763e 5 个月前
8c391e04 8 个月前
Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐