替换操作封装+框架优化(数据库断言优化+加入conftest.py+decimal处理)

正则的使用

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"]))

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值