Python开发解析Swagger文档小工具|Python 主题月

本文正在参加「Python主题月」,详情查看 活动链接

一、HTTPRUNNER开源项目

当前框架已经更新到3.x版本;已经向pytest测试框架靠拢,并继承它的相关特性及优点。
复制代码
  • 但它的优点,在某些人眼里也是有‘缺陷’的
如:它支持抓包导出har数据文件har2case转化成json/yaml格式的测试套件;也支持手动编辑维护json/yaml格式的测试用例;
但是面对接口文档,比较多的接口需要做接口测试或集成方面又略显不足,人力的手工成本大、效率又不高,有没有办法提高呢?
故而开发与该框架契合比较高的解析接口文档工具,既支持json/yaml格式又可以输出excel文档,方便更灵活的开发测试框架。
复制代码
  • 咱们先安装环境,然后再一步一步完成自己的需求
// 暂时不使用它3.x版本的功能
pip install -y httprunner==2.1.3
复制代码
  • 验证环境:hrun -h ,等效于httprunner -h

image.png

二、开发解析swagger脚本

  • 先安装环境所需要的python库
// 可以写入requirements.txt文件,批量安装:pip install -r requirements.txt
openpyxl==3.0.4
requests==2.24.0
xlwt==1.3.0
xlrd==1.2.0
PyYAML==5.4.1
复制代码
  • 开发工具及工程结构
    • pycharm是python工程师的开发利器
    • 工程结构截图如下:

微信截图_20210703105544.png

  • 请注意配置properties/目录下的config.ini配置文件,需要填写自己解析的接口文档地址
[swaggerUrl]
# swagger接口文档地址
baseSever_url = http://localhost:8090
复制代码
例:自己部署一个swagger项目演示效果
  • 效果如下:

微信截图_20210703103107.png

  • 但是我们要的不是UI,而是接口,于是F12一开

f12.png

三、跳过开发过程看效果演示

