目錄
一、背景
二、基礎比對規則
1、值校驗
(1)完全比對校驗-Matcher
(2)正則比對校驗-Term
(3)枚舉比對校驗-Enum
2、類型校驗-Like
3、數組類型校驗-EachLike
三、複雜資料結果比對規則
1、{{}}格式-字典套疊
2、[{}]格式-數組字典套疊
3、{[]}
4、Like-Term 套疊
5、Like-Matcher套疊
四、異常場景比對
1、null比對nullable
2、{}比對dict_emptiable
3、json格式串比對jsonloads
4、key不存在比對key_missable
5、多類型比對extra_types
四、自動生成 json 契約
一、背景
接口傳回的字段需要對傳回結構校驗、每個字段值類型、每個字段值校驗
傳回結果如果是json嵌套的情況且數組包含這樣結果有伸縮性的,校驗代碼會很長,考慮傳回的異常場景也多
例如:
data裡面的結構體校驗,似乎for循環也能解決,但是是不是需要”造輪子“,我想講的是一個已經有的輪子--pip install pactverify
{
"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": "刺激戰場"
}
]
}
二、基礎比對規則
1、值校驗
(1)完全比對校驗-Matcher
學習點:from pactverify.matchers import Matcher, PactVerify, PactJsonVerify
Matcher聲明的結構體需要完全比對
python類契約---PactVerify類初始化
json類契約---PactJsonVerify類初始化
verify比對後verify_result是比對結果,verify_info是具體的錯誤資訊
hard_mode=True, 預設為true,hard_mode = True時,實際傳回key必須嚴格等于預期key;hard_mode = False時,實際傳回key包含預期key即可
separator='$' 可自定義指定json關鍵字辨別符,預設為$
注意:一旦比對失敗後,verify正确結構體也會是失敗(應該是verify_info資訊寫入後重新verify沒有重置造成的)
##############################################################################
# 校驗值=11
# 1、python類契約格式
expect_format_1 = Matcher(11)
# 2、json契約格式
expect_format_json_1 = {
"$Matcher": 11
}
# 校驗值=1.0
# 1、python類契約格式
expect_format_2 = Matcher(1.0)
# 2、json契約格式
expect_format_json_2 = {
'$Matcher': 1.0
}
# 校驗值='1'
# 1、python類契約格式
expect_format_3 = Matcher('11')
# 2、json契約格式
expect_format_json_3 = {
'$Matcher': '11'
}
################################################################################
# 校驗值={'k1': 'v1'}
# 1、python類契約格式
expect_format_4 = Matcher({'k1': 'v1'})
# 2、json契約格式
expect_format_json_4 = {
'$Matcher': {'k1': 'v1'}
}
# 使用執行個體---pactVerify初始化之後隻能使用一次
>>> from pactverify.matchers import Matcher, PactVerify, PactJsonVerify
>>> p1_1 = PactVerify(expect_format_1,hard_mode=True)
>>> p1_1.verify(11)
>>> p1_1.verify_result
True
>>> p1_1.verify(12)
>>> p1_1.verify_result # 這裡12是不比對的,傳回False
False
>>> p1_1.verify(11)
>>> p1_1.verify_result #接着使用p1_1,就不能正确比對了,下面那樣初始化下
False
>>> p1_1 = PactVerify(expect_format_1,hard_mode=True)
>>> p1_1.verify(11)
>>> p1_1.verify_result
True
>>> p1_2 = PactJsonVerify(expect_format_json_1,hard_mode=True, separator='$')
>>> p1_2.verify(11)
>>> p1_2.verify_result
True
# separate自定義
>>> expect_format_json_2 = {
... '&Matcher': 1.0
... }
>>> pp= PactJsonVerify(expect_format_json_2,separator='&')
>>> pp.verify(1.0)
>>> pp.verify_result
True
# hard_mode=False,實際多出來的k2沒有校驗
>>> p4_1 = PactVerify(expect_format_4,hard_mode=False)
>>> p4_1.verify({"k1":"v1","k2":"v2"})
>>> p4_1.verify_result
True
>>> p4_1 = PactVerify(expect_format_4,hard_mode=True)
>>> p4_1.verify({"k1":"v1"})
>>> p4_1.verify_result
True
>>> p4_1.verify({"k1":"v2"})
>>> p4_1.verify_result
False
>>> p4_1.verify_info
{'value_not_match_error': [{'actual_key': 'root.k1', 'actual_value': 'v2', 'expect_value': 'v1'}]}
(2)正則比對校驗-Term
example 、type_strict參數使用
from pactverify.matchers import Term
# 校驗值=r'^\d{2}$'example為了校驗類型,如下需要name的字段類型是int
# 1、python類契約
expect_format_1 = Term(r'^\d{2}$', example=11)
# 2、json契約--
expect_format_json_1 ={
'$Like': {
"name": {
'$Term': {
'$values': r'^\d{2}$',
'$params': {
'example': 11
}
}
}
}
}
# 預期r'^\d{2}$',type_strict = False時跳過對example參數類型校驗
# 1、python類契約
expect_format_2 = Term(r'^\d{2}$', example="11", type_strict=False)
# 2、json契約
expect_format_json_2 = {
'$Like': {
"name": {
'$Term': {
'$values': r'^\d{2}$',
'$params': {
'example': 11,
'type_strict': False
}
}
}
}
}
>>> pp=PactJsonVerify(expect_format_json_1, separator="$", hard_mode=True)
>>> pp.verify({"name":11})
>>> pp.verify_result
True
>>> pp.verify({"name":13})
>>> pp.verify_result
True
>>> pp.verify({"name":"22"})
>>> pp.verify_result
False
>>> pp.verify_info
{'type_not_match_error': [{'actual_key': 'root.name', 'actual_vaule': '22', 'expect_type': 'int'}]}
>>>
>>> pp1=PactJsonVerify(expect_format_json_2, separator="$", hard_mode=True)
>>> pp1.verify({"name":"22"})
>>> pp.verify_info
{}
>>> pp.verify_result
True
(3)枚舉比對校驗-Enum
from pactverify.matchers import Enum
# 預期11或22
#1、python類契約
expected_format_1 = Enum([11, 22])
# 2、json契約
expected_format_json_1 = {
'$Enum': [11, 22]
}
# iterate_list為true時,當目标資料為數組時,會周遊數組中每個元素是否in [11, 22]
#1、python類契約
expected_format_2 = Enum([11, 22], iterate_list=True)
# 2、json契約
expected_format_json_2 = {
'$Enum': {
'$values': [11, 22],
'$params': {'iterate_list': True}
}
}
>>> pp = PactVerify(expected_format_1,hard_mode=True)
>>> pp.verify(11)
>>> pp.verify_result
True
>>> pp.verify(22)
>>> pp.verify_result
True
>>> pp.verify(33)
>>> pp.verify_result
False
>>> pp.verify_info
{'enum_not_match_error': [{'actual_key': 'root', 'actual_value': 33, 'expect_enum': [11, 22]}]}
>>> pp1 = PactJsonVerify(expected_format_json_1,separator="$")
>>> pp1.verify(11)
>>> pp1.verify_result
True
>>> pp1.verify(33)
>>> pp1.verify_result
False
>>> pp1.verify_info
{'enum_not_match_error': [{'actual_key': 'root', 'actual_value': 33, 'expect_enum': [11, 22]}]}
>>> pp2 = PactJsonVerify(expected_format_json_2,separator="$")
>>> pp2.verify([11,22,11])
>>> pp2.verify_result
True
>>> pp2.verify([11,22,33])
>>> pp2.verify_result
False
>>> pp2.verify_info
{'enum_not_match_error': [{'actual_key': 'root.2', 'actual_value': 33, 'expect_enum': [11, 22]}]}
2、類型校驗-Like
# 預期type(11)
#1、python類契約
expect_format_1 = Like(11)
# 2、json契約
expect_format_json_1 = {
'$Like': 11
}
# 預期type(1.0)
#1、python類契約
expect_format_2 = Like(1.0)
# 2、json契約
expect_format_json_2 = {
'$Like': 1.0
}
# 預期type('11')
#1、python類契約
expect_format_3 = Like('11')
# 2、json契約
expect_format_json_3 = {
'$Like': '11'
}
# 預期傳回資料actual為dict結構,actual['k1'] == type('v1')
#1、python類契約
expect_format_4 = Like({'k1':'v1'})
#2、json契約
expect_format_json_4 = {
'$Like': {'k1':'v1'}
}
>>> pp1=PactVerify(expect_format_1)
>>> pp1.verify(22)
>>> pp1.verify_result
True
>>> pp1.verify("11")
>>> pp1.verify_result
False
>>> pp1.verify_info
{'type_not_match_error': [{'actual_key': 'root', 'actual_vaule': '11', 'expect_type': 'int'}]}
>>>
>>> pp2 = PactJsonVerify(expect_format_json_1,separator="$")
>>> pp2.verify(11)
>>> pp2.verify_result
True
>>> pp2.verify("11")
>>> pp2.verify_result
False
>>> pp2.verify_info
{'type_not_match_error': [{'actual_key': 'root', 'actual_vaule': '11', 'expect_type': 'int'}]}
>>> pp3 = PactVerify(expect_format_4,hard_mode=True)
>>> pp3.verify({"k1":"v1"})
>>> pp3.verify_result
True
>>> pp3.verify({"k1":11})
>>> pp3.verify_result
False
>>> pp3.verify_info
{'type_not_match_error': [{'actual_key': 'root.k1', 'actual_vaule': 11, 'expect_type': 'str'}]}
>>> pp4 = PactJsonVerify(expect_format_json_4,hard_mode=True)
>>> pp4.verify({"k1":"dsf"})
>>> pp4.verify_result
True
>>> pp4.verify({"k1":None})
>>> pp4.verify_result
False
>>> pp4.verify_info
{'type_not_match_error': [{'actual_key': 'root.k1', 'actual_vaule': None, 'expect_type': 'str'}]}
>>>
3、數組類型校驗-EachLike
# 預期[type(11)]
#1、python類契約
expect_format_1 = EachLike(11)
#2、json契約
expect_format_json_1 = {
'$EachLike': 11
}
# 預期[type(1.0)]
#1、python類契約
expect_format_2 = EachLike(1.0)
#2、json契約
expect_format_json_2 = {
'$EachLike': 1.0
}
# 預期[type('11')]
#1、python類契約
expect_format_3 = EachLike('11')
#2、json契約
expect_format_json_3 = {
'$EachLike': '11'
}
# 預期[Like{'k1':'v1'}]
#1、python類契約
expect_format_4 = EachLike({'k1': 'v1'})
#2、json契約
expect_format_json_4 = {
'$EachLike': {'k1': 'v1'}
}
# 預期[Like{'k1':'v1'}]或[],minimum為數組最小長度,預設minimum=1
#1、python類契約
expect_format_5 = EachLike({'k1': 'v1'}, minimum=0)
#2、json契約
expect_format_json_5 = {
'$EachLike': {
# $values,$params結構用于額外傳參
'$values': {'k1': 'v1'},
'$params': {'minimum': 0}
}
}
>>> pp=PactVerify(expect_format_1,hard_mode=True)
>>> pp.verify([11,22,44])
>>> pp.verify_result
True
>>> pp.verify([11,22,44,None])
>>> pp.verify_result
False
>>> pp.verify_info
{'type_not_match_error': [{'actual_key': 'root.3', 'actual_vaule': None, 'expect_type': 'int'}]}
>>> pp2 = PactJsonVerify(expect_format_json_5,separator="$")
>>> pp2.verify([])
>>> pp2.verify_result
True
>>> pp2.verify([{"k1":"v1"}])
>>> pp2.verify_result
True
>>> pp2.verify([{"k1","v1"}])
>>> pp2.verify_result
False
>>> pp2.verify_info
{'type_not_match_error': [{'actual_key': 'root.0', 'actual_vaule': {'v1', 'k1'}, 'expect_type': 'dict'}]}
三、複雜資料結果比對規則
- Matcher,Like 和 EachLike 類可以不限層級嵌套,Term 和 Enum 則不能嵌套其他規則
- 比對規則多層嵌套時,内層規則優先生效
1、{{}}格式-字典套疊
actual_data = {
'code': 0,
'msg': 'success',
'data': {
"id": 1,
"name": 'lili'
}
}
# python類契約
expect_format = Like({
'code': 0,
'msg': 'success',
'data': Like({
"id": 1,
"name": 'lili'
})
})
# json契約
expect_format_json = {
'$Like': {
'code': 0,
'msg': 'success',
'data': {
'$Like': {
"id": 1,
"name": 'lili'
}
}
}
}
2、[{}]格式-數組字典套疊
actual_data = [[{
"id": 1,
"name": 'lili'
}]]
# python類契約
expect_format = EachLike(EachLike({
"id": 1,
"name": 'lili'
}))
# json契約
expect_format_json = {
'$EachLike': {
'$EachLike': {
"id": 1,
"name": 'lili'
}
}
}
3、{[]}
actual_data = {
'code': 0,
'msg': 'success',
'data': [{
"id": 1,
"name": 'lili'
},{
"id": 2,
"name": 'lilei'
}]
}
# python類契約
expect_format = Like({
'code': 0,
'msg': 'success',
'data': EachLike({
"id": 1,
"name": 'lili'
})
})
# json契約
expect_format_json = {
'$Like': {
'code': 0,
'msg': 'success',
'data': {
'$EachLike': {
"id": 1,
"name": 'lili'
}
}
}
}
4、Like-Term 套疊
actual_data = {
'code': 0,
'msg': 'success',
'data': {
"id": 1,
"name": 'lili'
}
}
# python類契約
expect_format = Like({
'code': 0,
'msg': 'success',
'data': Like({
"id": 1,
"name": Term(r'\w*', example='lili')
})
})
# json契約
expect_format_json = {
'$Like': {
'code': 0,
'msg': 'success',
'data': {
'$Like': {
"id": 1,
"name": {
'$Term': {
'$values': r'\w*',
'$params': {
'example': 'lili'
}
}
}
}
}
}
}
5、Like-Matcher套疊
actual_data = {
'name': 'lilei',
'age': 12
}
# python類契約
expect_format = Like({
# name字段值類型比對
'name': 'lilei',
# age字段值比對
'age': Matcher(12),
})
# json契約
expect_format_json = {
'$Like': {
# name字段值類型比對
'name': 'lilei',
# age字段值比對
'age': {
'$Matcher': 12
},
}
}
四、異常場景比對
1、null比對nullable
nullable 參數在 hard_mode = True 時也生效
# nullable為true時允許傳回null,預期null和(actual為dict結構,actual['k1'] == 'v1' or null)#1、python類契約
expect_format = Matcher({'k1': 'v1'}, nullable=True)
# 2、json契約
expect_format_json = {
'$Matcher': {
'$values': {'k1': 'v1'},
'$params': {'nullable': True}
}
}
# nullable為true時允許傳回null,預期null和(actual為dict結構,actual['k1'] == type('v1') or null)形式
#1、python類契約
expect_format = Like({'k1': 'v1'}, nullable=True)
#2、json契約
expect_format_json = {
'$Like': {
'$values': {'k1': 'v1'},
'$params': {'nullable': True}
}
}
# nullable為true時允許傳回null,預期null和[null,{'k1':null}]形式
#1、python類契約
expect_format = EachLike({'k1': 'v1'}, nullable=True)
#2、json契約
expect_format_json = {
'$EachLike': {
'$values': {'k1': 'v1'},
'$params': {'nullable': True}
}
}
# nullable為true時允許傳回null,預期null和11形式
#1、python類契約
expect_format = Term(r'^\d{2}$', example=11, nullable=True)
#2、json契約
expect_format_json = {
'$Term': {
'$values': r'^\d{2}$',
'$params': {'example': 11, 'nullable': True}
}
}
# nullable為true時允許傳回null,預期null和11/22/33形式
#1、python類契約
expect_format = Enum([11, 22, 33], nullable=True)
#2、json契約
expect_format_json = {
'$Enum': {
'$values': [11, 22, 33],
'$params': {'nullable': True}
}
}
2、{}比對dict_emptiable
dict_emptiable 在 hard_mode = True 時也生效
# dict_emptiable為true時,允許傳回{},預期{}和(actual為dict結構,actual['k1'] == 'v1')形式
#1、python類契約
expect_format = Matcher({'k1': 'v1'}, dict_emptiable=True)
#2、json契約
expect_format_json = {
'$Matcher': {
'$values': {'k1': 'v1'},
'$params': {'dict_emptiable': True}
}
}
# dict_emptiable為true時,允許傳回{},預期{}和(actual為dict結構,actual['k1'] == type('v1'))形式
#1、python類契約
expect_format = Like({'k1': 'v1'}, dict_emptiable=True)
# 2、json契約
expect_format_json = {
'$Like': {
'$values': {'k1': 'v1'},
'$params': {'dict_emptiable': True}
}
}
3、json格式串比對jsonloads
# actual為"{\"k1\":\"v1\"}"json字元串格式時,先進行json.loads再校驗
#1、python類契約
expect_format = Matcher({'k1': 'v1'}, jsonloads=True)
#2、json契約
expect_format_json = {
'$Matcher': {
'$values': {'k1': 'v1'},
'$params': {'jsonloads': True}
}
}
# actual為"{\"k1\":\"v1\"}"json字元串格式時,先進行json.loads再校驗
#1、python類契約
expect_format = Like({'k1': 'v1'}, jsonloads=True)
#2、json契約
expect_format_json = {
'$Like': {
'$values': {'k1': 'v1'},
'$params': {'jsonloads': True}
}
}
# actual為"[{\"k1\":\"v1\"}]"json字元串格式時,先進行json.loads再校驗
#1、python類契約
expect_format = EachLike({'k1': 'v1'}, jsonloads=True)
#2、json契約
expect_format_json = {
'$EachLike': {
'$values': {'k1': 'v1'},
'$params': {'jsonloads': True}
}
}
# actual為"[11,22]"json字元串格式時,先進行json.loads再校驗
#1、python類契約
expected_format = Enum([11, 22], jsonloads=True)
#2、json契約
expected_format_json = {
'$Enum': {
'$values': {'k1': 'v1'},
'$params': {'jsonloads': True}
}
}
4、key不存在比對key_missable
1. key_missable 在 hard_mode = True 時也生效
2. key_missable 針對 actual_data 本身的 key,dict_key_missable 針對 actual_data 字典中的 key,可以同時生效
# key_missable為true時,允許key不存在,key存在時走正常校驗;Matcher,Like,EachLike,Term和Enum類都可使用該屬性 python類契約
expect_format = Matcher({
'code': Like(0, key_missable=True),
'msg': Matcher('success', key_missable=True),
'data': EachLike(11, key_missable=True),
'age': Term(r'^\d{2}$', example=11, key_missable=True),
'num': Enum([11, 22, 33], key_missable=True)
})
# key_missable為true時,允許key不存在,key存在時走正常校驗;Matcher,Like,EachLike,Term和Enum類都可使用該屬性 json契約
expect_format_json = {
'$Matcher': {
'code': {
'$Like': {
'$values': 0,
'$params': {'key_missable': True}
}
},
'msg': {
'$Matcher': {
'$values': 'success',
'$params': {'key_missable': True}
}
},
'data': {
'$EachLike': {
'$values': 11,
'$params': {'key_missable': True}
}
},
'age': {
'$Term': {
'$values': r'^\d{2}$',
'$params': {'example': 11, 'key_missable': True}
}
},
'num': {
'$Enum': {
'$values': [11, 22, 33],
'$params': {'key_missable': True}
}
},
}}
# dict_key_missable為true時,允許dict結構中的key不存在,但key不能多(hard_mode=true時),key存在時正常校驗 python類契約
expected_format = Matcher({
'name': 'lilei',
'age': 12,
'sex': 'man'
}, dict_key_missable=True)
# dict_key_missable為true時,允許dict結構中的key不存在,但key不能多(hard_mode=true時),key存在時正常校驗 json契約
expected_format_json = {
'$Matcher': {
'$values': {
'name': 'lilei',
'age': 12,
'sex': 'man'
},
'$params': {'dict_key_missable': True}
}
}
# dict_key_missable為true時,允許dict結構中的key不存在,但key不能多(hard_mode=true時),key存在時正常校驗 python類契約
expected_format = Like({
'name': 'lilei',
'age': 12,
'sex': 'man'
}, dict_key_missable=True)
# dict_key_missable為true時,允許dict結構中的key不存在,但key不能多(hard_mode=true時),key存在時正常校驗 json契約
expected_format_json = {
'$Like': {
'$values': {
'name': 'lilei',
'age': 12,
'sex': 'man'
},
'$params': {'dict_key_missable': True}
}
}
# dict_key_missable為true時,允許dict結構中的key不存在,但key不能多(hard_mode=true時),key存在時正常校驗 python類契約
expected_format = EachLike({
'name': 'lilei',
'age': 12,
'sex': 'man'
}, dict_key_missable=True)
# dict_key_missable為true時,允許dict結構中的key不存在,但key不能多(hard_mode=true時),key存在時正常校驗 json契約
expected_format_json = {
'$EachLike': {
'$values': {
'name': 'lilei',
'age': 12,
'sex': 'man'
},
'$params': {'dict_key_missable': True}
}
}
5、多類型比對extra_types
# actual資料為type(11)或type('11'),extra_types可以添加多個示例資料,對基礎資料類型(int,float,boolean,str,None)示例有效,對list dict等類型無效 python類契約
expect_format = Like(11, extra_types=['11'])
# actual資料為type(11)或type('11'),extra_types可以添加多個示例資料,對基礎資料類型(int,float,boolean,str,None)示例有效,對list dict等類型無效 json契約
expect_format_json = {
'$Like': {
'$values': 11,
'$params': {'extra_types': ['11']}
}
}
# actual資料為[type(11)]或[type('11')],extra_types可以添加多個示例資料,對基礎資料類型示例(int,float,boolean,str,None)有效,對list dict等類型無效 python類契約
expect_format = EachLike(11, extra_types=['11'])
# actual資料為[type(11)]或[type('11')],extra_types可以添加多個示例資料,對基礎資料類型示例(int,float,boolean,str,None)有效,對list dict等類型無效 json契約
expect_format_json = {
'$EachLike': {
'$values': 11,
'$params': {'extra_types': ['11']}
}
}
四、自動生成 json 契約
from pactverify.utils import generate_pact_json_by_response
response_json = {
"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": "刺激戰場"
}
]
}
# 參數說明:響應json資料,契約關鍵字辨別符(預設$)
pact_json = generate_pact_json_by_response(response_json, separator='$')
print(pact_json)