蔚来汽车数字座舱,通过服务端TSP系统集成了很多供应商的系统,比如天气、音乐、视频等娱乐系统。数字座舱调用TSP封装的供应商接口实现对天气的查询,音乐和视频的播放。TSP与供应商之间要共同遵守一个契约,保证提供给数字座舱的数据结构的稳定。
TSP对供应商系统的Response透传给数字座舱,因此在进行TSP系统接口测试的过程中,一个重要的检查点,就是保证提供给数字座舱的数据结构的稳定性,例如返回字段要完整、字段数据类型要符合约定,避免因供应商的改动,返回不符合约定的Response给数字座舱导致数字座舱异常。从而保证当供应商接口的响应发生了变更,通过接口测试能够第一时间发现,哪怕供应商没有及时通知到TSP。
因为数字座舱、TSP以及供应商之间,都是采取JSON作为数据交换的格式,因此,TSP接口测试中,保证Response字段的完整性、数据类型的合法性,就是检查JSON格式的完整性和合法性。
JSON Schema 是描述JSON数据结构的一种格式,通过JSON Schema可以描述JSON的字段以及字段数据类型等信息。因此,校验JSON Schema就可以校验Response是否符合约定。这种测试也常被叫做契约测试。
本文将介绍,如何通过JSON Schema对TSP接口的Response进行契约测试。
01 — 什么是JSON Schema
JSON Schema是一种用来描述JSON数据的一种JSON数据结构,它本身也是一个JSON格式。JSON Schema也有版本,最新版的版本是 Draft 7,此前还有Draft 6,Draft 4 和Draft 3。
一个JSON格式的数据,通常是由以下一种或多种数据类型组成的:
- string
- Numeric(integer、number)
- object
- array
- boolean
- null
JSON Schema中对上面的6种数据类型,都有相应的属性对其进行描述。下面是一个JSON数据,它是由object类型的data,Numeric类型的cur_num,string类型的keyword,boolean类型的vip,array类型的list组成,list中的数据是object类型:
{
"data": {
"cur_num": 10,
"keyword": "朴树",
"vip": true,
"list": [
{
"album_id": 2032482,
"publish": "2018-11-13T20:20:39+00:00"
},
{
"album_id": 7986,
"publish": "2018-11-13T20:20:39+00:00"
}
]
}
}
下面的JSON Schema可以描述上面JSON格式数据:
{
"$schema": "http://json-schema.org/draft-04/schema#",
"type": "object",
"properties": {
"data": {
"type": "object",
"properties": {
"cur_num": {
"type": "integer",
"minimum": 1,
"maximum": 10,
"exclusiveMaximum": false
},
"keyword": {
"type": "string",
"minLength": 1,
"maxLength": 10
},
"vip": {
"type": "boolean"
},
"list": {
"type": "array",
"minItems": 1,
"maxItems": 10,
"items": {
"type": "object",
"properties": {
"album_id": {
"type": "integer"
},
"publish": {
"type": "string",
"format":"date-time"
}
},
"required": [
"album_id",
"publish"
]
}
}
},
"required": [
"cur_num",
"keyword",
"vip",
"list"
]
}
},
"required": [
"data"
]
}
JSON Schema还是很容易看懂的,现在我们简单来介绍一下:
- "$schema"表示这个JSON Schema是使用的draft-04版本的规范;
- data的type是object类型;
- data包含的properties有cur_num、keyword、vip和list,这四个属性都是required,也就是必须包含的;
- cur_num的type是个integer(整型),它的取值范围最小值是1(“minimum”: 1,),最大值是10(“maximum”: 10,),包含10(“exclusiveMaximum”: false);
- keyword的类型是string(字符串),最小长度是1(“minLength”: 1),最大长度是10(“maxLength”: 10);
- vip的类型是布尔型;
- list的类型是数组array,至少包含1个元素(“minItems”: 1),最多包含10个元素(“maxItems”: 10)每一个元素都是object类型,包含album_id和publish两个必须属性
- publish是个string类型,格式是个日期时间格式(“format”:“date-time”)。
比如对于string类型的数据,JSON Schema可以通过format对常见的字符串进行描述,比如描述时间和日期的"date-time",描述电子邮件的"email",描述主机的"hostname",描述IP地址的"ipv4"和"ipv6",描述URI的"uri"。如果不属于常见的字符串格式,还可以通过pattern正则表达式来描述,比如"pattern": "^(\([0-9]{3}\))?[0-9]{3}-[0-9]{4}$“可以用来描述”(888)555-1212"这样的字符串。
通过上面的讲解,我想大家应该可以用JSON Schema来描述一个JSON字符串了吧。更全面的JSON Schema的语法可以参考http://json-schema.org/understanding-json-schema/。
上面的JSON Schema能否描述前面的JSON字符串呢?可以通过在线的一个Validator进行验证一下,我们将上面的JSON Schema和JSON复制粘贴到这个网站https://www.jsonschemavalidator.net/上,如果网页下方出现No errors found. JSON validates against the schema,则表示JSON字符串符合JSON Schema的描述。
02 — 自动化测试中如何断言
在自动化测试中,如何通过代码方式对JSON格式的Response进行JSON Schema断言呢?因为我平时都是用Python进行自动化测试的,今天就来介绍一下python语言中JSON Schema断言的方式。
需要安装一个jsonschema软件包:
pip install jsonschema
假设respose_data是通过某一个接口的response,我们对其进行JSON Schema校验,使用的JSON Schema是schema,自动化测试代码可以写成如下所示:
# content of test_json_schema.py
from jsonschema import validators
def test_json_schema_validator():
schema = {
"$schema": "http://json-schema.org/draft-04/schema#",
"type": "object",
"properties": {
"data": {
"type": "object",
"properties": {
"cur_num": {
"type": "integer",
"minimum": 1,
"maximum": 10,
"exclusiveMaximum": False
},
"keyword": {
"type": "string",
"minLength": 1,
"maxLength": 1
},
"vip": {
"type": "boolean"
},
"list": {
"type": "array",
"minItems": 1,
"maxItems": 10,
"items": {
"type": "object",
"properties": {
"album_id": {
"type": "integer"
},
"publish": {
"type": "string",
"format": "date-time"
}
},
"required": [
"album_id",
"publish"
]
}
}
},
"required": [
"cur_num",
"keyword",
"vip",
"list"
]
}
},
"required": [
"data"
]
}
respose_data = {
"data": {
"cur_num": 10,
"keyword": "朴树",
"vip": True,
"list": [
{
"album_id": 2032482,
"publish": "2018-11-13T20:20:39+00:00"
},
{
"album_id": 7986,
"publish": "2018-11-13T20:20:39+00:00"
}
]
}
}
va = validators.Draft4Validator(schema)
va.validate(respose_data)
注意,在Python语言中boolen值是False和True,首字母大写。
执行上面的代码将看到如下输出:
tests/test_json_schema.py::test_json_schema_validator PASSED [100%]
如果我们将respose_data修改一下,来模拟接口的response发生了变化,比如"cur_num": 10,改成"cur_num": 20,再次执行上面的代码,则会看到测试不通过,并输出如下信息:
tests/test_json_schema.py:78:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
self = <jsonschema.validators.create.<locals>.Validator object at 0x106307710>
args = ({'data': {'cur_num': 20, 'keyword': '朴树', 'list': [{'album_id': 2032482, 'publish': '2018-11-13T20:20:39+00:00'}, {'album_id': 7986, 'publish': '2018-11-13T20:20:39+00:00'}], 'vip': True}},)
kwargs = {}, error = <ValidationError: '20 is greater than the maximum of 10'>
def validate(self, *args, **kwargs):
for error in self.iter_errors(*args, **kwargs):
> raise error
E jsonschema.exceptions.ValidationError: 20 is greater than the maximum of 10
E
E Failed validating 'maximum' in schema['properties']['data']['properties']['cur_num']:
E {'exclusiveMaximum': False,
E 'maximum': 10,
E 'minimum': 1,
E 'type': 'integer'}
E
E On instance['data']['cur_num']:
E 20
从输出的错误信息可以看出,instance[‘data’][‘cur_num’]的数值是20,不满JSON Schema中对其描述的最大值不能超过10的规范。因此断言失败。
03 — 总结
文本先介绍了JSON Schema如何描述JSON格式的数据,接着对Python语言中,如何使用jsonschema对接口的Response进行JSON Schema断言。在日常测试工作中,如果被测服务是对上游或者第三方供应商的Response进行透传,那么在测试这个被测服务时,一般就是对Response进行JSON Schema断言,来保证Response的数据结构稳定和合乎契约。
参考资料
https://juejin.im/entry/5c66975de51d4574b5550f1f
https://www.jianshu.com/p/1711f2f24dcf
https://www.jianshu.com/p/8d4ae03d92fb
https://json-schema.org/understanding-json-schema/index.html#
https://www.liquid-technologies.com/online-json-to-schema-converter
https://www.jsonschemavalidator.net/