案例5 基于Excel的接口测试框架
简单版
读取以下格式excel(仅第一张Sheet), 逐个发送接口,断言接口返回200, 并将状态及错误信息写回Excel
已知:Excel中接口编写格式规范如下
- url如果有查询参数,要写到url中,例如?a=1&b=2
- 如果需要添加自定义请求头 按 key:value格式编写,:左右允许有空格,每行一个
- 请求数据支持表单和JSON格式,如果数据形似JSON格式 (如去除前后空格或空行后以{或[开头),按JSON格式发送数据
否则按表单格式发送数据,表单变量和请求头变量编写格式一致,已:分开,左右允许有空格,如果值以<>包裹,则视为一个文件路径,按multipart/formdata格式发送文件
注意:不使用其他测试框架,自己写循环和异常处理,如果断言状态码为200失败则视为FAIL,其他出错认为 ERROR, 结果根据状态单元格填充不同的颜色
难度版:
增加变量提取和断言
已知:
- 在每个接口运行后,生成一个系统变量response (requests库的响应对象),并存入一个字典格式的上下文变量中
- 允许用户使用$上下文中的变量名 (如 $response)来使用变量,并提取生成新的变量,放入上下文字典中
- 允许用户使用标准 Python表达式来进行断言 ,断言中支持上下文变量引用 如
$respoonse.status_code == 200
- 断言中也可以使用前面提取的变量,下面的接口url, 请求数据,headers,数据提取,断言中也支持引用上面生成的变量
思路
数据读取及发请求
- 读取excel得到所有接口数据列表(跳过标题行或,和标题行组成字典格式)
- 按requests.request()方法的参数要求清洗组装数据 得到:
{"method": "GET", "url": "https://....", "headers": {}, "data": {}, "json": {}, "files": {}}
- 其中headers要求是一个字典,可以没有或为None, 例如
{"flag": "test", "token": "JWT abcd"}
- data是所有非文件的表单数据,字典格式 例如
{"name": "kevin", "password": "test"}
- json从excel单元格中读出来是字符串,要用
json.loads()
转为字典 (或列表),可能有异常(用户填写的JSON格式不合规),如果数据解析出错,该用例状态为ERROR
- 遍历清理后的数据 列表,for循环发请求,断言响应状态码为200, 并做异常捕获(发请求 和 断言一起捕获),分别捕获断言异常和其他异常,断言异常时状态为FAIL, 并记录异常信息,其他异常时状态为ERROR并记录异常信息
- 将状态回写到原Excel,添加单元格颜色,并保存(也可以生成新Excel, 不破坏原格式)
断言及数据提取
- Excel单元格中读出来是一个多行字符串,例如:
excepted = '''$response.json()['form']['name']=='admin'
$response.json()['form']['password']=='test'
$response.json()['form']['password']==$flag'''
- 按换行分割可以得到每一个断言 描述 (字符串格式)
assert_list = excpeted.split('\n')
另外我们需要把每个语句前的\(符号去掉 , 注意:假设咱们的断言描述,暂时只允许`\)变量` 开头
assert_list = [item.lstrip('$') for item in assert_list] # 推导式不理解的可以问我
- 请求后得到response变量
response = requests.request(**req)
其中**req
是字典解包, req是request方法的参数数据, 例如 {"method" : ... , "url": ..., "headers": ...}
- python使用
exec()
可以运行字符串,使用eval()
可以对字符串进行表达式求值
比如,字符串形式的断言描述 "response.json()['form']['name']=='admin'"
我们可以用
eval("response.json()['form']['name']=='admin'")
来用Python来计算,相等返回True, 不相等返回False, 加上assert关键词就变成了断言
assert eval("response.json()['form']['name']=='admin'")
- 对于断言后面的
$变量
,如$flag
, 假设暂时只支持引用基本类型变量(数字、字符串、列表、字典等)
用一个字典类型的变量来保存可使用\(引用的变量,如`context = {}` 变量提取时 `"flag=\)response.headers['flag']"先按等号拆分成
key = "flag"和
value = "\(response.headers['flag']" ·, 去掉value前的`\)并用
eval()`求值得到结果,并放入context中
context["flag"] = eval(response.headers['flag']")
处理 断言语句中的 变量引用"response.json()['form']['password']=='$flag'"
可以先用python string库的Template中的变量替换,将$变量替换成对应的值
from string import Template
rendered_text = Template("response.json()['form']['password']=='$flag'").safe_substitute({"flag": "test")
就可以吧字符串变成了response.json()['form']['password']=='test'
然后用eval求值,加assert断言即可
assert eval(response.json()['form']['password']=='test')