1号初级py自动化笔记之路

(根据自己学习过程一步步总结,我是小菜鸟)

1.第一条requests请求

### 1.1浅试一个post请求
url = "http://api.xxxx.com/futureloan/member/login"
 请求类型:post
请求体
req_data = {
    "mobile_phone": "13524050000",
    "pwd": "123456789"
}
resp = requests.post(url, json=req_data,headers=headers)
print("登陆的响应结果: \n", resp.text)

print:

 {"code":0,"msg":"OK","data":{"id":183953,"leave_amount":1000.0,"mobile_phone":"13524050000","reg_name":"小咸鱼","reg_time":"2024-03-14 10:38:02.0","type":1,"token_info":{"token_type":"Bearer","expires_in":"2024-03-14 10:46:58","token":"eyJhbGciOiJIUzUxMiJ9.eyJtZW1iZXJfaWQiOjE4Mzk1MywiZXhwIjoxNzEwMzg3Nzg2fQ.9i3HO_chjQzKt11eAwcxUvdmKzaDPF5koL4_zRL7eP19tE4gjmeEXLgNfwpHtcfFpNLXoChBOOFBeHuNs1QtA"}},"copyright":"dd技术有限公司"}

# 1.2 常用调用接口的方法是提取出来返回数据,作为下一个接口的参数去请求

接着1.1的返回做提取

json_res = resp.json() 
token = json_res["data"]["token_info"]["token"]
member_id = json_res["data"]["id"] 
print('提取的token是: ',json_res["data"]["token_info"]["token"]) 
print('提取的手机号id是: ',json_res["data"]["id"])

print:

提取的token是:    eyJhbGciOiJIUzUxMiJ9.eyJtZW1iZXJfaWQiOjE4Mzk1MywiZXhwIjoxNzEwMzg3Nzg2fQ.9i3-HO_chjQzKt11eAwcxUvdmKzaDPF5koL4_zRL7eP19tE4gjmeEXLgNfwpHtcfFpNLXoChBOOFBeHuNs1QtA
提取的手机号id:   183953
1.3提问

1)如果请求50个请求,是不是可以把相同部分封装一起调用?

2)有的接口需要token,有的不需要怎么处理?(实际工作中只有登录接口不需要token,其余接口都需要从登录接口获得的token作为请求的入参)

2.浅封装请求

2.1 把发送请求封装类,并把请求头的变量token字段封装,默认=None

class MyRequests:
    # 初始化方法
    def __init__(self):
        # 请求头
        self.headers = {"X-xxx-Media-Type": "xxx.v2"}
    # 属性
    # 方法 post/put.. json=XXX , get..  params=XXX
    def send_requests(self,method,url, data,token=None):
        # 处理请求头
        self.__deal_header(token)
        # 调用requests的方法去发起一个请求。并得到响应结果
        if method.upper() == "GET":
            resp = requests.request(method, url, params=data, headers=self.headers)
        else:
            resp = requests.request(method, url, json=data, headers=self.headers)
        return resp
    def __deal_header(self,token=None):
        if token:
            self.headers["Authorization"] = "Bearer {}".format(token)

2.2 调用下该封装类

mr = MyRequests()
    # url地址
    url = "http://api.xxxcom/futureloan/member/login"
    # 请求类型:post
    method = "post"
    # 请求体
    req_data = {
        "mobile_phone": "13524050000",
        "pwd": "123456789"}
    resp = mr.send_requests(method,url,  req_data)
    print(resp.json())

print:

{'code': 0, 'msg': 'OK', 'data': {'id': 183953, 'leave_amount': 1000.0, 'mobile_phone': '13524050000', 'reg_name': 'py37小咸鱼', 'reg_time': '2024-03-14 10:38:02.0', 'type': 1, 'token_info': {'token_type': 'Bearer', 'expires_in': '2024-03-14 14:18:02', 'token': 'eyJhbGciOiJIUzUxMiJ9.eyJtZW1iZXJfaWQiOjE4Mzk1MywiZXhwIjoxNzEwMzk3MDgyfQ.1PQxQSOoE_GnzcjT4dUN3elB5XmdqzRRMVsDJ2Tlcr0tfkRxO5MSrDEEMZKLLSwpHBZKs_uIRfuwukNhg7ZUjA'}}, 'copyright': 'Copyright嘻嘻信息技术有限公司'}
2.3 提问

请求清爽了,感觉参数比较多,没有规范,把全部参数整合一起?

3.浅整合入参

3.1 列表是测试用例集合,一个字典就是一条用例

datas = [
    {"method": "post" ,
         "url": "http://api.xxx.com/futureloan/member/register",
     "req_data": {"mobile_phone": "13524050000","pwd": "123456789","reg_name": "py小咸鱼"}},
    {"method": "post",
     "url": "http://api.xxx/futureloan/member/login",
     "req_data":{"mobile_phone": "13524050000","pwd": "123456789"}},
    {"method": "post",
     "url": "http://api.xxx.com/futureloan/member/recharge",
     "req_data": {"member_id": None,"amount": 1000}}
]

执行下:

