测试之路 pytest接口自动化框架扩展-json数据解析

2022-12-12 16:44:57 浏览数 (1)

各位大佬,国庆快乐~小王同学又来更新公众号了。上期内容说到小p的工作流程。本期来介绍下小j的工作职责。废话不多,昊料开始~

开篇

上期内容简单说到了。params类类型参数的解析方法。相较于简单。本期内容就json格式的数据解析,来进行阐述。

在MeterSphere中,有两种方式可以进行json格式的数据维护。一种是使用他们自带的JsonSchema来填写key-value表单。另一种就是手写json。

手写json在日常工作中效率较低,原因有二,一是手写太麻烦,占据大量个工作时间,影响效率。二是对正确性以及层级结构无法保证准确性。两者相比较,故选择JsonSchema的方式来维护json格式的数据。

json格式数据模型如下

代码语言:javascript复制
"jsonSchema": {
      "properties": {
        "字段1": {
          "mock": {
            "mock": ""
          },
          "type": "string",
          "description": "字段描述。。。"
        },
        "字段2": {
          "type": "number",
          "mock": {
            "mock": ""
          },
          "minLength":50,
          "maxLength":100
        },
        "字段3": {
          "type": "integer",
          "mock": {
            "mock": ""
          },
          "description": "字段描述"
        }
      },
      "type": "object",
      "mock": {
        "mock": ""
      },
      "required": [
        "字段1",
        "字段2",
      ]
    }

使用JsonSchema作为最外层节点,第二层节点包含了类型、字段属性、必填字段列表等参数信息。第三层节点就是字段的一些属性,包含了字段长度、字段名称、字段类型、字段描述等

特别需要说明的是,MeterSphere的字段类型有很多,其中包含了object以及array这两种类型的数据

  • object:如果字段类型是object,那么该字段节点下会嵌套另外一些字段,这些字段也是json格式的
  • array:同理,如果字段类型是array,那么该字段下面会嵌套一个列表,列表中的每一个元素,都是json格式,不可以手动设置key,是从0递增自动命名。

这两种类型是可以无限重复套娃下去。只要你需要。

所以在解析这类数据时,我们就需要先解决这种层层嵌套的问题。

思路梳理

  • 首先判断一下数据类型是否为上述这种套娃格式
    • 判断字段类型是object还是array
    • 利用python的递归,调用自身。并将字段属性作为参数传给这个函数
  • 然后提取字段中的最大值,最小值,以及参数名称、类型
  • 判断当前字段是否在必填列表中,如果在,则将这个字段设置为必填

如上是大概的解题思路,抛开拆解套娃,代码相对简单。如下是源码展示

代码语言:javascript复制
# 解析json请求的参数
def post_arguments(data, required_list=None):
    field = {}
    if not isinstance(data, dict):
        raise TypeError("'data' is not dict")
    for key, value in data.items():
        if value["type"] == "object" and "properties" in value:
            # 递归调用,实现多层嵌套解析
            if "required" in value:
                recursion_par = post_arguments(value["properties"], value["required"])
                par = {
                    key:
                        {"type": value["type"],
                         "description": value["description"] if "description" in value else "",
                         **recursion_par
                         }
                }
                field.update(par)
            else:
                recursion_par = post_arguments(value["properties"])
                par = {
                    key: {"type": value["type"],
                          "description": value["description"] if "description" in value else "",
                          **recursion_par
                          }
                }
                field.update(par)
        elif value["type"] == "array" and "items" in value:
            for l, i in enumerate(value["items"]):
                for arr_key, arr_value in i.items():
                    if arr_value == "object" and "properties" in arr_value:
                        # 递归调用,实现多层嵌套解析
                        if "required" in arr_value:
                            recursion_par = post_arguments(arr_value["properties"], value["required"])
                            par = {
                                l: {"type": value["type"],
                                    "description": value["description"] if "description" in value else "",
                                    **recursion_par

                                    }
                            }
                            field.update(par)
                        else:
                            recursion_par = post_arguments(arr_value["properties"])
                            par = {
                                l: {"type": value["type"],
                                    "description": value["description"] if "description" in value else "",
                                    **recursion_par
                                    }
                            }
                            field.update(par)
                    elif arr_value == "array" and "items" in arr_value:
                        if "required" in arr_value:
                            recursion_par = post_arguments(arr_value["properties"], arr_value["required"])
                            par = {
                                l: {"type": value["type"],
                                    "description": value["description"] if "description" in value else "",
                                    **recursion_par
                                    }
                            }
                            field.update(par)
                        else:
                            recursion_par = post_arguments(arr_value["properties"])
                            par = {
                                l: {"type": value["type"],
                                    "description": value["description"] if "description" in value else "",
                                    **recursion_par
                                    }
                            }
                            field.update(par)
                    else:
                        maxLength = MAX_LENGTH
                        minLength = MIN_LENGTH
                        required = "false"
                        if "maxLength" in arr_key:
                            maxLength = i["maxLength"]
                        elif "maxLength" in arr_key:
                            minLength = i["minLength"]
                        if required_list:
                            if l in required_list:
                                required = "true"
                        items_par = {
                            key:
                                {
                                    "type": value["type"],
                                    "description": value["description"] if "description" in value else "",
                                    l: {
                                        "type": i["type"],
                                        "required": required,
                                        "max": maxLength,
                                        "min": minLength
                                    }
                                }
                        }
                        field.update(items_par)
        else:
            maxLength = MAX_LENGTH
            minLength = MIN_LENGTH
            required = "false"
            if "maxLength" in value:
                maxLength = value["maxLength"]
            elif "minLength" in value:
                minLength = value["minLength"]
            if required_list:
                if key in required_list:
                    required = "true"
                else:
                    required = "false"
            par = {
                key: {
                    "type": value["type"],
                    "description": value["description"] if "description" in value else "",
                    "required": required,
                    "max": maxLength,
                    "min": minLength,
                }
            }
            field.update(par)
    return field

可以看到,思路不是很难,但是代码还是比较臃肿的,其中有很多的代码是冗余的,在后期优化中,将考虑这块重构一下。大家在写的时候将思路缕清,别写出我这么烂的代码。。。。引以为戒~

结语

总结一下这个函数

  • 首先在写的时候,多重嵌套是个难题,可以通过递归的方式解决
  • 另外一定在思路缕清的前提下,再开始写代码,我就是在边写边思考,一个for循环一个for循环的嵌套。导致代码极其臃肿。执行效率有一定程度的降低,且代码可读性不好
  • 公共代码提取:像一些数据结构模板,这些都可以提取成一个公共变量,然后调用即可。在函数中反复写着相类似的模板,是一种很愚蠢的行为。。。

说着说着就成批斗大会了。。

好了,本期内容就先到这里了。虽然小j体型憨态可拘,办事效率也比较低,但是也能够正常完成任务,待小j同学提高业务能力以后,再给大家做工作汇报。

最后,祝各位大佬国庆节快乐。外出游玩要做好防疫措施哈~886

0 人点赞