起源
最近使用自建的测试平台时,发现 「批量创建 / 修改用例」 不够方便,故计划新增 「测试用例导入 / 导出」 功能。从易于编程的角度上考虑,决定使用 Excel 承载测试用例数据。
导出功能较为可控,但导入功能的输入数据完全由使用者主导。作为 QA,当然要对用户输入数据做到最全面、最深层次的校验。故笔者决定实现一个深度校验函数。
实现思路
计划该函数共接收 2 个参数:pre_validate_data、expected_structure。
其中 pre_validate_data 代表 预校验数据,expected_structure 则代表 期望的 结构。
函数返回则是一个布尔值,True 表示数据满足所期待的结构, False 则代表数据不满足期待的结构。
expected_structure 参数的结构经过思考后示例结构如下:
expected_structure = {'expectedTypeRange': [list],
'expectedValueRange':
[
{'expectedTypeRange':[dict], 'expectedDict':expect_dict},
{'expectedTypeRange':[dict], 'expectedDict':{}}
]
}
考虑到列表和字典较为特殊,expectedTypeRange 代表数据类型的可接受范围,expectedValueRange 主要用于辅助校验列表结构中每个元素的类型,expectedDict 则代表所期待的字典类型。
其中 expectedDict 示例值如下:
expected_dict = {
"baseText": {'expectedTypeRange': [str]},
"comparedText": {'expectedTypeRange': [str]},
"targetSimilarity": {
'expectedTypeRange': [dict],
'expectedDict': {
'test':{
'expectedTypeRange': [int, float]
}
}
}
}
可以看出字典中所有值的结构又是一个完整的 expected_structure 结构。
代码展示
(可优化的点还有很多~)
# 判断 pre_validate_data 符不符合给定的 expected_structure
def is_data_valid(expected_structure, pre_validate_data):
# 结构字典基础校验
if not isinstance(expected_structure, dict) or not expected_structure:
raise TypeError('expected_structure must be valid dict')
# 获取结构字典中固定键的值
expected_type_range = expected_structure.get('expectedTypeRange')
expected_value_range = expected_structure.get('expectedValueRange')\
if expected_structure.get('expectedValueRange') else []
expected_dict = expected_structure.get('expectedDict') if expected_structure.get('expectedDict') else {}
# 基础校验
if not all(map(lambda x: isinstance(x, type), expected_type_range)) \
or not isinstance(expected_value_range, list) or not isinstance(expected_dict, dict):
raise TypeError('expectedType、 expectedRange、 expectedDict must be type_list、 list、dict ')
# 数据类型不在期待值中则返回 False
if not type(pre_validate_data) in expected_type_range:
return False
# 如果待验证数据类型为 list ,则将每一个元素与期待值进行校验
if type(pre_validate_data) == list:
for list_piece in pre_validate_data:
if expected_value_range and not any(map(is_data_valid, expected_value_range,
x2list(len(expected_value_range), list_piece))):
return False
# 如果待校验数据类型为 dict,则将每一个键的值与期待值进行校验
elif type(pre_validate_data) == dict:
for k, v in expected_dict.items():
if k not in pre_validate_data or not is_data_valid(v, pre_validate_data[k]):
return False
return True
其中 x2list 函数做的事情是将 x(任意变量)复制后组成指定长度的数组:
def x2list(expected_len, raw_material):
new_list = list()
for i in range(expected_len):
new_list.append(raw_material)
return new_list
效果展示
test_data = [{"baseText": "", "comparedText": "", "targetSimilarity": {'test':'1.9'}},
{"baseText": "", "comparedText": "", "targetSimilarity": {'test':1.9}}]
expected_dict = {
"baseText": {'expectedTypeRange': [str]},
"comparedText": {'expectedTypeRange': [str]},
"targetSimilarity": {
'expectedTypeRange': [dict],
'expectedDict': {
'test':{
'expectedTypeRange': [int, float]
}
}
}
}
expected_structure = {
'expectedTypeRange': [list],
'expectedValueRange':
[
{'expectedTypeRange':[dict], 'expectedDict':expect_dict}
]
}
print(is_data_valid(expected_structure = expected_structure, pre_validate_data = test_data))
控制台输出如下:
False
Process finished with exit code 0
好的我们将 test_data 稍作修改({'test':'1.9'} ---> {'test':1.9}):
test_data = [{"baseText": "", "comparedText": "", "targetSimilarity": {'test':1.9}},
{"baseText": "", "comparedText": "", "targetSimilarity": {'test':1.9}}]
运行代码后控制台输出如下:
True
Process finished with exit code 0
总结
虽然说 深度校验函数 已经实现,但是仍然需要思考参数结构是否合理,后续持续优化、改进......
欢迎大家扫码关注我的公众号「智能自动化测试」,回复:测试进阶教程,即可免费获得 进阶教程 ~
再次感谢您的支持!
点个赞谢谢~