目录
前言:
在接口自动化测试中,全量字段校验是一个非常重要的步骤。全量字段校验可以帮助我们检查接口返回的数据是否符合预期,确保接口的正确性和稳定性。
一.背景
公司前端吐槽后台接口有时会更改返回的数据结构,返回的字段名与字段类型与接口文档不一致,希望有一个快速检测接口返回数据的所有字段名与字段类型的方法
以下方数据为例,要校验 data 数组中 dict 结构中的字段名与字段类型,可以写脚本遍历数据,但是由于每个接口返回的数据结构可能不一致,可能需要针对每个接口做不同的逻辑,所以需要一个比较通用的校验方法
{
"msg": "success",
"code": 0,
"data": [{
"type_id": 249,
"name": "王者荣耀",
"order_index": 1,
"status": 1,
"subtitle": " ",
"game_name": "王者荣耀"
}, {
"type_id": 250,
"name": "绝地求生",
"order_index": 2,
"status": 1,
"subtitle": " ",
"game_name": "绝地求生"
}, {
"type_id": 251,
"name": "刺激战场",
"order_index": 3,
"status": 1,
"subtitle": " ",
"game_name": "刺激战场"
}
]
}
在研究了契约测试后,抽取pact-python部分代码,实现:自定义接口返回数据格式 (【契约定义】)-实际响应数据格式校验 (【契约校验】) 的功能
备注:这里的【契约】等同于接口响应数据结构
二.校验原则
1.实际返回字段名要严格等于或者含契约定义字段名(根据不同匹配模式来确定)
2.字段值可以值相等或类型相等
目标:对返回数据进行全量 (字段名 - 值/类型) 校验
契约定义方式:支持 python 类契约和 json 契约
三.快速使用
安装:
pip install pactverify
1.python 类契约使用
from pactverify.matchers import Matcher, Like, EachLike, Enum, Term, PactVerify
# 定义契约格式
expect_format = Matcher({
'code': 0, # code key存在,值相等,code==0
'msg': 'success', # msg key存在,值相等,msg=='success'
# [{}]结构
'data': EachLike({
"type_id": 249, # type_id key存在,值类型相等,type(type_id) == type(249)
"name": "王者荣耀", # name key存在,值类型相等,type(name) == type("王者荣耀")
}),
'type': Enum([11,22]),
'list': EachLike(11,minimum=2)
})
# 实际返回数据
actual_data = {
"msg": "success",
"code": 1,
'type': 12,
"data": [{
# type_id类型不匹配
"type_id": '249',
"name": "王者荣耀"
}, {
# 缺少name
"type_id": 250,
}, {
# 比契约定义多index字段
"type_id": 251,
"name": "刺激战场",
"index": 111
}
],
'list': [11]
}
# hard_mode默认为true,hard_mode = True时,实际返回key必须严格等于预期key;hard_mode = False时,实际返回key包含预期key即可
mPactVerify = PactVerify(expect_format, hard_mode=True)
# 校验实际返回数据
mPactVerify.verify(actual_data)
# 校验结果 False
print(mPactVerify.verify_result)
''' 校验错误信息
错误信息输出actual_key路径:root.data.0.name形式
root为根目录,dict类型拼接key,list类型拼接数组下标(从0开始)
{
# 实际key少于预期key错误
'key_less_than_expect_error': ['root.data.1.name'],
# 实际key多与预期key错误,只在hard_mode = True时才报该错误
'key_more_than_expect_error': ['root.data.2.index'],
# 值不匹配错误
'value_not_match_error': [{
'actual_key': 'root.code',
'actual_value': 1,
'expect_value': 0
}
],
# 类型不匹配错误
'type_not_match_error': [{
'actual_key': 'root.data.0.type_id',
'actual_vaule': '249',
'expect_type': 'int'
}
],
# 数组长度不匹配错误
'list_len_not_match_error': [{
'actual_key': 'root.list',
'actual_len': 1,
'min_len': 2
}
],
# 枚举不匹配错误
'enum_not_match_error': [{
'actual_key': 'root.type',
'actual_value': 12,
'expect_enum': [11, 22]
}
]
}
'''
print(mPactVerify.verify_info)
2.json 契约使用
from pactverify.matchers import PactJsonVerify
# 定义json契约格式
expect_format = {
'$Matcher': {
'code': 0, # code key存在,值相等,code==0
'msg': 'success', # msg key存在,值相等,msg=='success'
# [{}]结构
'data': {
'$EachLike': {
"type_id": 249, # type_id key存在,值类型相等,type(type_id) == type(249)
"name": "王者荣耀", # name key存在,值类型相等,type(name) == type("王者荣耀")
}},
'type': {
'$Enum': [11, 22]
},
'list': {
'$EachLike': {
# $values,$params形式传递额外参数
'$values': 11,
'$params': {
'minimum': 2
}
}
}
}
}
# 实际返回数据
actual_data = {
"msg": "success",
"code": 1,
'type': 12,
"data": [{
# type_id类型不匹配
"type_id": '249',
"name": "王者荣耀"
}, {
# 缺少name
"type_id": 250,
}, {
# 比契约定义多index字段
"type_id": 251,
"name": "刺激战场",
"index": 111
}
],
'list': [11]
}
# hard_mode默认为true,hard_mode = True时,实际返回key必须严格等于预期key;hard_mode = False时,实际返回key包含预期key即可
# separator可自定义指定json关键字标识符,默认为$
mPactJsonVerify = PactJsonVerify(expect_format, hard_mode=True, separator='$')
# 校验实际返回数据
mPactJsonVerify.verify(actual_data)
# 校验结果 False
print(mPactJsonVerify.verify_result)
''' 校验错误信息
错误信息输出actual_key路径:root.data.0.name形式
root为根目录,dict类型拼接key,list类型拼接数组下标(从0开始)
{
# 实际key少于预期key错误
'key_less_than_expect_error': ['root.data.1.name'],
# 实际key多与预期key错误,只在hard_mode = True时才报该错误
'key_more_than_expect_error': ['root.data.2.index'],
# 值不匹配错误
'value_not_match_error': [{
'actual_key': 'root.code',
'actual_value': 1,
'expect_value': 0
}
],
# 类型不匹配错误
'type_not_match_error': [{
'actual_key': 'root.data.0.type_id',
'actual_vaule': '249',
'expect_type': 'int'
}
],
# 数组长度不匹配错误
'list_len_not_match_error': [{
'actual_key': 'root.list',
'actual_len': 1,
'min_len': 2
}
],
# 元祖不匹配错误
'enum_not_match_error': [{
'actual_key': 'root.type',
'actual_value': 12,
'expect_enum': [11, 22]
}
]
}
'''
print(mPactJsonVerify.verify_info)
3.python 类契约转 json 契约
1、python类契约不带参数
# python类契约
expect_format = Like({'k1': 'v1'})
# json契约
expect_format_json = {
'$Like': {'k1': 'v1'}
}
2