mr = MyRequests()
# 一组就是一条用例
#某一组即便运行失败了,下一组仍然会运行。
@pytest.mark.parametrize("item", datas)
def test_api1(item):
    resp = mr.send_requests(item["method"], item["url"], item["req_data"])
    print(resp.json())

print:
在这里插入图片描述
总结:通过封装调用方法,封装入参,使用驱动传参,全都封装到列表里,一个字典就是一组数据就是一条用例,有几个用例就执行几遍。用字典不同的key调用不同的值,即使失败了也不会影响其他用例,执行代码也少了。

补充:涉及pytest装饰器 @pytest.mark.parametrize 使用方法可以单独研究下

3.2 把测试用例写在excel,读取

#用例地址
excel_path=r"D:\EasyProject\day5\testdatas\测试用例.xlsx"
#打开一个工作簿
work_book=load_workbook(excel_path)
print(work_book.sheetnames)
#打开一个sheet页
work_sheet=work_book["注册接口"]
3.2.1 方法1
datas=[]
cases=[]
#values_only=True默认返回单元格的值,false返回cell对象(默认)
for row in work_sheet.iter_rows(min_row=1,values_only=True):
    #每一行作为一个值存入datas列表
    datas.append(row)
print(datas)
#datas列表中第二行数据行开始遍历,并且与第一行表头组合成想要的字典组合。
for i in datas[1:]:
    cases.append(dict(zip(datas[0],i)))
print(cases)
3.2.2 方法2
cases=[]
#获取页面所有行的值
excel_datas = list(work_sheet.iter_rows(values_only=True))
#第一行作为key保存到key列表
keys_list=excel_datas[0]
#除非第一行的全部行保存到value列表
values_list=excel_datas[1:]
#循环value列表,取到每一行时都与第一行key列组合成一个字典格式的新数据
for i in values_list:
    case=dict(zip(keys_list,i))
    cases.append(case)
print(cases)
3.2.2 方法3
#得到当前sheet的总行号,总列号
row_nums = sh.max_row
col_nums = sh.max_column
data = list(sh.values)
print(data)
keys = data[0] # 获取所有的列名
all_data = []
for row in data[1:]:
    row_dict = dict(zip(keys,row))
    all_data.append(row_dict)
3.2.2 方法4
# 得到当前sheet的总行号,总列号
row_nums = sh.max_row
col_nums = sh.max_column

# 只读取第一行(作为key)
# 行号是1  通过代码自动得到列号
keys = []
for col_index in range(1, sh.max_column + 1):
    keys.append(sh.cell(1, col_index).value)
print(keys)

# 遍历行号,取第一行
for row_index in range(2, sh.max_row + 1):
    values = []
    # 在每一行里面,从第1列开始,获取所有列的值
    for col_index in range(1, sh.max_column + 1):
        values.append(sh.cell(row_index, col_index).value)
    # keys和values打包 - zip函数
    case = dict(zip(keys, values))
    print(case)
3.2.2 方法5

这个方法超级简单, 不需要像前4种方法,直接执行.excel_to_dict()方法即可。

from xToolkit import xfile
def read_data():
    res=xfile.read("测试用例1229.xlsx").excel_to_dict("信息披露")
    return res

if __name__ == '__main__':
    # excel的文件路径
    excel_path = r"D:\\testDatas\测试用例1229.xlsx"
    rr=read_data()
    print(rr)

4.封装断言、exel读取、logs 、path

我感觉写了几个接口后,发现确实很杂乱,试想如果在加20个接口100个用例呢?
屡下一个接口的实现流程,发起请求,读取用例,执行用例,对比结果断言,上下游接口间参数传递和使用,发送报告,无人值守定时运行。
发现在每个接口每个用例执行时,这些都是必做流程,所以现阶段现学习时先封装这几项。

4.1 封装断言

class MyAssert:
    def assert_response_value(self,check_str, response_dict):
        """
        :param check_str: 从excel当中,读取出来的断言列。是一个列表形式的字符串。里面的成员是一个断言
        :param response_dict: 接口请求之后的响应数据,是字典类型。
        :return: None
        """
        # 所有断言的比对结果列表
        check_res = []
        # 把字符串转换成python列表
        check_list = ast.literal_eval(check_str)  # 比eval安全一点。转成列表。

        for check in check_list:
            # 通过jsonpath表达式,从响应结果当中拿到了实际结果
            actual = jsonpath.jsonpath(response_dict, check["expr"])
            if isinstance(actual, list):
                actual = actual[0]

            # 与实际结果做比对,check["type"]还有其他类型再接着判断即可
            if check["type"] == "==":
                check_res.append(actual == check["expected"])

        if False in check_res:
            logger.error("部分断言失败!,请查看比对结果为False的")
            raise AssertionError
        else:
            logger.info("所有断言成功!")

4.2 封装excel

from openpyxl import load_workbook

