什么是 JSON Schema对妄?
- JSON Schema is a vocabulary that allows you to annotate and validate JSON documents.
- http://json-schema.org/
- 示例沟优,簡(jiǎn)而言之赛不,json schema就是json數(shù)據(jù)格式的一個(gè)描述,可以用來(lái)作實(shí)例驗(yàn)證浪耘。
// JSON Example
{
"message": "操作成功",
"responseCode": "0",
"hasError": false,
"data": {
"id": 100123456
}
}
// 與之對(duì)應(yīng)的JSON Schema
{
"$schema": "http://json-schema.org/schema#",
"type": "object",
"properties": {
"message": {
"type": "string"
},
"responseCode": {
"type": "string"
},
"hasError": {
"type": "boolean"
},
"data": {
"type": "object",
"properties": {
"id": {
"type": "integer"
}
},
"required": ["id"]
}
},
"required": ["data", "hasError", "message", "responseCode"]
}
接口自動(dòng)化測(cè)試?yán)?JSON Schema 斷言 Response乱灵?
接口自動(dòng)化測(cè)試一些常用斷言方法:
- status_code:response.status_code == 200
- headers.xxx:Content-Type == application/json
- content.xxx:content.responseCode == "0"、content.message == "操作成功"
今天來(lái)個(gè)不一樣的七冲,Json schema Validate:
#!/usr/bin/env python
# -*- encoding: utf-8 -*-
from jsonschema import validate
#! 正常的返回值
resp_pass = {
"message": "操作成功",
"responseCode": "0",
"hasError": False,
"data": {
"id": 100120384
}
}
#! 錯(cuò)誤的返回值痛倚,沒(méi)有返回Data ID
resp_fail = {
"message": "操作成功",
"responseCode": "0",
"hasError": False,
"data": {}
}
#! 預(yù)期Schema
resp_schema = {
"$schema": "http://json-schema.org/schema#",
"type": "object",
"properties": {
"message": {
"type": "string"
},
"responseCode": {
"type": "string"
},
"hasError": {
"type": "boolean"
},
"data": {
"type": "object",
"properties": {
"id": {
"type": "integer"
}
},
"required": ["id"]
}
},
"required": ["data", "hasError", "message", "responseCode"]
}
#! 驗(yàn)證成功
try:
validate(resp_pass, resp_schema)
except Exception as e:
print('An exception occurred: ', e)
#! 驗(yàn)證失敗
try:
validate(resp_fail, resp_schema)
except Exception as e:
print('An exception occurred: ', e)
以上執(zhí)行結(jié)果為:
# 看得懂哈,不解釋了 :)
An exception occurred: 'id' is a required property
Failed validating 'required' in schema['properties']['data']:
{'properties': {'id': {'type': 'integer'}},
'required': ['id'],
'type': 'object'}
On instance['data']:
{}
json schema validate fail
問(wèn):這么好用澜躺,怎么寫(xiě)預(yù)期的 json schema呢蝉稳?
https://www.liquid-technologies.com/online-json-to-schema-converter
可以”硬寫(xiě)“也可以在線轉(zhuǎn)換,輸入預(yù)期的返回值json苗踪,轉(zhuǎn)換成對(duì)應(yīng)的 json schema
進(jìn)階:初次自動(dòng)生成 JSON Schema用于后續(xù)斷言颠区?
以上,我們?cè)趯?xiě)接口Case的時(shí)候還需要打開(kāi)額外的網(wǎng)站去轉(zhuǎn)換 json to json schema通铲,有沒(méi)有更簡(jiǎn)單的方式?
#!/usr/bin/env python
# -*- encoding: utf-8 -*-
from genson import SchemaBuilder
#! 正確的返回值
resp_pass = {
"message": "操作成功",
"responseCode": "0",
"hasError": False,
"data": {
"id": 100120384
}
}
#! 通過(guò)正確的返回值生成預(yù)期Json schema
builder = SchemaBuilder()
builder.add_object(resp_pass)
resp_schema = builder.to_json()
print(resp_schema)
""" 輸出結(jié)果
{
"$schema": "http://json-schema.org/schema#",
"type": "object",
"properties": {
"message": {
"type": "string"
},
"responseCode": {
"type": "string"
},
"hasError": {
"type": "boolean"
},
"data": {
"type": "object",
"properties": {
"id": {
"type": "integer"
}
},
"required": ["id"]
}
},
"required": ["data", "hasError", "message", "responseCode"]
}
"""
我們可以把預(yù)期的Json Schema 存在本地(比如CSV或者數(shù)據(jù)庫(kù)中)器贩,validate時(shí)先查詢有沒(méi)有expect schema颅夺,有就取出來(lái)與response做validation朋截,沒(méi)有就自動(dòng)用當(dāng)前的response生成并保存expect schema(so調(diào)試時(shí)要確保用正確的返回值生成),后續(xù)都拿這個(gè)expect schema來(lái)validate吧黄。把這個(gè)過(guò)程寫(xiě)成一個(gè)方法一直復(fù)用部服,是不是又省時(shí)又省力?
from jsonschema import validate
from genson import SchemaBuilder
# 偽代碼
def validate_jsonschema(resp_json, expect_flag):
"""
resp_json:response 返回值
expect_flag:去數(shù)據(jù)庫(kù)查詢對(duì)應(yīng)期望schemat的一個(gè)flag拗慨,通常用接口名
創(chuàng)建schema邏輯
1. 數(shù)據(jù)庫(kù)查詢是否有對(duì)應(yīng)flag(對(duì)應(yīng)接口)的期望schema數(shù)據(jù)廓八;
2. if 有: 直接拿來(lái)跟response的json數(shù)據(jù)做validate
3. if 無(wú): 插入當(dāng)前這條的響應(yīng)json數(shù)據(jù)對(duì)應(yīng)的schema進(jìn)數(shù)據(jù)庫(kù),assert True赵抢! 所以用例寫(xiě)好調(diào)試正常之后剧蹂,第一次運(yùn)行確保response正確 然后生成對(duì)應(yīng)的schema并保存
"""
sql_expect = f"SELECT expect_schema from resp_schema WHERE flag ='{expect_flag}'"
with MySqlDB() as db:
expect_schema = db.fetch_one(sql_expect)
if isinstance(resp_json, dict):
# ? 判斷response content為dict(json)格式,否則直接False
if expect_schema:
# ? 判斷是否存在預(yù)期schema
try:
expect_schema = json.loads(expect_schema.get('expect_schema'))
validate(resp_json, expect_schema)
except Exception as e:
logger.log_error(e.text)
assert False
# ? ValidationFailure烦却,F(xiàn)ail the case
else:
builder = SchemaBuilder()
builder.add_object(resp_json)
expect_schema = builder.to_json(indent=2, ensure_ascii=False)
resp_schema = json.dumps(resp_json, indent=2, ensure_ascii=False)
# ? 將respons json轉(zhuǎn)換成expect schema宠叼,記錄下來(lái)
sql_insert = "INSERT INTO resp_schema VALUES (%s, %s, %s)"
with MySqlDB() as db:
db.create_one(sql_insert,
(expect_flag, expect_schema, resp_schema))
assert True
else:
assert False
# ! resp 非dict格式Fail