一、背景:
import pytest
import os # 路径配置需要引入os 模块
import jsonfrom common.myConf import MyConf
from common.my_path import conf_dir
from common.my_requests import MyRequests
from common.my_excel import MyExcel
from common.my_assert import MyAssert
from common.mylogger import logger
from common.my_path import testdata_dir
from common.my_data import Data #引入data 模块
from common.my_extract import extract_data_from_response# 第一步:读取注册接口的测试数据 - 是个列表,列表中的每个成员,都是一个接口用例的数据。
excel_path = os.path.join(testdata_dir, "测试用例.xlsx")
print(excel_path)
me = MyExcel(excel_path, "充值接口")
cases = me.read_data()# 第二步:遍历测试数据,每一组数据,发起一个http的接口
# 实例化请求对象
mq = MyRequests()
massert = MyAssert()"""
# 把前置写在fixture里面
# @pytest.fixture(scope="class"):表示pyteat用法的前置条件,
# 放在类里面,这样做的好处是不需要重复登录
"""
@pytest.fixture(scope="class")
def prepare():
# 登陆
conf = MyConf(os.path.join(conf_dir, "data.ini")) # 准备的数据
user = conf.get("normal" ,"user")
passwd = conf.get("normal", "passwd")
login_url = "member/login"
data = {"mobile_phone" :user ,"pwd" :passwd} # 发起请求,前面已经引入了发起请求的封装模块
resp = mq.send_requests("post", login_url, data) # 拿token,id,leave_amount
# 因为我们并没有把提取封装,所以我们这里使用json 进行提取
# leave_amount :是充值要传的一个参数
resp_dict = resp.json()
member_id = resp_dict["data"]["id"]
token = resp_dict["data"]["token_info"]["token"]
leave_amount = resp_dict["data"]["leave_amount"] """
2、把前置条件当中的设置Data 属性,然后在测试用例当中更改它的值,
如果key 已经存在,则更新他的值,如果不存在,则添加一个值
"""
setattr(Data, "token", token)
#为了方便获取使用,把这里改成str
# setattr(Data, "member_id", member_id)
setattr(Data, "member_id", str(member_id))
# setattr(Data, "leave_amount", leave_amount)
setattr(Data, "leave_amount", str(leave_amount))
# yield token,member_id,leave_amount 这句话就可以不要了@pytest.mark.usefixtures("prepare")
class TestRecharge: # 设置前置条件:
@pytest.mark.parametrize("case", cases)
def test_recharge(self, case, prepare):
# 1、在写用例的时候,接收到上一个接口以下三个值:
# token, member_id, leave_amount = prepare #需要关联的字段在前置条件当中已经设置了从data当中获取,所以这里旧不需要再接收了,注释掉 # 2、下一接口的请求数据中,需要替换,替换为上一个接口中提取的数据。
if case["req_data"] and case["req_data"].find('#member_id#') != -1:
# 替换掉占位符 -
case["req_data"] = case["req_data"].replace('#member_id#', getattr(Data, "member_id")) if case["assert_list"] and case["assert_list"].find('#leave_amount#') != -1:
# 替换掉占位符 -
case["assert_list"] = case["assert_list"].replace('#leave_amount#', getattr(Data, "leave_amount")) # 3、把替换之后的请求数据(json格式的字符串),转换成一个字典
req_dict = json.loads(case["req_data"]) # 4、发起请求,并接收响应结果
if hasattr(Data, "token"):
resp = mq.send_requests(case["method"], case["url"], req_dict, token=getattr(Data, "token"))
else:
resp = mq.send_requests(case["method"], case["url"], req_dict)
logger.info(resp.json()) # 5、提取响应结果中的数据,并设置为全局变量
if case["extract"]:
# 调用提取处理函数
extract_data_from_response(case["extract"], resp.json()) # 结果空列表
assert_res = [] # 4、断言响应结果中的数据sert_response_value(case["assert_list"], resp.json())
# assert_res.append(response_check_res)
if case["assert_list"]:
response_check_res = massert.as # 5、断言数据库 - sql语句、结果与实际、比对的类型
if case["assert_db"]:
db_check_res = massert.assert_db(case["assert_db"])
assert_res.append(db_check_res) # 最终的抛AsserttionError
if False in assert_res:
raise AssertionError #从断言响应成功的结果当中,提取leave_amount 的值,并更新全局变量当中的eave_amount 的值
setattr(Data, "leave_amount", str(resp.json()["Data"]["leave_amount"]))
上次的一个充值接口用例当中,我自己又深入思考了以下问题,进行了优化如下:
第一:把前置条件写在测试用例里面
第二:从响应结果当中,提取的某字段(leave_amount)值,并更新在全局变量当中,也是写在测试用例当中
第三:关于字段需要替换的地方
首先,我们先来看前面两个问题:
第一个问题:把前置条件写在测试用例里面,如果接下来新增项目,各种不同的接口所用的前置条件不一样
那么,你是不是得在测试用例当中写很多很多的前置条件?那以后做业务流的时候怎么办呢?
第二个问题:从响应结果当中,提取leave_amount的值,并更新到全局变量当中,但是我们思考一下,
比如上一个接口要是处理失败了,那么我们这一步是不是没必要去做?是的,所以如果上一个接口处理失败了,我们
这里还得增加一些判断的代码进行判断处理。
——把前置接口全部写在Excel 当中
在该用例前面增加一个我们需要用到上一个接口用例,在extract 用提取表达式写上下一个接口需要使用到的参数,
我们在写测试用例的时候,需不需要提取,只要判断extract 列有没有提取表达式,如果有,把jsonpath 的值(比如:$..token),按照
从响应结果当中提取出来重新赋值给它,再把它设置为data全局变量,后面的测试用例当中需要用到这些值直接反问即可。
这样我们就不需要在前置条件当中写一堆的代码了,
也就是接口依赖之间的处理方法处理:
第一步:提取值:
比如:我们的充值接口,在充值之前需要登录,我们直接在Excel 表中增加一行登录的前置接口
需要注意的地方:
如果你提取的member_id 作为全局变量,那么你在后面使用member_id 作为全局变量的时候,一定要前后保持一致,不然找不到,会报错
还有就是member_id 接口本身是字符串类型,就要加引号,如果不是,就不需要加引号。
我们的登录user 和passwd 也可以直接写在 data的全局变量当中
接下来,我们还需要对extract 列进行封装处理
——定义一个公共的提取方法:my_extract.py
解析excel 当中extract l列,然后从响应结果中提取,然后设置Data类属性
"""
从响应结果当中,提取值,并设置为全局变量(Data类作为本框架的全局变量类)
1、提取表达式:放在excel当中
(可能提取1个,可能提取多个。。以表达式个数为准,有多少个取多少个)
2、提取出来之后,设置为Data类属性
"""
import jsonpath
from common.my_data import Data
def extract_data_from_response(extract_epr, response_dict):
"""
从响应结果当中提取值,并设置为Data类的属性。
:param extract_epr: excel当中extract列中的提取表达式。是一个字典形式的字符串。
key为全局变量名。value为jsonpath提取表达式。
'{"token":"$..token","member_id":"$..id","leave_amount":"$..leave_amount"}'
:param response: http请求之后的响应结果。要求是字典类型。,如果是字符串类型需要转换
:return:None
"""
# 1、从excel中读取的提取表达式,转成字典对象
extract_dict = eval(extract_epr)
# 2、遍历1中字典的key,value.key是全局变量名,value是jsonpath表达式。
for key,value in extract_dict.items():
# 根据jsonpath从响应结果当中,提取真正的值。value就是jsonpath表达式
result = jsonpath.jsonpath(response_dict, value)
# jsonpath找了就是列表,找不到返回False
# 如果提取到了真正的值,那么将它设置为Data类的属性。key是全局变量名,result[0]就是提取后的值
if result:
#str(result[0]:提取之后统一转换成字符串
setattr(Data, key, str(result[0]))
总结:
处理接口关联:
第一步:提取值
通过jsonpath从响应结果中提取,然后设置为全局变量。我的框架中Data类用来存储全局变量的。
如何通过jsonpath去提取的呢?可能会提取1个值?可能会提取多个值?框架通用性,为了达到所有接口通用。
1)在excel当中添加了一列:extract。如果当前这一行的请求,有需求要从响应中提取。
那么就在extract列对应的位置,写上表达式。
形式是字典形式,key-value.key就是变量名,value就jsonpath提取表达式。
2)定义了一公共的提取方法:
解析excel当中extract列,然后从响应结果中提取,然后设置为Data类的属性。
3)在测试框架的接口自动化用例当中,通过判断extract列有没有值,来自动提取。
第二步:替换值
————下一次分享
那么,我们判断extract 当中不为null,就需要提取,那么我们的提取表达式写在哪里?
——》写在发起请求之后
把提取的表达式写在发起请求之后,这里面的全局变量提取就不需要了,直接去掉
到这里,我们只是完成了一个提取的动作,还有一个替换的动作,也就是第三个问题:
第三个问题:关于字段替换的地方,当我们接口用例很多很多,上千条时候,一个一个的替换,你能确保你每个字段都替换了吗?
而且上百上千个接口一个一个替换也会显得比较麻烦。
————下一次分享
————如何更加做灵活替换
总结优化后的测试用例代码示例:
import pytest
import os
import json
from common.myConf import MyConf
from common.my_path import conf_dir
from common.my_requests import MyRequests
from common.my_excel import MyExcel
from common.my_assert import MyAssert
from common.mylogger import logger
from common.my_path import testdata_dir
from common.my_data import Data #解决day9问题
from common.my_extract import extract_data_from_response
# 第一步:读取注册接口的测试数据 - 是个列表,列表中的每个成员,都是一个接口用例的数据。
excel_path = os.path.join(testdata_dir, "测试用例.xlsx")
print(excel_path)
me = MyExcel(excel_path, "充值接口")
cases = me.read_data()
# 第二步:遍历测试数据,每一组数据,发起一个http的接`口
# 实例化请求对象
mq = MyRequests()
massert = MyAssert()
class TestRecharge:
@pytest.mark.parametrize("case", cases)
def test_recharge(self, case):
# 2、下一接口的请求数据中,需要替换,替换为上一个接口中提取的数据。
if case["req_data"] and case["req_data"].find('#member_id#') != -1:
# 替换掉占位符 -
case["req_data"] = case["req_data"].replace('#member_id#', getattr(Data, "member_id"))
if case["assert_list"] and case["assert_list"].find('#leave_amount#') != -1:
# 替换掉占位符 -
case["assert_list"] = case["assert_list"].replace('#leave_amount#', getattr(Data, "leave_amount"))
# 3、把替换之后的请求数据(json格式的字符串),转换成一个字典
req_dict = json.loads(case["req_data"])
# 4、发起请求,并接收响应结果
#要对token进行做判断,什么情况下需要传token,什么情况下不需要传
if hasattr(Data, "token"):
resp = mq.send_requests(case["method"], case["url"], req_dict, token=getattr(Data, "token"))
else:
resp = mq.send_requests(case["method"], case["url"], req_dict)
logger.info(resp.json())
# 5、提取响应结果中的数据,并设置为全局变量
if case["extract"]:
# 调用提取处理函数
extract_data_from_response(case["extract"], resp.json())
# 结果空列表
assert_res = []
# 4、断言响应结果中的数据
if case["assert_list"]:
response_check_res = massert.assert_response_value(case["assert_list"], resp.json())
assert_res.append(response_check_res)
# 5、断言数据库 - sql语句、结果与实际、比对的类型
if case["assert_db"]:
db_check_res = massert.assert_db(case["assert_db"])
assert_res.append(db_check_res)
# 最终的抛AsserttionError
if False in assert_res:
raise AssertionError