背景

公司上线了信创版本的新项目,迭代频繁,需要能够支持对基础功能的快速回归,考虑到 UI 测试投入成本大收益慢且不能实现快速回归,选择了接口自动化测试。调研了市场上的自动化测试工具,基于公司项目的特点,最终选择了灵活性较大的 httprunner 框架。

基于系统请求特点改造适配
接口测试遇到的问题:
1、系统请求的请求头中需要包含 token,用例步骤中需要自动带上 token
2、系统请求的参数 content-type 都是 application/x-www-form-urlencoded; charset=UTF-8,传入到后端的中文都是经过了 url 编码后的,如果在用例编写过程中也直接用编码后的参数,就很难做到可参数化,且用例的参数无法直观看到是什么参数。
解决方法是定义一个装饰器 pre_decorator,作用于类上面。pre_decorator 实现注入 base_url,构造带有 token 的请求头,从参数 excel 文件读取参数,自动替换测试类的参数和对测试步骤中的请求参数进行 url 编码

def pre_decorator(cls):
    pre_handle(cls.config, cls.config.path, cls.teststeps)
    return cls  

在类加载时候返回经过 pre_handle 处理后返回新的类,pre_handle

def pre_handle(config=Config, file_path='', teststeps=[Step]):
    config.base_url('${ENV(base_url)}')
    config.verify(False)
    # 构造请求头
    if teststeps:
        headers = {
            "User-Agent": get_user_agent(),
            "Content-Type": "application/x-www-form-urlencoded; charset=UTF-8",
            "Authentication": get_accessToken()
        }
        for step in teststeps:
            if step.request.headers:
                headers.update(step.request.headers)
                step.request.headers.update(headers)
            else:
                step.request.headers.update(headers)
    # 填充参数
    index = str.index(file_path, 'testcases')
    file_path = file_path[index:].replace('\\', '.')
    if parameters == {}:
        init_parameters()
    config_variables = {}
    if file_path in parameters:
        if parameters[file_path] and str(parameters[file_path]) != 'nan':
            config_variables = eval(parameters[file_path])
            if config.struct().variables != None and len(config.struct().variables) > 0:
                new_config_variables = copy.copy(config.struct().variables)
                new_config_variables.update(config_variables)
                config.variables(**new_config_variables)
            else:
                config.variables(**config_variables)
    # 自动替换参数和编码参数
    if teststeps:
        for step in teststeps:
            # 替换参数
            data = step.request.data
            if data != None:
                for param in config.struct().variables:
                    data = re.sub(f"{param}=(?!\\$).+?&", f"{param}=        {config.struct().variables[param]}&", data)
                    data = re.sub(f'"{param}":"(?!\\$)(?!\\[).+?"', f'"{param}":"{config.struct().variables[param]}"',
                                  data)
                    data = re.sub(f'\'{param}\':.+?\'(?!\\$)(?!\\[).+?\'',
                                  f'\'{param}\': \'{config.struct().variables[param]}\'', data)
                step.request.data = data
                if 'Content-Type' not in step.request.headers:
                    step.request.headers['Content-Type'] = 'application/x-www-form-urlencoded; charset=UTF-8'
                # 编码参数
                if 'application/x-www-form-urlencoded; charset=UTF-8' in step.request.headers["Content-Type"] and type(
                        data) == str and '${' not in data:
                    step.request.data = parse.quote(step.request.data, safe="=,&, 00:00:00,00:00:00")
                elif 'application/x-www-form-urlencoded' in step.request.headers["Content-Type"] and type(data) == str:
                    step.struct().setup_hooks.append("${encode($request)}")

用例编写和运行
开发的小工具实现了可以快速将请求转换为步骤,请求参数做了 url 反编码,可以快速编写用例,开发了用例启用功能,选择需要跑的用例,点击运行测试用例即可一键执行测试用例,执行完后自动打开对应的 allure 报告,实现快速回归。

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。


↙↙↙阅读原文可查看相关链接,并与作者交流