自动化测试平台:数据自定义函数处理的实现

easy-test

之前做了一个接口自动化测试平台easy-test,并且后期一直在使用和迭代优化,有兴趣的可以看下,一起讨论交流。
对于这个工具,从一个测试的角度,通过进行接口的自动化测试,不断的完善已有功能和添加新功能。在最初的大版本后,又新增了html报告下载,测试初始变量设置,数据处理结果查看等功能。

接口返回数据自定义处理

此次新增了一个功能对于这个工具来说是史诗级更新,解决了参数关联中一个比较难处理的问题,这个功能就是自定义关联数据处理,可以通过在页面上自己定义一个python函数来对接口返回数据进行处理,保存为变量供下一个接口使用。
自动化测试平台:数据自定义函数处理的实现_第1张图片
如图所示,这个用例(接口)的输入框可以填写python函数作为它的处理语句,这个函数是自定义的,函数名称、参数名称完全可以起任意名称,只需要遵循规则:入参为2个字典,param1->接口返回数据 param2->当前运行工程的全局变量, 需要返回一个变量字典。

为什么要做这么一个功能?

在实际的使用这个工具进行接口自动化测试过程中,发现在数据关联的处理上不够灵活。比如新增接口新增了一个数据A(有id、name字段),查询接口查询到包含A的数据列表,删除接口删除A,在这么一个流程中,删除A需要知道A的id,但新增不会返回id,而查询接口返回的是包含A的列表(此处知道A的name,无法根据名称条件查询),按照工具之前提供的后置处理方法如json提取器、正则表达式,则只能得到其中一个数据的id,不能保证是A的。如果要得到A的id,就需要遍历数据列表,找到name等于A的name的数据,然后获取id。在写代码脚本时可以简单操作的,在平台化后就没有那么灵活,因为这样就需要根据业务逻辑来定制了。
所以想到了通过新增一个自定义的函数,每个接口都可以有属于自己的函数,在函数里面做自己的逻辑来实现不同的业务需要。这就在平台化和脚本中间找到了一个平衡点,兼顾了灵活性和规范化。

ps:使用jsonpath也可以实现上面的业务,此处只是举例,不同的业务有不同的需求

怎么样实现这个功能?

将自定义的函数作为文本保存在数据库中,字段类型为TEXT,在数据存储方面这样做改动是最小的,只需要修改数据库表结构就可以。在执行测试时,再从数据库中读取数据写入到文件中进行执行。
这样一个预设脚本,在运行过程中执行的操作,其实就是在代码中再执行代码。由于后端使用python,所以最先想到使用importlib来动态加载模块,调用函数。但实际试验时发现,只能动态加载flask启动前的模块,flask启动后修改的代码,需要重启服务后才能生效,总不能修改一条用例的数据后就重启一次服务,所以这个方案被否决了。
既然不能通过flask内部调用进行自定义函数的执行,那么就简单粗暴一点,直接进行命令行操作python xxx.py执行脚本,再获取结果保存。

def make_deal_file(data, fun, var_dick):
    template = """
# coding:utf-8
interface_return = {{data}}
var_dick = {{var_all}}

    
{{fun}}
    
    
print({{fun_name}}(interface_return, var_dick))
"""

    fun_name = re.findall(r'def (.*)\(', fun)[0]
    r = template.replace('{{data}}', data).replace('{{var_all}}', var_dick).replace('{{fun}}', fun).\
        replace('{{fun_name}}', fun_name)

    # 将文件的true替换为True,false替换为False
    replace_true = re.sub('true', 'True', r)
    replace_false = re.sub('false', 'False', replace_true)
    content = replace_false

    tmp_directory = os.path.dirname(os.path.dirname(os.path.realpath(__file__))) + '/document/tmp/'
    if not os.path.exists(tmp_directory):
        os.makedirs(tmp_directory)
    file_name = fun_name + '_' + str(int(time.time() * 1000000)) + '.py'
    path = tmp_directory + file_name

    f = open(path, 'w', encoding='utf-8')
    f.write(content)
    f.close()

    return path

这个就是一个生成被处理脚本的方法,它接收三个参数,分别是接口返回数据、自定义的python函数和当前运行过程中的变量字典,其中接口返回数据和变量字典是自定义函数的参数,接口返回是被处理的数据源,变量字典是作为辅助获取变量,比如获取例子中数据A的name。自定义函数需要返回处理完成的变量字典。

        # 用户自定义的python函数处理
        elif self.deal == CaseDealEnum.CUSTOMIZE.value:
            deal_path = make_deal_file(json.dumps(interface_return, ensure_ascii=False), self.condition,
                                       json.dumps(var_dick, ensure_ascii=False))
            try:
                result = os.popen('python ' + deal_path)
                result_str = re.sub('\'', '\"', result.read())
                deal_res = json.loads(result_str)
                self.deal_result.update(deal_res) if deal_res else 1
                var_dick.update(self.deal_result)
            except Exception:
                current_app.logger.error('自定义后置处理异常' + str(sys.exc_info()))
            finally:
                remove_deal_file(deal_path)

在执行测试时,如果处理方式为自定义处理,则先生成被执行的python文件,然后通过os.popen运行自定义函数获取结果,将结果保存在当前用例中并添加到当前运行过程中的变量字典中。


经过使用验证,这个功能解决了后置处理不够灵活的问题,非常好用。和没有这个功能的版本比较,此次升级可以说是史诗级更新。

你可能感兴趣的:(点点点工程师,自动化,软件测试,python,接口,flask)