Python之数据结构深度校验

256 阅读2分钟

起源

最近使用自建的测试平台时,发现 「批量创建 / 修改用例」 不够方便,故计划新增 「测试用例导入 / 导出」 功能。从易于编程的角度上考虑,决定使用 Excel 承载测试用例数据。

导出功能较为可控,但导入功能的输入数据完全由使用者主导。作为 QA,当然要对用户输入数据做到最全面、最深层次的校验。故笔者决定实现一个深度校验函数

实现思路

计划该函数共接收 2 个参数:pre_validate_dataexpected_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

总结

虽然说 深度校验函数 已经实现,但是仍然需要思考参数结构是否合理,后续持续优化、改进......

欢迎大家扫码关注我的公众号「智能自动化测试」,回复:测试进阶教程,即可免费获得 进阶教程 ~

再次感谢您的支持!

点个赞谢谢~