设计动态变量参数(二)

233 阅读5分钟

「这是我参与2022首次更文挑战的第14天,活动详情查看:2022首次更文挑战」。

抛出问题:复杂的问题简单解决,是一项高深的解决技巧,这次好像并没有什么不妥?

请认真的思考一下?你突然会发现,在做(参数关联)这件事情之前,是否需要先识别哪些接口有参数依赖、哪些参数需要提取出来作为全局变量,并且只能是写在代码中(一个接口请求对应一个测试方法),一旦这个参数发生了改变,就要去修改代码,这无疑是致命的;如果存在接口多、参数多的情况(这是一定存在的),你会发现全篇的global字眼。

如何设计参数并完成替换

前面说了global关键字,但是咱们还学过了re正则表达式模块、还有其中关于字符串替换的方法,先回顾一下这些知识点!

  • 入参:可以是从生成的数据中替换、也可以是从上一个接口响应的参数再进行替换;在我们设计接口测试用例数据的时候,一般都是存在外部文件介质中;然后ddt通过读取不同的数据执行测试。

思路:假设参数data = {"mobile":"#mobile#","passwd":"#passwd#"}

测试方法会获取data参数,直接请求时它是非法的,咱们需要进行替换;

咱们直接跳过if条件判断#mobile#是否在参数中,直接使用re正则匹配

r_str = "#mobile#"

n_str = re.sub(r_str,"13800138000",data) # data从excel读取的时候是str类型

print(n_str) # 输出:{"mobile":"13800138000","passwd":"#passwd#"}

是不是这样就需要if条件判断#xxx#是否存在data数据中,然后进行替换?至于参数中有多少个#xxx#格式的数据,咱们是不知道的。

所以需要通过设计通用的参数替换方法,为了方便写正则,咱们使用了#xxx#将需要替换的参数标识出来。

因为咱们知道入参可能不止一个,在re模块中有个search方法;还有替换的参数从哪里需要解决,两个方向:一是配置文中读取,二是接口响应所得。

r_str = "#(.+?)#" # 演示正则匹配字符串并能提取替换的参数和目标数据

res = re.search(r_str,data) # 从data查找##包裹的数据,

r = res.group() # search返回,没有匹配到则为None;反之r是需要被替换的

key = res.group(1) # 提取#xxx#包裹的参数为key,key则作为咱们需要替换的数据

  • 根据上面给出的思路,整理封装一个通用的替换接口参数的方法
import re

mobile = "13800138000"
passwd = "123456"

data = '{"mobile":"#mobile#","passwd":"#passwd#"}'

class TestData:
    """这个类的作用:是用来保存需要替换的数据"""
    pass

setattr(TestData, "mobile", mobile)
setattr(TestData, "passwd", passwd)

conf =None

def instead_param(param,pattern="#(.+?)#"):
    # 判断是否有需要替换的数据
    while re.search(pattern, param):
        # 匹配出第一个要替换的数据
        res = re.search(pattern, param)
        # 提取待替换的内容
        item = res.group()
        # 获取替换内容中的数据项
        key = res.group(1)
        try:
            # 优先去类属性种查找key
            param = param.replace(item, getattr(TestData, key))
        except:
            try:
                # 根据替换内容中的数据项去配置文件中找到对应的内容,进行替换
                param = param.replace(item, conf.get_value("info", key))
            except:
                break    # 有一个没找到就退出循环,反正是个错
    return param

print(instead_param(data)) # 输出:{"mobile":"13800138000","passwd":"123456"}
  • 需要解释一下TestData类是用来干什么的吗?作为一个存取数据的介质,并无实际意义。

解决替换的参数从哪里来?需要捕获异常,如果文件中没有;咱们需要分析参数去哪里获取,通常是指从上一个接口请求响应返回来的;

预先取之必先予之,在识别之后,记得将有关联的参数setattr设置为类属性,然后在替换的时候才能使用getattr获取类属性并完成替换。

  • 再将举例的Test类使用这个方法来进行参数关联(参数替换):关于TestData和instead_param方法引用上面的模块
class Test(unittest.TestCase):
    
    headers = {"content-type":"application/json"}
    
    
    def setUp(self):
        self.mobile = "138" + str(random.randint(10000000,99999999))
        
        setattr(TestData, "mobile", self.mobile)
        setattr(TestData, "code", "1234")

    
    def test_01_register(self):
        """新用户注册"""
        data = '{"mobile":"#mobile#", "code":"#code#"}'
        
        # 使用替换参数的方法
        param = instead_param(data)
        # 测试是否输出成功
        print(param)
        
        url = "http://xxxxx/api/register"
        # res = requests.post(url, json=data, headers=self.headers).json()
        #  示例先忽略断言
        res = {
          "code": 0,
          "codeDesc": "string",
          "detailDesc": "string",
          "result": True,
          "value": {
            "userId": 123
          }
        }
        setattr(TestData, "userId", str(res.get("value").get("userId")))
    
    
    def test_02_getUserInfo(self):
        """获取用户信息"""
        # 这里演示全局变量引用
        data = '{"user_id": "#userId#"}' # 假设数据是这样的
        # 引用这个替换参数的方法
        param = instead_param(data) 
        
        # 测试是否成功替换
        print(param)
        
        url = "http://xxxxx/api/get_userinfo"
        self.headers["Authorization"] = "**************************************" # 当然还有token权限认证相关
        # requests.get(url, json=data, headers=self.headers)
        
        
if __name__ == '__main__':
    unittest.main()

测试输出的参数,是否替换成功;

{"mobile":"13877046142", "code":"1234"} # 第一个测试方法输出

{"user_id": "123"} # 第二个测试方法输出

  • 注意:在setattr方法的时候,切记是str字符串类型,否则getattr会出现None,不会替换成功。

扩展

上面的案例使用了re模块及(setattr\getattr)类方法实现了关联参数替换的方法,这是对测试用例(字符串类型)的参数有效,如果是yaml格式的用例呢?这个参数替换的方法如何设计?提示一下:python递归函数。