正则的使用
1、匹配1个字符
. 除换行符以外的所有字符 \n
\d 只匹配数字0-9
\D 匹配非数字
\w 匹配包括下划线的任何单词字符。等价于“[A-Za-z0-9_]”, 支持中文
\W 匹配任何非单词字符。等价于“[^A-Za-z0-9_]
ss1 = '{"mobile_phone": "#user#","pwd": "#password#"}'
res = re.findall("#*", ss1) # * 匹配前一个字符,0次或者多次 0次等于空字符也会显示,不方便阅览。
print(res)
# 结果为:['', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '#', '', '', '', '', '#', '', '', '', '', '', '', '', '', '', '', '#', '', '', '', '', '', '', '', '', '#', '', '', '']
===============================================================
ss1 = '{"mobile_phone": "#user#","pwd": "#password#"}'
res = re.findall("#+", ss1) # + 匹配前一个字符,1次或者多次
print(res)
# 结果为:['#', '#', '#', '#']
================================================================
ss1 = '{"mobile_phone": "#user#","pwd": "#password#"}'
res = re.findall("#\w+#", ss1) #案例: # 号开头,\w中间有字符,+ 号:字符不限数量, #号结尾
print(res)
# 结果为:['#user#', '#password#']
===============================================================
# 贪婪模式: 尽可能的匹配更多更长 对人民币贪婪,越多越好。
ss1 = '{"mobile_phone": "#user#","pwd": "#password#"}'
res = re.findall("#.+#", ss1)
print(res)
============================================================
# 非贪婪模式: 尽可能的匹配更少 在数量表达后面加上? 对无偿加班时间,越少越好。
ss1 = '{"mobile_phone": "#user#","pwd": "#password#"}'
res = re.findall("#.+?#", ss1) # ? 匹配前一个字符,0次或1次
print(res)
==============================================================
# 非贪婪模式: 尽可能的匹配更少 在数量表达后面加上? 对无偿加班时间,越少越好。
ss1 = '{"mobile_phone": "#user#","pwd": "#password#"}'
res = re.findall("#(.+?)#", ss1) # ? 匹配前一个字符,0次或1次
print(res)
================================================================
"""
{n} 匹配前一个字符n次
{n,m} 匹配前一个字符最少是n次,最多是m次
{n,} 匹配前一个字符最少是n次,没有下限。
"""
ss1 = '{"mobile_phone": "#user#","pwd": "#password#"}'
res = re.findall("#\w{2,}?#", ss1)
print(res)
===================================================================
import re
from common.my_data import Data
case = {
"url" : 'member/login',
"member" : 'post',
"req_data" : '{"mobile_phone": "#user#","pwd": "#password#"}',
"extract" : '{"token":"$..token","member_id":"$..id","leave_amount":"$..leave_amount"}'
}
# 第一步:把excel当中的一整个测试用例(excel当中的一行)转换成字符串
case_str = str(case)
print(case_str)
# 第二步:利用正则表达式提取 mark 标识符
res = re.findall("#(.+?)#", case_str)
print(res)
# 第三步:遍历标识符 mark,如果标识符是全局变量 Data 类的属性名,则用属性值替换掉 mark。
if res:
for mark in res:
# 如果全局变量Data类有mark这个属性名
if hasattr(Data, mark): # 如果 是否有(Data, mark)这个属性:
# 使用全局变量Data类的mark属性值,去替换测试用例当中的#mark#
case_str = case_str.replace("#{mark}#", getattr(Data,mark)) # case_str = case_str.替换(f"{#mark#}", 获取(Data, mark))
==========================================================================
封装以 re 正则表达式替换与手机脚本替换
import re
from common.my_data import Data
from common.py_log import LoggerHandler
from common.handele_phonne import get_new_phone
logger = LoggerHandler()
"""
在编写测试用例时,用例涉及到的所有mark标识符(#。。。#)都能够替换成功
1、来自响应结果当中提取
2、脚本生成的
3、配置文件。。。等等 ———— 还有很多
请问在框架当中,Data 类主要用在哪些地方?
1、封装的提取方法(从响应结果中提取数据放到 Data 中)
2、封装的替换方法(从 Data 当中拿数据)
"""
def replace_case_with_re(case_dict):
"""
替换测试用例当中所有的标识符,通过正则表达式获取所有的 mark,然后遍历 mark 一个个替换。
替换的值呢,来自于:
1、如果是 #phone#,则来自于脚本生成。表示要一个未注册的手机号码。
2、其他的 mark,均从 Data 类的属性中获取。
:param case_dict: 从 Excel 当中读取出来的一行测试数据。为字典形式。
:return: 替换之后的测试数据。类型为字典。
"""
# 第一步:把excel当中的一整个测试用例(excel当中的一行)转换成字符串
case_str = str(case_dict)
# 第二步:利用正则表达式提取 mark 标识符
to_be_replaced_marks_list = re.findall("#(.+?)#", case_str)
# 第三步:遍历标识符 mark,如果标识符是全局变量 Data 类的属性名,则用属性值替换掉 mark。
if to_be_replaced_marks_list:
logger.info("要替换的mark标识符有:{}".format(to_be_replaced_marks_list))
# 判断是否有 #phone# 这个标识符,如果有,调用手机生成手机号码的脚本,然后替换。
if "phone" in to_be_replaced_marks_list:
new_phone = get_new_phone()
logger.info("有#phone#标识符,需要生成新的手机号码:{}".format(new_phone))
case_str = case_str.replace(f"#phone#", new_phone)
# 从 Data 类当中取值来替换标识符。
for mark in to_be_replaced_marks_list:
# 如果全局变量Data类有mark这个属性名
if hasattr(Data, mark):
logger.info("将标识符 {} 替换为 {}".format(mark, getattr(Data, mark)))
# 使用全局变量Data类的mark属性值,去替换测试用例当中的#mark#
case_str = case_str.replace(f"#{mark}#", getattr(Data, mark))
logger.info("替换之后的用例数据为:\n{}".format(case_str))
# 第四步: 将完全替换后的一整个测试用例,转换回字典
new_case_dict = eval(case_str)
return new_case_dict
============================================================================
import json
import os
import pytest
from common.my_data import Data
from common.py_log import LoggerHandler
from common.my_path import conf_dir
from common.my_conf import MyConf
from common.my_requests import MyRequests
from common.My_excel import MyExcel
from common.my_assert import MyAssert
from common.my_path import testdata_dir
from common.my_replace import replace_case_with_re
from common.my_extract import extract_data_from_response
logger = LoggerHandler()
"""
前置:登陆成功(意味着要鉴权)
步骤:充值
断言:金额对不对
后置:释放资源/清理数据
1、类级别地的前置 -- 所有的充值用例,只需要登陆一次就够了。
登陆帐号:
1、用固定的一个帐号 - 配置化(Conf目录下,data.ini里配置用户)
2、已配置的帐号,如何保证它是已经存在的??
用之前,查一下数据库,如果没有,就注册(session前置)。
2、接口关联处理 -- 登陆接口的返回值,要提取出来,然后作为充值接口的请求参数
准备知识 :re正则表达式、 postman是如何处理参数传递(接口关联的)。
"""
# 第一步:读取注册接口的测试数据 - 是个列表,列表中的每个成员,都是一个接口用例的数据。
excel_path = os.path.join(testdata_dir, "注册接口用例.xlsx")
print(excel_path)
me = MyExcel(excel_path, "充值")
cases = me.read_data()
# 第二步:遍历测试数据,每一组数据,发起一个http的接口
# 实例化请求对象
mq = MyRequests()
myassert = MyAssert()
class TestRecharge:
@pytest.mark.parametrize("case", cases)
def test_recharge(self,case):
# 1、下一个接口的请求数据中,需要替换,替换为上一个接口中提取的数据。
case = replace_case_with_re(case)
# 2、把替换之后的请求数据(json格式的字符串),转换成一个字典
req_dict = json.loads(case["req_data"])
# 3、发起请求,并接收响应结果
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())
# 结果空列表
assert_res = []
# 5、断言响应结果中的数据
if case["assert_list"]:
response_check_res = myassert.assert_response_value(case["assert_list"], resp.json())
assert_res.append(response_check_res)
if False in assert_res:
pass
else:
# 4、提取响应结果中的数据,并设置为全局变量
if case["extract"]:
# 调用提取处理函数
extract_data_from_response(case["extract"], resp.json())
# 6、断言数据库 - sql语句、结果与实际、比对的类型
if case["assert_db"]:
db_check_res = myassert.assert_db(case["assert_db"])
assert_res.append(db_check_res)
# 最终的抛:AssertionError
if False in assert_res:
raise AssertionError
# # 从响应结果当中,提取 leave_amount 的值,并更新全局变量当中,leave_amount 的值。
# setattr(Data, "leave_amount", str(resp.json()["data"]["leave_amount"]))
============================================================================
在数据库断言封装当中,添加了,对于sql查询数据的比对。
db_type=eq
执行sql语句得到了字典数据
对字典数据进行了比较(在excel的数据库断言当中,expected也换成了字典形式)
处理了Decimal类型的数据
====================================================
案例:
assert_db |
---|
[{“sql”:“select member.leave_amount from member where id=#member_id#;”,“expected”:{“leave_amount”:#leave_amount#+2000},“db_type”:“eql”}] |
import ast
from decimal import Decimal
import jsonpath
from common.my_mysql import My_mysql
from common.py_log import LoggerHandler
logger = LoggerHandler()
class MyAssert:
def assert_response_value(self, check_srt, response_dict):
"""
:param check_srt: 从excel当中,读取出来的断言列。是一个列表形式的字符串。里面的成员是一个断言
:param response_dict: 接口请求之后的响应数据,是字典类型。
:return: None
"""
# 所有断言的比对结果列表
check_res = []
# check_list = ast.literal_eval(check_srt) # 比eval安全一点。转成列表。
check_list = eval(check_srt)
for check_index in check_list:
logger.info(f"断言的字段:{check_index}")
# 通过jsonpath表达式,从响应结果当中拿到实际结果
actual = jsonpath.jsonpath(response_dict, check_index["expr"])
if isinstance(actual,list):
actual = actual[0]
logger.info(f"从相应结果当中,提取到的值:\n{actual}")
logger.info("期望结果:\n{}".format(check_index["expected"]))
# 与实际结果做比对
if check_index["type"] == "eql":
logger.info("比对两个值是否相等。")
logger.info("比对结果:\n{}".format(actual == check_index["expected"]))
check_res.append(actual == check_index["expected"])
elif check_index["type"] == "gt":
logger.info("比对2个值的大小。")
logger.info("比对结果为:\n{}".format(actual > check_index["expected"]))
if False in check_res:
logger.error("断言失败!请查看结果为:False")
# raise AssertionError
return False
else:
logger.info("所有断言成功!")
return True
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安全一点。转成列表。
check_db_list = eval(check_db_str)
# 建立数据库连接
db = My_mysql()
# 遍历check_db_list
for check_db_dict in check_db_list:
logger.info("当前要比对的sql语句:\n{}".format(check_db_dict["sql"]))
logger.info("当前执行sql的查询类型(查询结果条数 或 查询某个值。):\n{}".format(check_db_dict["db_type"]))
logger.info("期望结果为:\n{}".format(check_db_dict["expected"]))
# 根据type来调用不同的方法来执行sql语句。
if check_db_dict["db_type"] == "count":
logger.info("比对数据库查询的结果条数,是否符合期望")
# 执行 sql 语句。查询结果是一个整数。
res = db.get_count(check_db_dict["sql"])
logger.info("sql的执行结果为:\n{}".format(res))
elif check_db_dict["db_type"] == "eql":
logger.error("比对数据库查询出来的数据,是否与期望相等")
# 执行 sql 语句,查询结果是一个字典:key ———— value
res = db.get_one_data(check_db_dict["sql"])
logger.info("sql的执行结果为:\n{}".format(res))
# 对于数据库查询结果当中,如果有 Decimal 类型,则转换为 float 类型。
for key,value in res.items():
if isinstance(value, Decimal):
res[key] = float(value)
else:
logger.error("不支持的数据库对比类型!!,请检查你的断言写法!!")
raise Exception
# 字典和字典对比。 将比对结果添加到结果列表当中
check_db_res.append(res == check_db_dict["expected"])
logger.info("比对结果为:{}".format(res == check_db_dict["expected"]))
if False in check_db_res:
logger.error("部分断言失败!,请查看数据库比对结果为False的")
# raise AssertionError
return False
else:
logger.info("所有断言成功!")
return True
if __name__ == '__main__':
# 已经从excel当中读取出来的字符串
check_db_str='''[{"sql":"select id from member where mobile_phone='14560748362'","expected":1,"db_type":"count"}]'''
res = MyAssert().assert_db(check_db_str)
print(res)
===================================================================================
import pytest
import os
import json
from common.my_data import Data
from common.my_conf 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.py_log import LoggerHandler
from common.my_path import testdata_dir
from common.my_replace import replace_case_with_re
from common.my_extract import extract_data_from_response
logger = LoggerHandler()
"""
前置:登陆成功(意味着要鉴权)
步骤:充值
断言:金额对不对
后置:释放资源/清理数据
1、类级别地的前置 -- 所有的充值用例,只需要登陆一次就够了。
登陆帐号:
1、用固定的一个帐号 - 配置化(Conf目录下,data.ini里配置用户)
2、已配置的帐号,如何保证它是已经存在的??
用之前,查一下数据库,如果没有,就注册(session前置)。
2、接口关联处理 -- 登陆接口的返回值,要提取出来,然后作为充值接口的请求参数
准备知识 :re正则表达式、 postman是如何处理参数传递(接口关联的)。
"""
# 第一步:读取注册接口的测试数据 - 是个列表,列表中的每个成员,都是一个接口用例的数据。
excel_path = os.path.join(testdata_dir, "注册接口用例.xlsx")
print(excel_path)
me = MyExcel(excel_path, "充值")
cases = me.read_data()
# 第二步:遍历测试数据,每一组数据,发起一个http的接口
# 实例化请求对象
mq = MyRequests()
my_assert = MyAssert()
class TestRecharge:
@pytest.mark.parametrize("case",cases)
def test_recharge(self, case):
# 1、下一个接口的请求数据中,需要替换,替换为上一个接口中提取的数据。
case = replace_case_with_re(case)
# 2、把替换之后的请求数据(json格式的字符串),转换成一个字典
req_dict = json.loads(case["req_data"])
# 3、发起请求
if hasattr(Data,"token"):
resp = mq.send_requests(case["url"], case["method"], req_dict, token=getattr(Data,"token"))
else:
resp = mq.send_requests(case["url"], case["method"], req_dict)
logger.info(resp.json())
# 4、提取响应结果中的数据,并设置为全局变量
if case["extract"]:
# 调用提取函数
extract_data_from_response(case["extract"],resp.json())
# 结果空列表
assert_res = []
# 5、断言响应结果中的数据
if case["assert_list"]:
response_check_res = my_assert.assert_response_value(case["assert_list"], resp.json())
assert_res.append(response_check_res)
# 6、断言数据库 - sql语句、结果与实际、比对的类型
if case["assert_db"]:
db_check_res = my_assert.assert_db(case["assert_db"])
assert_res.append(db_check_res)
# 最终的抛AsserttionError
if False in assert_res:
raise AssertionError
# 从响应结果当中,提取leave_amount的值
setattr(Data,"leave_amount",str(resp.json()["data"]["leave_amount"]))