脚本设计思想:
1、既要符合httprunner框架支持的json/yaml用例,又要支持excel输出
2、这点思想主要来源于爬虫,爬取接口响应的数据进行解析重新组装输出
复制代码
脚本核心代码,更多请上github查看
  • 清洗数据函数
    def wash_params(self, params, api, method, tag):
        """
        清洗数据json,把每个接口数据都加入到一个字典中
        :param params:
        :param params_key:
        :param method:
        :param key:
        :return:
        replace('false', 'False').replace('true', 'True').replace('null','None')
        """
        # 定义接口数据格式
        http_interface = {"name": "", "variables": {},
                          "request": {"url": "", "method": "", "headers": {}, "json": {}, "params": {}}, "validate": [],
                          "output": []}
        # 测试用例的数据格式:
        http_api_testcase = {"name": "", "api": "", "variables": {
        }, "validate": [], "extract": [], "output": []}

        # 这里的问题需要具体来分析,开发有时概要使用其他符号分割///分割符号需要替换
        case_name = params['summary']#.replace('/', '_').replace(" ", "_").replace(":", "_")
        case_name=re_pattern(case_name)
        # 用例名称
        http_interface['name'] = case_name
        http_api_testcase['name'] = case_name
        # 这是写入testcasejson下的名字,不是生成api的目录
        http_api_testcase['api'] = 'api/{}/{}.json'.format(tag, case_name)
        # 所有方法大写
        http_interface['request']['method'] = method.upper()
        # 这个是替换uri中的/get请求的拼接方式,有些是?参数=&参数拼接,需要另外解析
        http_interface['request']['url'] = api.replace(
            '{', '$').replace('}', '')
        parameters = params.get('parameters')    # 未解析的请求参数
        responses = params.get('responses')    # 未解析的响应参数
        if not parameters:    # 确保参数字典存在
            parameters = {}
            
        # 给测试用例字典,加入解析出来的参数
        for each in parameters:
            if each.get('in') == 'body':    # body 和 query 不会同时出现
                schema = each.get('schema')
                if schema:
                    ref = schema.get('$ref')
                    if ref:
                        # 这个uri拆分,根据实际情况来取第几个/反斜杠
                        param_key = ref.split('/', 2)[-1]
                        param = self.definitions[param_key]['properties']
                        for key, value in param.items():
                            if 'example' in value.keys():
                                http_interface['request']['json'].update(
                                    {key: value['example']})
                            else:
                                http_interface['request'][
                                    'json'].update({key: ''})
            
            # 实际是请求方法或者是请求参数的格式
            elif each.get('in') == 'query':
                name = each.get('name')
                for key in each.keys():
                    if not 'example' in key:    # 取反,要把在query的参数写入json测试用例
                        http_interface['request'][
                            'params'].update({name: each[key]})
        
        # 解析接口文档请求参数
        for each in parameters:
            if each.get('in') == 'header':
                name = each.get('name')
                for key in each.keys():
                    if 'example' in key:
                        http_interface['request'][
                            'headers'].update({name: each[key]})
                    else:
                        if name == 'token':
                            http_interface['request'][
                                'headers'].update({name: '$token'})
                        else:
                            http_interface['request'][
                                'headers'].update({name: ''})
                                
        # 解析接口文档响应参数
        for key, value in responses.items():
            schema = value.get('schema')
            if schema:
                ref = schema.get('$ref')
                if ref:
                    param_key = ref.split('/')[-1]
                    res = self.definitions[param_key]['properties']
                    i = 0
                    for k, v in res.items():
                        if 'example' in v.keys():
                            http_interface['validate'].append({"eq": []})
                            http_interface['validate'][i][
                                'eq'].append('content.' + k)
                            http_interface['validate'][i][
                                'eq'].append(v['example'])
                            http_api_testcase['validate'].append({"eq": []})
                            http_api_testcase['validate'][i][
                                'eq'].append('content.' + k)
                            http_api_testcase['validate'][
                                i]['eq'].append(v['example'])
                            i += 1
                else:
                    if len(http_interface['validate']) != 1:
                        http_interface['validate'].append({"eq": []})
            else:
                if len(http_interface['validate']) != 1:
                    http_interface['validate'].append({"eq": []})
        
        # 判断如果断言为空,则默认添加http状态断言
        if http_interface.get("validate"):
            http_interface.get("validate")[0].update({"eq":["status_code", 200]})
        
        # 测试用例的请求参数为空字典,则删除这些key
        if http_interface['request']['json'] == {}:
            del http_interface['request']['json']
        if http_interface['request']['params'] == {}:
            del http_interface['request']['params']
        # 定义接口测试用例
        tags_path = os.path.join(case_dir, tag).replace("/", "_").replace(" ", "_")
        # 创建不存在的文件目录,递归创建
        if not os.path.exists(tags_path):
            os.makedirs(tags_path)
            
        # 拼接api用例路径
        json_path = os.path.join(tags_path, case_name + '.json')
        # testcases/写入数据
        write_data(http_interface, json_path)
        
        return http_api_testcase
复制代码
  • 进入脚本,执行程序入口
if __name__ == '__main__':
    url = conf.get_value("swaggerUrl", "baseSever_url")
    js = AnalysisSwaggerJson(url)
    js.analysis_json_data(isDuplicated=False)
    js.write_excel(url, handlefile.get_file_list(case_dir))
复制代码
  • 结果输出如图所示

微信截图_20210703111436.png

  • 既有json,同时又有excel文件,是不是两全其美之法?
先来完善api/目录下的测试用例:
  • 补全接口入参,如图所示

微信截图_20210703113055.png

  • 进入工程swagger用例目录,执行:hrun testcases\用户相关接口.json,日志截图:

image.png

  • 并且生成html测试报告,打开如图:

image.png

  • 是不是非常nice
一边生成json/yaml符合httprunner测试框架;
一边生成excel可以自己定制开发自动化测试框架。
复制代码

四、Swagger工具总结

  • 先推广一波httprunner_swagger小工具,已经有19个star了,还缺你哦;
  • 欢迎提出不同优化建议,如果再结合其他测试框架,做成一个强大第三方开源库,服务更多的人群,这该是一件美事!
© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享