测试用例框架优化(一)

一、背景:


import pytest
import os # 路径配置需要引入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 #引入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

 

 
posted @ 2022-01-11 09:58  乘风破浪的小落夜  阅读(379)  评论(0编辑  收藏  举报