• apiAutoTest-更新2020/11/23


    原始版本

    简书:https://www.jianshu.com/p/6bfaca87a93b
    博客园:https://www.cnblogs.com/zy7y/p/13426816.html
    testerhome:https://testerhome.com/topics/25003

    最新用例截图以及用例填写格式

    case.png

    Snipaste_2020-11-23_07-21-53.png

    数据依赖/路径参数依赖

    我理解的参数依赖/接口依赖就是接口进行关联操作,比如有些查询接口需要登录之后才可以操作,那么我们就需要拿到token之类的东西,这一部分东西是放到header中的,apiAutoTest围绕的只有路径参数依赖,请求数据依赖

    • 路径参数依赖

      譬如说现在的restful,一个users接口,路由一般这样的users他的请求方式是get,这个路由我们把他认为是查所有用户,如果查某一个用户可能是这样的users/:id也是个get请求,这里这个id想表达的意思是这里有个需要个用户id的参数,比如1-500里面的任意1个,也就是说这个id是可变的,可以从登录接口的返回响应取一个叫userId的值

    • 请求参数依赖

      这个应该好理解些,就是说支付接口需要的订单id,是从上一步提交订单接口返回的响应订单id

    举个例子

    假设现在有个实际响应结果字典如下

    {"case_002": {
            "data": {
                "id": 500,
                "username": "admin",
                "mobile": "12345678",
            }},
      "case_005": {
            "data": {
                "id": 511,
                "create_time": 1605711095
            },
        }
    }
    
    • excel中接口路径内容:users/&$.case_005.data.id&/state/&$.case_005.data.careate_time&

      代码内部解析后如下:users/511/state/1605711095

      &$.case_005.data.id& 代表从响应字典中提取case_005字典中data字典中的id的值,提取出来的结果是511

    • excel中请求参数内容如下:

      {
       "pagenum": 1, 
       "pagesize": "12",
       "data": &$.case_005.data&, 
       "userId": &$.case_002.data.id&
      }
      

      代码内部解析后如下:

      {
       "pagenum": 1, 
       "pagesize": "12",
       "data": {
                  "id": 511,
                  "create_time": 1605711095
              }, 
       "userId": 500
      }
      

    其实不难看出其中规则&jsonpath提取语法&,如果你需要的内容是字符串类型,只需要这样"&jsonpath提取语法&"

    上传文件

    用例中书写格式,在上传文件栏

    # 单文件上传在excel中写法
    {"接口中接受文件对象的参数名": "文件路径地址"}
    
    # 多文件上传在excel中写法
    {"接口中接受文件对象的参数名": ["文件路径1", "文件路径2"]}
    

    预期结果

    用例书写格式

    # 断言一个内容
    {"jsonpath提取表达式": 预期结果内容}
    # 多个断言
    {"jsonpath提取表达式1": 预期结果内容1,"jsonpath提取表达式2": 预期结果内容2}
    

    其他优化

    • config.yaml文件中新增可配置初始header,整体代码优化,相比之前,同样测试用例执行下,快了2s左右
    • 将配置文件读取,用例读取整合在read_file.py
    • 移除报告压缩方法
    • 减少日志信息

    现依赖处理代码

    tools/init.py

    #!/usr/bin/env/python3
    # -*- coding:utf-8 -*-
    """
    @project: apiAutoTest
    @author: zy7y
    @file: __init__.py
    @ide: PyCharm
    @time: 2020/7/31
    """
    import json
    import re
    
    import allure
    
    from jsonpath import jsonpath
    from loguru import logger
    
    
    def extractor(obj: dict, expr: str = '.') -> object:
        """
        根据表达式提取字典中的value,表达式, . 提取字典所有内容, $.case 提取一级字典case, $.case.data 提取case字典下的data
        :param obj :json/dict类型数据
        :param expr: 表达式, . 提取字典所有内容, $.case 提取一级字典case, $.case.data 提取case字典下的data
        $.0.1 提取字典中的第一个列表中的第二个的值
        """
        try:
            result = jsonpath(obj, expr)[0]
        except Exception as e:
            logger.error(f'提取不到内容,丢给你一个错误!{e}')
            result = None
        return result
    
    
    def rep_expr(content: str, data: dict, expr: str = '&(.*?)&') -> str:
        """从请求参数的字符串中,使用正则的方法找出合适的字符串内容并进行替换
        :param content: 原始的字符串内容
        :param data: 在该项目中一般为响应字典,从字典取值出来
        :param expr: 查找用的正则表达式
        return content: 替换表达式后的字符串
        """
        for ctt in re.findall(expr, content):
            content = content.replace(f'&{ctt}&', str(extractor(data, ctt)))
        return content
    
    
    def convert_json(dict_str: str) -> dict:
        """
        :param dict_str: 长得像字典的字符串
        return json格式的内容
        """
        try:
            if 'None' in dict_str:
                dict_str = dict_str.replace('None', 'null')
            elif 'True' in dict_str:
                dict_str = dict_str.replace('True', 'true')
            elif 'False' in dict_str:
                dict_str = dict_str.replace('False', 'false')
            dict_str = json.loads(dict_str)
        except Exception as e:
            if 'null' in dict_str:
                dict_str = dict_str.replace('null', 'None')
            elif 'true' in dict_str:
                dict_str = dict_str.replace('true', 'True')
            elif 'False' in dict_str:
                dict_str = dict_str.replace('false', 'False')
            dict_str = eval(dict_str)
            logger.error(e)
        return dict_str
    
    
    def allure_title(title: str) -> None:
        """allure中显示的用例标题"""
        allure.dynamic.title(title)
    
    
    def allure_step(step: str, var: str) -> None:
        """
        :param step: 步骤及附件名称
        :param var: 附件内容
        """
        with allure.step(step):
            allure.attach(json.dumps(var, ensure_ascii=False, indent=4), step, allure.attachment_type.TEXT)
    

    tools/data_process.py

    #!/usr/bin/env/python3
    # -*- coding:utf-8 -*-
    """
    @project: apiAutoTest
    @author: zy7y
    @file: data_process.py
    @ide: PyCharm
    @time: 2020/11/18
    """
    from tools import logger, extractor, convert_json, rep_expr, allure_step
    from tools.read_file import ReadFile
    
    
    class DataProcess:
        response_dict = {}
        header = ReadFile.read_config('$.request_headers')
        have_token = header.copy()
    
        @classmethod
        def save_response(cls, key: str, value: object) -> None:
            """
            保存实际响应
            :param key: 保存字典中的key,一般使用用例编号
            :param value: 保存字典中的value,使用json响应
            """
            cls.response_dict[key] = value
            logger.info(f'添加key: {key}, 对应value: {value}')
    
        @classmethod
        def handle_path(cls, path_str: str) -> str:
            """路径参数处理
            :param path_str: 带提取表达式的字符串 /&$.case_005.data.id&/state/&$.case_005.data.create_time&
            上述内容表示,从响应字典中提取到case_005字典里data字典里id的值,假设是500,后面&$.case_005.data.create_time& 类似,最终提取结果
            return  /511/state/1605711095
            """
            # /&$.case.data.id&/state/&$.case_005.data.create_time&
            return rep_expr(path_str, cls.response_dict)
    
        @classmethod
        def handle_header(cls, token: str) -> dict:
            """处理header
            :param token: 写: 写入token到header中, 读: 使用带token的header, 空:使用不带token的header
            return
            """
            if token == '读':
                return cls.have_token
            else:
                return cls.header
    
        @classmethod
        def handler_files(cls, file_obj: str) -> object:
            """file对象处理方法
            :param file_obj: 上传文件使用,格式:接口中文件参数的名称:"文件路径地址"/["文件地址1", "文件地址2"]
            实例- 单个文件: &file&D:
            """
            if file_obj == '':
                return
            for k, v in convert_json(file_obj).items():
                # 多文件上传
                if isinstance(v, list):
                    files = []
                    for path in v:
                        files.append((k, (open(path, 'rb'))))
                else:
                    # 单文件上传
                    files = {k: open(v, 'rb')}
            return files
    
        @classmethod
        def handle_data(cls, variable: str) -> dict:
            """请求数据处理
            :param variable: 请求数据,传入的是可转换字典/json的字符串,其中可以包含变量表达式
            return 处理之后的json/dict类型的字典数据
            """
            if variable == '':
                return
            data = rep_expr(variable, cls.response_dict)
            variable = convert_json(data)
            return variable
    
        @classmethod
        def assert_result(cls, response: dict, expect_str: str):
            """ 预期结果实际结果断言方法
            :param response: 实际响应字典
            :param expect_str: 预期响应内容,从excel中读取
            return None
            """
            expect_dict = convert_json(expect_str)
            index = 0
            for k, v in expect_dict.items():
                actual = extractor(response, k)
                index += 1
                logger.info(f'第{index}个断言,实际结果:{actual} | 预期结果:{v} 
    断言结果 {actual == v}')
                allure_step(f'第{index}个断言',  f'实际结果:{actual} = 预期结果:{v}')
                assert actual == v
    

    源码地址

    master: 分支为最新代码

    version1.0: 分支为之前开源的代码(通过字典迭代的方式来处理数据依赖)

    Https://gitee.com/zy7y/apiAutoTest.git

    Https://github.com/zy7y/apiAutoTest.git

    后续打算

    目前在公司正在做接口测试,说实话也是摸索着来,以上的优化项都是实际做的过程中突然想到的,然后就更新了

    • 接入用例前后置SQL, 前置SQL目前想的是现在项目中遇到的问题,有些接口没有返回需要的数据,这里就要用前置SQL查询的结果传到请求数据里面了,后置SQL主要是请求后查看数据库中的数据是否变动,形成数据库断言 2020/12/08 完成 , 至此 apiAutoTest 应该不会 再有大更新~~~ 谢谢 看这个Demo的人
    • 企业微信推送:目前项目中预想的效果,是后端人员提交代码,自动部署之后,通过gitlab-ci 启动测试代码,进行接口测试完成之后采集allure中的测试结果一有异常/失败用例就发送邮件并进行企业微信推送给领导 这个其实就是重新发送请求~
    • .... 就不说了还有很多优化项,能力不够好好充电吧,~~

    致谢

    谢谢各位对apiAutoTest的帮助,谢谢~~~

  • 相关阅读:
    UVA1349 Optimal Bus Route Design 最优巴士路线设计
    POJ3565 Ants 蚂蚁(NEERC 2008)
    UVA1663 Purifying Machine 净化器
    UVa11996 Jewel Magic 魔法珠宝
    NEERC2003 Jurassic Remains 侏罗纪
    UVA11895 Honorary Tickets
    gdb调试coredump(使用篇)
    使用 MegaCLI 检测磁盘状态并更换磁盘
    员工直接坦诚直来直去 真性情
    山东浪潮超越3B4000申泰RM5120-L
  • 原文地址:https://www.cnblogs.com/zy7y/p/14022398.html
Copyright © 2020-2023  润新知