「这是我参与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递归函数。