zoukankan      html  css  js  c++  java
  • 自定义实现httprunner debugtalk 的函数助手功能

    1.需求背景:

    我们在进行接口请求时需要用到各种各样的数据格式,比如随机唯一值,时间戳等等这些可以通过参数化函数来实现httprunner在实现上也参考了jm的类似思想设计 :

    我们如果做平台化时,就可以实现类似debugtalk的设计思路来实现参数化函数自定义使用:

    设计思路:

    1.动态加载模块debugtalk里的方法并且获取参数和返回值:

    2.请求参数提取出函数并且判断它是否在debugtalk加载出来对象方法里,如果在就执行替换方法位里面方法执行返回值:

    第一步,编写debugtalk 定义函数钩子

     第二步:编写动态加载提取方法mapping:

    import types
    import importlib
    import ast
    import re
    import json
    import os
    
    def parse_string_value(str_value):
        """
        :param str_value: '123'==>123
        :return:
        """
        try:
            return ast.literal_eval(str_value)
    
        except ValueError:
            return str_value
        except SyntaxError:
            # e.g. $var, ${func}
            return str_value
    
    
    def load_module_functions(module):
        """
        load debugtalk functions mapping
        """
    
        module_functions = {}
    
        for name, item in vars(module).items():
            if isinstance(item, types.FunctionType):
                module_functions[name] = item
    
        return module_functions
    
    
    def parse_function_params(params):
        """
        parse the function params and return it
        example:
            parse_function_params("1, 2, a=3, b=4")
        :return:  {'args': [1, 2], 'kwargs': {'a':3, 'b':4}}
        """
        function_meta = {
            "args": [],
            "kwargs": {}
        }
    
        params_str = params.strip()
        if params_str == "":
            return function_meta
    
        args_list = params_str.split(',')
        for arg in args_list:
            arg = arg.strip()
            if '=' in arg:
                key, value = arg.split('=')
                function_meta["kwargs"][key.strip()] = parse_string_value(value.strip())
            else:
                function_meta["args"].append(parse_string_value(arg))
    
        return function_meta
    
    
    
    
    
    def extra_func_name(data: dict):
        """
        extract method name list  of data value
        :return : ['__RandomInt(5,8)}}', '__UUID1()'] ect.
         """
        d = json.dumps(data, separators=(',', ':'))
        funcs = re.findall(r"{{(.*?)}}", d)
        return funcs
    
    
    def hook_replace(data: dict)->dict:
        """
        :function: replace the function with debugtalk function's return result
        :param data: {"name": "${{__RandomInt(5,8)}}", "foo2": "${{__UUID1()}}"}
        :return: dict 
        """
        dump_string = json.dumps(data)
        imported_module = importlib.import_module("mysite.debugtalk")
        mapping = extra_func_name(data)
        for method_name in mapping:
            function_name = re.findall("(.*?)[(]", method_name)[0]
            func_mapping = load_module_functions(imported_module)
            function = func_mapping.get(function_name)
            if function:
                params = re.findall(function.__name__ + "[(](.*?)[)]", method_name)[0]
                args_kwargs = parse_function_params(params)
                args, kwargs = args_kwargs.get("args"), args_kwargs.get("kwargs")
                if not args and not kwargs:
                    res = function()
                    if isinstance(res, (int, float, list)):
                        ret = dump_string.replace('"${{' + method_name + '}}"', json.dumps(res))
                        dump_string = ret
                    else:
                        ret = dump_string.replace('${{' + method_name + '}}', str(res))
                        dump_string = ret
                else:
                    res = function(*args, **kwargs)
    
                    if isinstance(res, (int, float, list)):
                        ret = dump_string.replace('"${{' + method_name + '}}"', json.dumps(res))
                        dump_string = ret
                    else:
                        ret = dump_string.replace('${{' + method_name + '}}', str(res))
                        dump_string = ret
        return json.loads(dump_string)
    

    其中hook_replace 方法func_mapping 打印出来是这样:

    {'__RandomString': <function __RandomString at 0x00000185B901A048>, '__RandomInt': <function __RandomInt at 0x00000185B9114510>,

    可以通过key获取到function:

    而key可以提取data = {"name": "${{__RandomInt(5,8)}}", "foo2": "${{__UUID1()}}"},通过正则提取:

    def extra_func_name(data: dict):
    """
    extract method name list of data value
    :return : ['__RandomInt(5,8)}}', '__UUID1()'] ect.
    """
    d = json.dumps(data, separators=(',', ':'))
    funcs = re.findall(r"{{(.*?)}}", d)
    return funcs
    然后迭代数组mapping,再次提取函数名称function_name,这样就可以通过function_name 为key拿到func_mapping 的对应函数对象,

    接下来就需要获取参数了,入参*args,**kwargs:

    params = re.findall(function.__name__ + "[(](.*?)[)]", method_name)[0]

    把参数解析通过args_kwargs = parse_function_params(params)返回:
    {'args': [1, 2], 'kwargs': {'a':3, 'b':4}} 或者 {}
    由于并不是所有的函数都有入参所以需要判断
    if not args and not kwargs:
    res = function()
    else:
        res = function(*args, **kwargs)

    最后就是替换函数助手为实际返回值了,分str一种情况,int,float list 一种情况,因为有的参数要求请求时就是int类型保持原有数据类型:
    if isinstance(res, (int, float, list)):
    ret = dump_string.replace('"${{' + method_name + '}}"', json.dumps(res))
    dump_string = ret
    else:
    ret = dump_string.replace('${{' + method_name + '}}', str(res))
    dump_string = ret

    最后就是关于动态加载模块知识:

  • 相关阅读:
    237. Delete Node in a Linked List
    430. Flatten a Multilevel Doubly Linked List
    707. Design Linked List
    83. Remove Duplicates from Sorted List
    160. Intersection of Two Linked Lists
    426. Convert Binary Search Tree to Sorted Doubly Linked List
    142. Linked List Cycle II
    类之间的关系
    初始化块
    明确类和对象
  • 原文地址:https://www.cnblogs.com/SunshineKimi/p/15048900.html
Copyright © 2011-2022 走看看