class MyExcel:

    def __init__(self, excel_path, sheet_name):
        # 1、加载一个excel,得到工作薄 Workbook
        wb = load_workbook(excel_path)
        # 2、选择一个表单- 通过表单名 Sheet
        self.sh = wb[sheet_name]

    def read_data(self):
	    datas=[]
		cases=[]
		#values_only=True默认返回单元格的值,false返回cell对象(默认)
		for row in work_sheet.iter_rows(min_row=1,values_only=True):
	    #每一行作为一个值存入datas列表
	    datas.append(row)
		print(datas)
		#datas列表中第二行数据行开始遍历,并且与第一行表头组合成想要的字典组合。
		for i in datas[1:]:
	   	 	cases.append(dict(zip(datas[0],i)))
	    return cases

4.3 封装excel

class MyLogger(Logger):

    def __init__(self):
        # conf = MyConf("conf.ini")
        # file = conf.get("log", "file")
        file = "api.log"
        # 1、设置日志的名字、日志的收集级别
        # super().__init__(conf.get("log","name"), conf.get("log","level"))
        super().__init__("xxxapi", logging.INFO)

        # 2、可以将日志输出到文件和控制台

        # 自定义日志格式(Formatter)
        fmt_str = "%(asctime)s %(name)s %(levelname)s %(filename)s [%(lineno)d] %(message)s"
        # 实例化一个日志格式类
        formatter = logging.Formatter(fmt_str)

        # 实例化渠道(Handle).
        # 控制台(StreamHandle)
        handle1 = logging.StreamHandler()
        # 设置渠道当中的日志显示格式
        handle1.setFormatter(formatter)
        # 将渠道与日志收集器绑定起来
        self.addHandler(handle1)

        if file:
            # 文件渠道(FileHandle)
            handle2 = logging.FileHandler(file, encoding="utf-8")
            # 设置渠道当中的日志显示格式
            handle2.setFormatter(formatter)
            self.addHandler(handle2)


logger = MyLogger()

4.4 封装excel

path很好的解决路径读错,不适配等问题

import os

# 1、basedir
basedir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
print(basedir)
# 拼到配置文件路径
conf_dir = os.path.join(basedir, "Conf")
print(conf_dir)
# 拼接  测试数据路径
testdata_dir = os.path.join(basedir, "testdatas")
print(testdata_dir)
打印:
E:\sunqq_python\git_test\day7
E:\sunqq_python\git_test\day7\Conf
E:\sunqq_python\git_test\day7\testdatas

5. 说说封装断言的感想

此时此刻此能力的我,在学习到与数据库的值做断言时,一下子想通了一些串联的场景。从用例设置,封装断言,调用断言。
发现每一步骤都是关联,有小心机的。

  • 5.1用例内容

在用例里加入断言数据库列,要求规则1、必须是列表形式;2、列表中的成员,必须是字典形式;3、字典必须有3个key:sql, expected, type_db;4、type_db表示查询数据库时,是获取结果条数(type为count),要是获取数据。
在这里插入图片描述

  • 5.2封装断言

接上面4.1里断言类My_assert里新增assert_db()方法

 def assert_db(self,check_db_str):
        """
        1、将check_db_str转成python对象(列表),通过eval
        2、遍历1中的列表,访问每一组db比对
        3、对于每一组来讲,1)调用数据库类,执行sql语句。调哪个方法,根据type来决定。得到实际结果
                       2)与期望结果比对
        :param check_db_str: 测试数据excel当中,assert_db列读取出来的数据库检验字符串。
              示例:[{"sql":"select id from member where mobile_phone='#phone#'","expected":1,"type":"count"}]
        :return:
        """
        # 所有断言的比对结果列表
        check_db_res = []
        # 把字符串转换成python列表
        check_db_list = ast.literal_eval(check_db_str)  # 比eval安全一点。转成列表。
        # 建立数据库连接
        db = MyMysql()
        # 遍历check_db_list
        for check_db_dict in check_db_list:
            # 根据type来调用不同的方法来执行sql语句。
            if check_db_dict["db_type"] == "count":
                # 执行sql语句
                res = db.get_count(check_db_dict["sql"])
                # 将比对结果添加到结果列表当中
                check_db_res.append(res == check_db_dict["expected"])

        if False in check_db_res:
            return False
        else:
            logger.info("所有断言成功!")
            return True
  • 5.3调用使用
# 结果空列表
assert_res = []
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

总结:用例设计里的[{“sql”:“select id from member where mobile_phone=‘#phone#’”,“expected”:1,“db_type”:“count”}
],字典[“sql”]的值其实就说实际结果,只是需要在运行后实现结果,所以这里写的是得到实际结果的手段。
字典[“db_type”]的值实际是看断言类型,然后可以在断言类My_assert类里调用目标方法即可。
做好5.1、5.2后,实际在5.3中只需要一句代码 db_check_res = massert.assert_db(case[“assert_db”])就可以实现。
今天先到这吧。

补充一点:对数据库做断言时,sql语句一定要写在一行然后复制,不然复制到excel中,再执行后是有空格的,我就踩了这个坑,日志看怎么都断言失败,找不到原因,原来是sql语句的问题。

原sql语句在这里插入图片描述
把sql语句复制到用例里,也没啥问题。
在这里插入图片描述
执行时,报错了
在这里插入图片描述

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值