接----口---

api_keyword

api_keyword.py

# -*- coding: utf-8 -*-
# @Time : 2023/11/5 20:25
# @Author : Hami
import allure
import requests
import jsonpath
import json

from deepdiff import DeepDiff

from day09.P02_ApiFrameProject.config import *


# 关键字驱动/基类/工具类
# 1. 发送请求:8种:post、get、put、delete...
# 2. 提取数据

# 补充知识点:方法的缺省值:params=None (默认值),如果没有传参则默认为None

class ApiKey:
    @allure.step(">>>>>>:开始发送Get请求")
    def get(self, url, params=None, **kwargs):
        """
        发送get请求
        :param url:接口请求url
        :param params: 拼接在url的参数
        :param kwargs: 其它的参数
        :return: 返回请求后的数据
        """
        # print(">>>>>>:开始发送Get请求")
        return requests.get(url=url, params=params, **kwargs)

    @allure.step(">>>>>>:开始发送Post请求")
    def post(self, url, data=None, json=None, **kwargs):
        """
         发送post请求
        :param url: 接口请求url
        :param data: data的请求数据
        :param json: json的请求数据
        :param kwargs: 其它的参数
        :return: 返回请求后的数据
        """
        # print(">>>>>>:开始发送Post请求")
        res = requests.post(url=url, data=data, json=json, **kwargs)
        print(">>>>>>:响应数据为:", res.json())
        return res

    @allure.step(">>>>>>:开始提取JsonPath数据")
    def get_text(self, response, key):
        """
        提取json当中的某个值
        :param response: 需要提取的json数据,比如:{"msg":"登录成功"}
        :param key: 对应要提取的jsonpath,比如: $.msg
        :return: 返回提取数据之后的【第一个值】
        """
        if isinstance(response, str):
            # 是字符串,我就让它转一下类型
            response = json.loads(response)

        print(">>>>>>:开始提取JsonPath数据")
        value_list = jsonpath.jsonpath(response, key)
        print(">>>>>>:提取数据为:", value_list[0])
        return value_list[0]

    @allure.step(">>>>>>:开始提取数据库的数据")
    def get_sqlData(self, sqlValue):
        """

        :param sqlValue: SQL,返回的数据是一个元组
        :return:
        """
        import pymysql

        # 1. 配置数据库连接信息并连接
        connection = pymysql.connect(
            host=DB_HOST,  # 数据库地址
            port=DB_PORT,
            user=DB_USER,  # 数据库用户名
            password=DB_PASSWORD,  # 数据库密码
            db=DB_NAME,  # 数据库名称
        )
        # 2. 创建游标对象,使用它进行操作
        cursor = connection.cursor()
        # 4. 使用游标对象去执行操作SQL
        cursor.execute(sqlValue)
        # 5. 得到结果集的下一行
        result = cursor.fetchone()
        # 6. 关闭数据库连接
        cursor.close()
        return result[0]

    @allure.step(">>>>>>:响应数据全量对比")
    def jsonDeepDiff(self, json1, json2, **other):
        """
        对比json数据的一致性
        :param json1: 期望结果
        :param json2: 实际结果
        :param other: 你想要写的对应的规则
        :return:
        """
        res = DeepDiff(json1, json2, **other)
        if res == {}:
            return True
        else:
            return False


# 函数的入口:main
if __name__ == '__main__':
    # 1. 实例化对象:ApiKey
    ak = ApiKey()
    # 2. 通过对应的类调用对应的方法 --四要素
    # url = "http://shop-xo.hctestedu.com/index.php?s=/api/user/login"
    # pulic_data = {"application": "app", "application_client_type": "weixin"}
    # # 请求参数-- body (你的body数据是要以json进行提交,参数:json)
    # data = {"accounts": "hami", "pwd": "123456", "type": "username"}
    #
    # res = ak.post(url=url, params=pulic_data, data=data)
    # # 3. 提取数据
    # text = ak.get_text(res.json(), "$.msg")

    # 案例2 :"{"msg":"登录成功"}"
    res = '{"msg":"登录成功"}'
    text = ak.get_text(res, "$.msg")

common

EncryptDateAES.py

# -*- coding: utf-8 -*-
# @Time : 2023/11/3 21:23
# @Author : Hami

"""
对称加密:加密和解密使用的是同一把钥匙,即:使用相同的密匙对同一密码进行加密和解密。
常用的加密方法:DES、3DES、AES...(AES算法是目前最常用的对称加密算法)
"""
import base64
from Crypto.Cipher import AES


class EncryptDate:
    # 构造方法
    def __init__(self, key):
        #  类属性
        self.key = key.encode("utf-8")  # 初始化密钥
        self.length = AES.block_size  # 初始化数据块大小 :16位
        self.aes = AES.new(self.key, AES.MODE_ECB)  # 初始化AES,ECB模式的实例
        # 截断函数,去除填充的字符
        self.unpad = lambda date: date[0:-ord(date[-1])]

    # 缺几位数据就补齐多少位数据:16的倍数
    def pad(self, text):  # text == tony
        """
        #填充函数,使被加密数据的字节码长度是block_size的整数倍
        """
        count = len(text.encode('utf-8'))  # count = 4
        add = self.length - (count % self.length)  # 求它们相差的位数
        # add = 16- (4%16)  === 16 - 4 == 12
        entext = text + (chr(add) * add)
        #  entext = “tony” + (chr(add) * 12  )  === entext == tony
        # print("entext的数据是:",entext)
        return entext

    # 加密函数
    def encrypt(self, encrData):  # 加密函数   encrData == tony (16位)
        res = self.aes.encrypt(self.pad(encrData).encode("utf8"))  # self.aes.encrypt(tony)
        msg = str(base64.b64encode(res), encoding="utf8")
        return msg

    # 解密函数
    def decrypt(self, decrData):  # 解密函数   XbXHJrNLwoTVcyfqM9eTgQ==
        # 从base64编码转回来
        res = base64.decodebytes(decrData.encode("utf8"))
        # 将数据进行对应的解密:XbXHJrNLwoTVcyfqM9eTgQ==
        msg = self.aes.decrypt(res).decode("utf8")
        # print("msg的值:",msg)
        # 把转回来的数据后面的字符去掉。
        return self.unpad(msg)


key = "1234567812345678"
ENCRYPTAES = EncryptDate(key)

FileDataDriver.py

# -*- coding: utf-8 -*-
# @Time : 2023/11/8 21:01
# @Author : Hami
import openpyxl
from day09.P02_ApiFrameProject.config import *
import yaml
from day09.P02_ApiFrameProject.common.EncryptDateAES import ENCRYPTAES


class FileReader:
    """
    专门用来读取和写入yaml、excel文件
    """

    # 读取excel--openpyxl -- 文件格式:.xlsx
    @staticmethod  # 直接通过类名进行调用
    def read_excel(file_path=CASEDATAURL, sheet_name=SHEETNAME):
        """
        读取Excel文件,只支持 .xlsx文件
        :param file_path: 文件路径
        :return: excel文件数据,元组的格式
        """
        # 打开现有的Excel文件或创建新的文件
        try:
            #  正常情况下直接打开
            workbook = openpyxl.load_workbook(file_path)
        except FileNotFoundError:
            workbook = openpyxl.Workbook()

        # 选择或创建指定的工作表
        if sheet_name in workbook.sheetnames:
            # 【正常】  判断有没有对应的shtttname ,有的话把对应的数据给我加载出来
            worksheet = workbook[sheet_name]
        else:
            # 没有的话,给我新建一个
            worksheet = workbook.create_sheet(sheet_name)

        # 获取列名 --- 把第2行的数据拿出来作为我们的key值
        headers = [cell.value for cell in worksheet[2]]
        # print("所有的key", headers)

        # 将数据存储为字典,并且放在我们data当中
        data = []  # 所有的数据

        # 把小的数据从第三行开始
        for row in worksheet.iter_rows(min_row=3, values_only=True):
            # 把所有的数据直接加进去
            # data.append(dict(zip(headers, row)))

            #  当 is_true == True才应该加进去
            new_data = dict(zip(headers, row))
            if new_data["is_true"] is True:
                data.append(new_data)
        workbook.close()
        # 所有的数据
        return data

    @staticmethod
    def writeDataToExcel(file_path=CASEDATAURL, sheet_name=SHEETNAME, row=None, column=None, value=None):
        # 打开现有的Excel文件或创建新的文件
        try:
            workbook = openpyxl.load_workbook(file_path)
        except FileNotFoundError:
            workbook = openpyxl.Workbook()

        # 选择或创建指定的工作表
        if sheet_name in workbook.sheetnames:
            worksheet = workbook[sheet_name]
        else:
            worksheet = workbook.create_sheet(sheet_name)

        # 写入数据到指定行和列
        worksheet.cell(row=row, column=column).value = value

        # 保存修改后的文件--- 所以执行过程当中excel是要关闭的状态
        workbook.save(file_path)

    @staticmethod
    def read_yaml(file_path=YAMLDATA):
        """
        读取yaml文件
        :param file_path: 文件路径
        :return: yaml文件数据
        """
        with open(file_path, 'r', encoding="utf-8") as file:
            data = yaml.safe_load(file)
        return data

    @staticmethod
    def write_yaml(data, file_path=YAMLDATA):
        """
        写入yaml文件,写入无序没有关系,通过key获取数据
        :param data: 需要写入的数据
        :param file_path: 文件路径
        :return:
        """
        with open(file_path, 'w', encoding="utf-8") as file:
            # allow_unicode=True,避免将中文字符转换为 Unicode 转义序列
            yaml.dump(data, file, allow_unicode=True)

    @staticmethod
    def data_EncryptDateAES(data):
        newdata = {}  # 去掉前面@符号同时数据进行加密
        for key in data:
            if key[0] == "@":
                # 需要加密处理
                newdata[key[1:]] = ENCRYPTAES.encrypt(data[key])
            else:
                # 不需要加密处理
                newdata[key] = data[key]
        return newdata


if __name__ == '__main__':
    # CaseData = FileReader.read_excel()

    # 需要加密处理
    data = {"@username": "tony", "@password": "123456"}
    res = FileReader.data_EncryptDateAES(data)
    print(res)

    # 不需要加密处理
    data = {"accounts": "hami", "@pwd": "123456", "type": "username"}
    res = FileReader.data_EncryptDateAES(data)
    print(res)

data

excel

编号[0]地址[1]路径[2]请求方法[3]公共参数(PARAMS)[4]请求头[5]参数[6]参数类型[7]校验字段[8]预期结果[9]检查结果[10]用例名[11]JSON提取_引用名称[12]SQL提取[13]正则提取_引用名称[14]story(小模块)[16]feature(大模块)[17]备注[18]级别[19]SQL校验字段名[20] 【列表格式】SQL期望数据[22]【集合格式】响应全量比对[25]响应全量条件[26]是否执行
idurlpathmethodparamsheadersdatatypeactualResultexpectResultresultcaseNamejsonExDatasqlExDataregularExDatastoryNamefeatureNameremarkranksqlAssertDatasqlExpectResultresponseExpectresponseExcludeis_true
3http://shop-xo.hctestedu.com/index.php?s=api/user/regpost{"application": "app",
"application_client_type": "weixin"}
{"accounts":"hami","pwd":"123456","type": "username"}data$.msg注册成功测试失败,断言失败。注册成功F01S01_注册F01_用户管理用例注册blockerTRUE

yaml

- actualResult: $..msg
  caseName: 成功调用登陆接口,msg返回登录成功
  data:
    accounts: hami
    pwd: 123456
    type: username
  expectResult: 登录成功
  featureName: F01_用户管理
  headers: null
  id: 0
  jsonExData:
    MSG: $.msg
    VAR_TOKEN: $..data.token
  method: post
  params:
    application: app
    application_client_type: weixin
  path: api/user/login
  rank: blocker
  regularExData: null
  remark: 正常用例登陆
  result: 测试通过,断言成功。
  sqlAssertData:
    id: SELECT id FROM sxo_user WHERE username='hami'
    name: SELECT username FROM sxo_user WHERE username='hami'
  sqlExData:
    id: SELECT id FROM sxo_user WHERE username='hami'
    name: SELECT username FROM sxo_user WHERE username='hami'
  sqlExpectResult:
    id: 65
    name: hami
  storyName: F01S01_登陆
  type: data
  url: http://shop-xo.hctestedu.com/index.php?s=
- actualResult: $..msg
  caseName: 加入购物车
  data:
    goods_id: 10
    spec: null
    stock: 1
  expectResult: 加入成功
  featureName: F01_购物车管理
  headers:
    Content-Type: application/json;charset=UTF-8
  id: 1
  jsonExData: null
  method: post
  params:
    application: app
    application_client_type: weixin
    token: 19182bcffd67ef89fae61d09da16d2fd
  path: api/cart/save
  rank: blocker
  regularExData: null
  remark: 能够正确的加入购物车
  result: 测试通过,断言成功。
  sqlAssertData: null
  sqlExData: null
  sqlExpectResult: null
  storyName: F02S01_加入购物车
  type: json
  url: http://shop-xo.hctestedu.com/index.php?s=
- actualResult: $..msg
  caseName: 错误的密码进行登录
  data:
    accounts: hami
    pwd: 4564641
    type: username
  expectResult: 密码错误
  featureName: F01_用户管理
  headers: null
  id: 2
  jsonExData: null
  method: post
  params:
    application: app
    application_client_type: weixin
  path: api/user/login
  rank: blocker
  regularExData: null
  remark: 正常用例登陆
  result: 测试通过,断言成功。
  sqlAssertData: null
  sqlExData: null
  sqlExpectResult: null
  storyName: F01S01_登陆
  type: data
  url: http://shop-xo.hctestedu.com/index.php?s=

logdata

log.log

2023-11-15 21:26:05 INFO 用例ID:testcase/Test_Excel_08.py::TestCase::testCase[CaseData0] conftest.py pytest_runtest_makereport 47
2023-11-15 21:26:05 INFO 测试结果:failed conftest.py pytest_runtest_makereport 48
2023-11-15 21:26:05 INFO 故障表示:self = <Test_Excel_08.TestCase object at 0x000001D249B9B9A0>
CaseData = {'actualResult': '$.msg', 'caseName': '注册成功', 'data': '{"accounts":"hami","pwd":"123456","type": "username"}', 'expectResult': '注册成功', ...}

    @pytest.mark.flaky(reruns=3, reruns_delay=3)
    @pytest.mark.parametrize("CaseData", AllCaseData)
    def testCase(self, CaseData):
        self.__dynamic_title(CaseData)
        CaseData = eval(Template(str(CaseData)).render(self.all_var))
    
        print(CaseData)
    
        # 写Excle的行和列
        row = CaseData["id"]
        column = 11
    
        # 初始化对应的值:
        res = None
        msg = None
        value = None
    
        # -------------------------发送请求-------------------------------
        try:
            # 请求数据
            url = CaseData["url"] + CaseData["path"]
            params = eval(CaseData["params"]) if isinstance(CaseData["params"], str) else None
            headers = eval(CaseData["headers"]) if isinstance(CaseData["headers"], str) else None
    
            if CaseData["data"] is None:  # 会影响框架的性能
                data = None
            else:
                # 进行加密处理
                data = FileReader.data_EncryptDateAES(eval(CaseData["data"]))
    
            dict_data = {
                "url": url,
                "params": params,
                "headers": headers,
                "data": data
            }
    
            if CaseData["type"] == "json":
                dict_data["data"] = json.dumps(dict_data["data"])
        except Exception:
            value = MSG_DATA_ERROR
            FileReader.writeDataToExcel(row=row, column=column, value=value)
        else:  # 响应对象
            # 得到对应的响应数据
            res = getattr(self.ak, CaseData["method"])(**dict_data)
    
        # -------------------------提取数据库的操作---------------------------
        self.__sql_extraction(CaseData)
    
        # -------------------------进行断言处理-------------------------------
        # 实际结果
        try:
            msg = self.ak.get_text(res.json(), CaseData["actualResult"])
        except Exception:
            value = MSG_EXDATA_ERROR
            FileReader.writeDataToExcel(row=row, column=column, value=value)
        else:
            # 只会是一个分支语言,但是不会造成测试结果成功或者失败,所以必须无论如何都是需要断言
            if msg == CaseData["expectResult"]:
                value = MSG_ASSERT_OK
                # 成功之后进行数据提取
                self.__json_extraction(CaseData, res)
    
            else:
                value = MSG_ASSERT_NO
            FileReader.writeDataToExcel(row=row, column=column, value=value)
        finally:
>           assert msg == CaseData["expectResult"], value
E           AssertionError: 测试失败,断言失败。
E           assert '账号已存在' == '注册成功'
E             - 注册成功
E             + 账号已存在

testcase\Test_Excel_08.py:181: AssertionError conftest.py pytest_runtest_makereport 49
2023-11-15 21:26:05 INFO 异常:<ExceptionInfo AssertionError("测试失败,断言失败。\nassert '账号已存在' == '注册成功'\n  - 注册成功\n  + 账号已存在") tblen=16> conftest.py pytest_runtest_makereport 50
2023-11-15 21:26:05 INFO 用例耗时:0.23679439999999996 conftest.py pytest_runtest_makereport 51
2023-11-15 21:26:05 INFO ************************************** conftest.py pytest_runtest_makereport 52
2023-11-15 21:26:08 INFO 用例ID:testcase/Test_Excel_08.py::TestCase::testCase[CaseData0] conftest.py pytest_runtest_makereport 47
2023-11-15 21:26:08 INFO 测试结果:failed conftest.py pytest_runtest_makereport 48
2023-11-15 21:26:08 INFO 故障表示:self = <Test_Excel_08.TestCase object at 0x000001D249B9B9A0>
CaseData = {'actualResult': '$.msg', 'caseName': '注册成功', 'data': '{"accounts":"hami","pwd":"123456","type": "username"}', 'expectResult': '注册成功', ...}

    @pytest.mark.flaky(reruns=3, reruns_delay=3)
    @pytest.mark.parametrize("CaseData", AllCaseData)
    def testCase(self, CaseData):
        self.__dynamic_title(CaseData)
        CaseData = eval(Template(str(CaseData)).render(self.all_var))
    
        print(CaseData)
    
        # 写Excle的行和列
        row = CaseData["id"]
        column = 11
    
        # 初始化对应的值:
        res = None
        msg = None
        value = None
    
        # -------------------------发送请求-------------------------------
        try:
            # 请求数据
            url = CaseData["url"] + CaseData["path"]
            params = eval(CaseData["params"]) if isinstance(CaseData["params"], str) else None
            headers = eval(CaseData["headers"]) if isinstance(CaseData["headers"], str) else None
    
            if CaseData["data"] is None:  # 会影响框架的性能
                data = None
            else:
                # 进行加密处理
                data = FileReader.data_EncryptDateAES(eval(CaseData["data"]))
    
            dict_data = {
                "url": url,
                "params": params,
                "headers": headers,
                "data": data
            }
    
            if CaseData["type"] == "json":
                dict_data["data"] = json.dumps(dict_data["data"])
        except Exception:
            value = MSG_DATA_ERROR
            FileReader.writeDataToExcel(row=row, column=column, value=value)
        else:  # 响应对象
            # 得到对应的响应数据
            res = getattr(self.ak, CaseData["method"])(**dict_data)
    
        # -------------------------提取数据库的操作---------------------------
        self.__sql_extraction(CaseData)
    
        # -------------------------进行断言处理-------------------------------
        # 实际结果
        try:
            msg = self.ak.get_text(res.json(), CaseData["actualResult"])
        except Exception:
            value = MSG_EXDATA_ERROR
            FileReader.writeDataToExcel(row=row, column=column, value=value)
        else:
            # 只会是一个分支语言,但是不会造成测试结果成功或者失败,所以必须无论如何都是需要断言
            if msg == CaseData["expectResult"]:
                value = MSG_ASSERT_OK
                # 成功之后进行数据提取
                self.__json_extraction(CaseData, res)
    
            else:
                value = MSG_ASSERT_NO
            FileReader.writeDataToExcel(row=row, column=column, value=value)
        finally:
>           assert msg == CaseData["expectResult"], value
E           AssertionError: 测试失败,断言失败。
E           assert '账号已存在' == '注册成功'
E             - 注册成功
E             + 账号已存在

testcase\Test_Excel_08.py:181: AssertionError conftest.py pytest_runtest_makereport 49
2023-11-15 21:26:08 INFO 异常:<ExceptionInfo AssertionError("测试失败,断言失败。\nassert '账号已存在' == '注册成功'\n  - 注册成功\n  + 账号已存在") tblen=16> conftest.py pytest_runtest_makereport 50
2023-11-15 21:26:08 INFO 用例耗时:0.20338590000000067 conftest.py pytest_runtest_makereport 51
2023-11-15 21:26:08 INFO ************************************** conftest.py pytest_runtest_makereport 52
2023-11-15 21:26:11 INFO 用例ID:testcase/Test_Excel_08.py::TestCase::testCase[CaseData0] conftest.py pytest_runtest_makereport 47
2023-11-15 21:26:11 INFO 测试结果:failed conftest.py pytest_runtest_makereport 48
2023-11-15 21:26:11 INFO 故障表示:self = <Test_Excel_08.TestCase object at 0x000001D249B9B9A0>
CaseData = {'actualResult': '$.msg', 'caseName': '注册成功', 'data': '{"accounts":"hami","pwd":"123456","type": "username"}', 'expectResult': '注册成功', ...}

    @pytest.mark.flaky(reruns=3, reruns_delay=3)
    @pytest.mark.parametrize("CaseData", AllCaseData)
    def testCase(self, CaseData):
        self.__dynamic_title(CaseData)
        CaseData = eval(Template(str(CaseData)).render(self.all_var))
    
        print(CaseData)
    
        # 写Excle的行和列
        row = CaseData["id"]
        column = 11
    
        # 初始化对应的值:
        res = None
        msg = None
        value = None
    
        # -------------------------发送请求-------------------------------
        try:
            # 请求数据
            url = CaseData["url"] + CaseData["path"]
            params = eval(CaseData["params"]) if isinstance(CaseData["params"], str) else None
            headers = eval(CaseData["headers"]) if isinstance(CaseData["headers"], str) else None
    
            if CaseData["data"] is None:  # 会影响框架的性能
                data = None
            else:
                # 进行加密处理
                data = FileReader.data_EncryptDateAES(eval(CaseData["data"]))
    
            dict_data = {
                "url": url,
                "params": params,
                "headers": headers,
                "data": data
            }
    
            if CaseData["type"] == "json":
                dict_data["data"] = json.dumps(dict_data["data"])
        except Exception:
            value = MSG_DATA_ERROR
            FileReader.writeDataToExcel(row=row, column=column, value=value)
        else:  # 响应对象
            # 得到对应的响应数据
            res = getattr(self.ak, CaseData["method"])(**dict_data)
    
        # -------------------------提取数据库的操作---------------------------
        self.__sql_extraction(CaseData)
    
        # -------------------------进行断言处理-------------------------------
        # 实际结果
        try:
            msg = self.ak.get_text(res.json(), CaseData["actualResult"])
        except Exception:
            value = MSG_EXDATA_ERROR
            FileReader.writeDataToExcel(row=row, column=column, value=value)
        else:
            # 只会是一个分支语言,但是不会造成测试结果成功或者失败,所以必须无论如何都是需要断言
            if msg == CaseData["expectResult"]:
                value = MSG_ASSERT_OK
                # 成功之后进行数据提取
                self.__json_extraction(CaseData, res)
    
            else:
                value = MSG_ASSERT_NO
            FileReader.writeDataToExcel(row=row, column=column, value=value)
        finally:
>           assert msg == CaseData["expectResult"], value
E           AssertionError: 测试失败,断言失败。
E           assert '账号已存在' == '注册成功'
E             - 注册成功
E             + 账号已存在

testcase\Test_Excel_08.py:181: AssertionError conftest.py pytest_runtest_makereport 49
2023-11-15 21:26:11 INFO 异常:<ExceptionInfo AssertionError("测试失败,断言失败。\nassert '账号已存在' == '注册成功'\n  - 注册成功\n  + 账号已存在") tblen=16> conftest.py pytest_runtest_makereport 50
2023-11-15 21:26:11 INFO 用例耗时:0.17476039999999848 conftest.py pytest_runtest_makereport 51
2023-11-15 21:26:11 INFO ************************************** conftest.py pytest_runtest_makereport 52
2023-11-15 21:26:14 INFO 用例ID:testcase/Test_Excel_08.py::TestCase::testCase[CaseData0] conftest.py pytest_runtest_makereport 47
2023-11-15 21:26:14 INFO 测试结果:failed conftest.py pytest_runtest_makereport 48
2023-11-15 21:26:14 INFO 故障表示:self = <Test_Excel_08.TestCase object at 0x000001D249B9B9A0>
CaseData = {'actualResult': '$.msg', 'caseName': '注册成功', 'data': '{"accounts":"hami","pwd":"123456","type": "username"}', 'expectResult': '注册成功', ...}

    @pytest.mark.flaky(reruns=3, reruns_delay=3)
    @pytest.mark.parametrize("CaseData", AllCaseData)
    def testCase(self, CaseData):
        self.__dynamic_title(CaseData)
        CaseData = eval(Template(str(CaseData)).render(self.all_var))
    
        print(CaseData)
    
        # 写Excle的行和列
        row = CaseData["id"]
        column = 11
    
        # 初始化对应的值:
        res = None
        msg = None
        value = None
    
        # -------------------------发送请求-------------------------------
        try:
            # 请求数据
            url = CaseData["url"] + CaseData["path"]
            params = eval(CaseData["params"]) if isinstance(CaseData["params"], str) else None
            headers = eval(CaseData["headers"]) if isinstance(CaseData["headers"], str) else None
    
            if CaseData["data"] is None:  # 会影响框架的性能
                data = None
            else:
                # 进行加密处理
                data = FileReader.data_EncryptDateAES(eval(CaseData["data"]))
    
            dict_data = {
                "url": url,
                "params": params,
                "headers": headers,
                "data": data
            }
    
            if CaseData["type"] == "json":
                dict_data["data"] = json.dumps(dict_data["data"])
        except Exception:
            value = MSG_DATA_ERROR
            FileReader.writeDataToExcel(row=row, column=column, value=value)
        else:  # 响应对象
            # 得到对应的响应数据
            res = getattr(self.ak, CaseData["method"])(**dict_data)
    
        # -------------------------提取数据库的操作---------------------------
        self.__sql_extraction(CaseData)
    
        # -------------------------进行断言处理-------------------------------
        # 实际结果
        try:
            msg = self.ak.get_text(res.json(), CaseData["actualResult"])
        except Exception:
            value = MSG_EXDATA_ERROR
            FileReader.writeDataToExcel(row=row, column=column, value=value)
        else:
            # 只会是一个分支语言,但是不会造成测试结果成功或者失败,所以必须无论如何都是需要断言
            if msg == CaseData["expectResult"]:
                value = MSG_ASSERT_OK
                # 成功之后进行数据提取
                self.__json_extraction(CaseData, res)
    
            else:
                value = MSG_ASSERT_NO
            FileReader.writeDataToExcel(row=row, column=column, value=value)
        finally:
>           assert msg == CaseData["expectResult"], value
E           AssertionError: 测试失败,断言失败。
E           assert '账号已存在' == '注册成功'
E             - 注册成功
E             + 账号已存在

testcase\Test_Excel_08.py:181: AssertionError conftest.py pytest_runtest_makereport 49
2023-11-15 21:26:14 INFO 异常:<ExceptionInfo AssertionError("测试失败,断言失败。\nassert '账号已存在' == '注册成功'\n  - 注册成功\n  + 账号已存在") tblen=16> conftest.py pytest_runtest_makereport 50
2023-11-15 21:26:14 INFO 用例耗时:0.2534118999999997 conftest.py pytest_runtest_makereport 51
2023-11-15 21:26:14 INFO ************************************** conftest.py pytest_runtest_makereport 52

report_allure

testcase

Test_Ecel.py

# -*- coding: utf-8 -*-
# @Time : 2023/11/8 20:55
# @Author : Hami
import json
import pytest
from day09.P02_ApiFrameProject.common.FileDataDriver import FileReader
from day09.P02_ApiFrameProject.api_keyword.api_key import ApiKey
from day09.P02_ApiFrameProject.config import *
import allure
from jinja2 import Template  # 变量渲染


class TestCase:
    # 获取对应的数据 CaseData 需要从文档当中去进行读取
    # 1. 获取数据(四要素) 2. 发送请求 3.获取响应数据 4.断言
    AllCaseData = FileReader.read_excel()
    ak = ApiKey()
    # 定义:all_val 存放提取出的数据
    all_var = {}

    def __dynamic_title(self, CaseData):
        # # 动态生成标题
        # allure.dynamic.title(data[11])

        # 如果存在自定义标题
        if CaseData["caseName"] is not None:
            # 动态生成标题
            # allure.dynamic.title(CaseData["caseName"])
            caseName = "CASEID:{}--{}".format(CaseData["id"], CaseData["caseName"])
            allure.dynamic.title(caseName)

        if CaseData["storyName"] is not None:
            # 动态获取story模块名
            allure.dynamic.story(CaseData["storyName"])

        if CaseData["featureName"] is not None:
            # 动态获取feature模块名
            allure.dynamic.feature(CaseData["featureName"])

        if CaseData["remark"] is not None:
            # 动态获取备注信息
            allure.dynamic.description(CaseData["remark"])

        if CaseData["rank"] is not None:
            # 动态获取级别信息(blocker、critical、normal、minor、trivial)
            allure.dynamic.severity(CaseData["rank"])

    def __json_extraction(self, CaseData, res):
        """
        提取响应之后的数据
        :param CaseData: 当前的Case,主要获取需要提取数据的字段:jsonExData
        :param res:响应得到的对应的结果
        :return:
        """
        try:
            if CaseData["jsonExData"]:
                Exdata = eval(CaseData["jsonExData"])  # {"VAR_TOKEN":"$..token","MSG":"$.msg"}
                print("需要提取的数据:>>>", Exdata)
                for key, value in Exdata.items():
                    # 通过对应的jsonpath获取具体的数据
                    new_value = self.ak.get_text(res.json(), value)
                    self.all_var.update(
                        {key: new_value}
                    )
                print("提取出来的数据:>>>", self.all_var)
            else:
                print("需要提取的数据为空")
        except Exception:
            print("请检查你需要提取数据数据格式的正确性。")

    def __sql_extraction(self, CaseData):
        """
         从数据库提取数据
        :param CaseData: 当前的Case,主要获取需要提取数据的字段:sqlExData
        :return:
        """
        try:
            if CaseData["sqlExData"]:
                Exdata = eval(CaseData[
                                  "sqlExData"])  # {"name":"SELECT username FROM sxo_user WHERE username='hami'","id":"SELECT id FROM sxo_user WHERE username='hami'"}
                print("SQL-需要提取的数据:>>>", Exdata)
                for key, value in Exdata.items():
                    # 通过对应的sql获取具体的数据
                    new_value = self.ak.get_sqlData(value)
                    self.all_var.update(
                        {key: new_value}
                    )
                print("SQL-提取出来的数据:>>>", self.all_var)
            else:
                print("SQL-需要提取的数据为空")
        except Exception:
            print("SQL-请检查你需要提取数据数据格式的正确性。")

    def __sql_assertData(self, CaseData):
        res = True
        if CaseData["sqlAssertData"] and CaseData["sqlExpectResult"]:
            # 实际结果:从数据库读取出来的数据--字典的格式 # {"name":"SELECT username FROM sxo_user WHERE username='hami'","id":"SELECT id FROM sxo_user WHERE username='hami'"}
            realityData = eval(CaseData["sqlAssertData"])
            # 期望结果:{"name":"hami","id":75}
            expectData = json.loads(CaseData["sqlExpectResult"])

            realityDataDict = {}
            for key, value in realityData.items():
                # 通过对应的sql获取具体的数据
                new_value = self.ak.get_sqlData(value)
                realityDataDict.update(
                    {key: new_value}
                )

            if expectData != realityDataDict:
                res = False
        return res

    @pytest.mark.flaky(reruns=3, reruns_delay=3)
    @pytest.mark.parametrize("CaseData", AllCaseData)
    def testCase(self, CaseData):
        self.__dynamic_title(CaseData)
        CaseData = eval(Template(str(CaseData)).render(self.all_var))

        print(CaseData)

        # 写Excle的行和列
        row = CaseData["id"]
        column = 11

        # 初始化对应的值:
        res = None
        msg = None
        value = None

        # -------------------------发送请求-------------------------------
        try:
            # 请求数据
            url = CaseData["url"] + CaseData["path"]
            params = eval(CaseData["params"]) if isinstance(CaseData["params"], str) else None
            headers = eval(CaseData["headers"]) if isinstance(CaseData["headers"], str) else None

            if CaseData["data"] is None:  # 会影响框架的性能
                data = None
            else:
                # 进行加密处理
                data = FileReader.data_EncryptDateAES(eval(CaseData["data"]))

            dict_data = {
                "url": url,
                "params": params,
                "headers": headers,
                "data": data
            }

            if CaseData["type"] == "json":
                dict_data["data"] = json.dumps(dict_data["data"])
        except Exception:
            value = MSG_DATA_ERROR
            FileReader.writeDataToExcel(row=row, column=column, value=value)
        else:  # 响应对象
            # 得到对应的响应数据
            res = getattr(self.ak, CaseData["method"])(**dict_data)

        # -------------------------提取数据库的操作---------------------------
        self.__sql_extraction(CaseData)

        # -------------------------进行断言处理-------------------------------
        # 实际结果
        try:
            msg = self.ak.get_text(res.json(), CaseData["actualResult"])
        except Exception:
            value = MSG_EXDATA_ERROR
            FileReader.writeDataToExcel(row=row, column=column, value=value)
        else:
            # 只会是一个分支语言,但是不会造成测试结果成功或者失败,所以必须无论如何都是需要断言
            if msg == CaseData["expectResult"]:
                value = MSG_ASSERT_OK
                # 成功之后进行数据提取
                self.__json_extraction(CaseData, res)

            else:
                value = MSG_ASSERT_NO
            FileReader.writeDataToExcel(row=row, column=column, value=value)
        finally:
            assert msg == CaseData["expectResult"], value

        # -------------------------进行数据库断言处理-------------------------------
        try:
            sqlAssertRes = self.__sql_assertData(CaseData)  # False True
        except:
            print("SQL断言出现问题")
            value = "SQL断言出现问题"
            sqlAssertRes = False
            assert sqlAssertRes, value
        else:
            assert sqlAssertRes
        finally:
            FileReader.writeDataToExcel(row=row, column=column, value=value)

        # -------------------------响应文本全量断言-------------------------------
        if CaseData["responseExpect"]:
            # 期望数据
            json1 = eval(CaseData["responseExpect"])
            other = eval(CaseData["responseExclude"])
            jsonMaxDataRes = self.ak.jsonDeepDiff(json1, res.json(), **other)
            assert jsonMaxDataRes, "两者不一致"

Test_runner.py

# -*- coding: utf-8 -*-
# @Time : 2023/11/8 20:55
# @Author : Hami
import json
import pytest
from day09.P02_ApiFrameProject.common.FileDataDriver import FileReader
from day09.P02_ApiFrameProject.api_keyword.api_key import ApiKey
from day09.P02_ApiFrameProject.config import *
import allure
from jinja2 import Template  # 变量渲染


class TestCase:
    # 获取对应的数据 CaseData 需要从文档当中去进行读取
    # 1. 获取数据(四要素) 2. 发送请求 3.获取响应数据 4.断言
    AllCaseData = FileReader.read_yaml()
    ak = ApiKey()
    # 定义:all_val 存放提取出的数据
    all_var = {}

    def __dynamic_title(self, CaseData):
        # # 动态生成标题
        # allure.dynamic.title(data[11])

        # 如果存在自定义标题
        if CaseData["caseName"] is not None:
            # 动态生成标题
            allure.dynamic.title(CaseData["caseName"])

        if CaseData["storyName"] is not None:
            # 动态获取story模块名
            allure.dynamic.story(CaseData["storyName"])

        if CaseData["featureName"] is not None:
            # 动态获取feature模块名
            allure.dynamic.feature(CaseData["featureName"])

        if CaseData["remark"] is not None:
            # 动态获取备注信息
            allure.dynamic.description(CaseData["remark"])

        if CaseData["rank"] is not None:
            # 动态获取级别信息(blocker、critical、normal、minor、trivial)
            allure.dynamic.severity(CaseData["rank"])

    def __json_extraction(self, CaseData, res):
        """
        提取响应之后的数据
        :param CaseData: 当前的Case,主要获取需要提取数据的字段:jsonExData
        :param res:响应得到的对应的结果
        :return:
        """
        try:
            if CaseData["jsonExData"]:
                Exdata = CaseData["jsonExData"] # {"VAR_TOKEN":"$..token","MSG":"$.msg"}
                print("需要提取的数据:>>>", Exdata)
                for key, value in Exdata.items():
                    # 通过对应的jsonpath获取具体的数据
                    new_value = self.ak.get_text(res.json(), value)
                    self.all_var.update(
                        {key: new_value}
                    )
                print("提取出来的数据:>>>", self.all_var)
            else:
                print("需要提取的数据为空")
        except Exception:
            print("请检查你需要提取数据数据格式的正确性。")

    def __sql_extraction(self, CaseData):
        """
         从数据库提取数据
        :param CaseData: 当前的Case,主要获取需要提取数据的字段:sqlExData
        :return:
        """
        try:
            if CaseData["sqlExData"]:
                Exdata = CaseData["sqlExData"]  # {"name":"SELECT username FROM sxo_user WHERE username='hami'","id":"SELECT id FROM sxo_user WHERE username='hami'"}
                print("SQL-需要提取的数据:>>>", Exdata)
                for key, value in Exdata.items():
                    # 通过对应的sql获取具体的数据
                    new_value = self.ak.get_sqlData(value)
                    self.all_var.update(
                        {key: new_value}
                    )
                print("SQL-提取出来的数据:>>>", self.all_var)
            else:
                print("SQL-需要提取的数据为空")
        except Exception:
            print("SQL-请检查你需要提取数据数据格式的正确性。")

    def __sql_assertData(self, CaseData):

        res = True
        if CaseData["sqlAssertData"] and CaseData["sqlExpectResult"]:
            # 实际结果:从数据库读取出来的数据--字典的格式 # {"name":"SELECT username FROM sxo_user WHERE username='hami'","id":"SELECT id FROM sxo_user WHERE username='hami'"}
            realityData = CaseData["sqlAssertData"]
            # 期望结果:{"name":"hami","id":75}
            expectData = CaseData["sqlExpectResult"]

            realityDataDict = {}
            for key, value in realityData.items():
                # 通过对应的sql获取具体的数据
                new_value = self.ak.get_sqlData(value)
                realityDataDict.update(
                    {key: new_value}
                )

            if expectData != realityDataDict:
                res = False
        return res

    @pytest.mark.parametrize("CaseData", AllCaseData)
    def testCase(self, CaseData):
        self.__dynamic_title(CaseData)
        CaseData = eval(Template(str(CaseData)).render(self.all_var))

        print(CaseData)

        # 写Yaml的下标
        row = CaseData["id"]

        # 初始化对应的值:
        res = None
        msg = None
        value = None

        # 知识:是否断言
        # is_assert = True

        # -------------------------发送请求-------------------------------
        try:
            # 请求数据
            params = eval(CaseData["params"]) if isinstance(CaseData["params"], str) else CaseData["params"]

            dict_data = {
                "url": CaseData["url"] + CaseData["path"],
                "params": params,
                "headers": CaseData["headers"],
                "data": CaseData["data"]
            }

            if CaseData["type"] == "json":
                dict_data["data"] = json.dumps(dict_data["data"])

        except Exception:
            value = MSG_DATA_ERROR
            CaseData["result"] = value
        else:
            # 得到对应的响应数据
            res = getattr(self.ak, CaseData["method"])(**dict_data)

        # -------------------------提取数据库的操作---------------------------
        self.__sql_extraction(CaseData)

        # -------------------------进行断言处理-------------------------------
        # 实际结果
        try:
            msg = self.ak.get_text(res.json(), CaseData["actualResult"])
        except Exception:
            value = MSG_EXDATA_ERROR
            CaseData["result"] = value
        else:
            # 只会是一个分支语言,但是不会造成测试结果成功或者失败,所以必须无论如何都是需要断言
            if msg == CaseData["expectResult"]:
                value = MSG_ASSERT_OK
                # 成功之后进行数据提取
                self.__json_extraction(CaseData, res)

            else:
                # is_assert = False
                value = MSG_ASSERT_NO
            CaseData["result"] = value
        finally:
            assert msg == CaseData["expectResult"], value

        # -------------------------进行数据库断言处理-------------------------------
        try:
            res = self.__sql_assertData(CaseData)  # False True
        except:
            print("SQL断言出现问题")
            value = "SQL断言出现问题"
            assert res, value
        else:
            assert res
        finally:
            CaseData["result"] = value

        # -------------------------把当前的CaseData值写入:可以通过后置处理方法去处理-------------------------------
        self.AllCaseData[row] = CaseData
        FileReader.write_yaml(self.AllCaseData)

config.py

# -*- coding: utf-8 -*-
# @Time : 2023/11/5 22:24
# @Author : Hami

# 配置常量的文件
# 环境变量
# 正式环境
PROJECT_URL = "http://shop-xo.hctestedu.com/index.php"
# 测试环境
# PROJECT_URL = "http://127.0.0.1:8080"

# # 运行模式:
# RUNMODE = "EXCEL"
# RUNMODE = "YAML"

# 测试用例的路径:
# CASEDATAURL = "./data/excel/api_cases_V1.xlsx"
# CASEDATAURL = "./data/excel/api_cases_V2.xlsx"
# CASEDATAURL = "./data/excel/api_cases_V3.xlsx"
# CASEDATAURL = "./data/excel/api_cases_V4.xlsx"
# CASEDATAURL = "./data/excel/api_cases_V5.xlsx"
# CASEDATAURL = "./data/excel/api_cases_V7.xlsx"
CASEDATAURL = "./data/excel/api_cases_V7_error.xlsx"
SHEETNAME = "Sheet1"

YAMLDATA = "./data/yaml/api_yaml_V6.yaml"

# 测试账号
USERNAME = "hami"
PASSWORD = "123456"
LOGINTYPE = "username"
PULIC_DATA = {"application": "app", "application_client_type": "weixin"}

# 错误提示异常信息
MSG_DATA_ERROR = "数据解析错误,请检查dict_data的值的正确性。"
MSG_EXDATA_ERROR = "响应数据提取失败。"
MSG_ASSERT_OK = "测试通过,断言成功。"
MSG_ASSERT_NO = "测试失败,断言失败。"

# 测试数据库的连接信息
DB_HOST = 'shop-xo.hctestedu.com'  # 数据库地址
DB_PORT = 3306
DB_USER = 'api_test'  # 数据库用户名
DB_PASSWORD = 'Aa9999!'  # 数据库密码
DB_NAME = 'shopxo_hctested'  # 数据库名称

conftest.py

# -*- coding: utf-8 -*-
# @Time : 2023/11/5 22:09
# @Author : Hami
import pytest
import logging
from day09.P02_ApiFrameProject.api_keyword.api_key import ApiKey
# 注意:名字是固定的。
# 可以写具体的方法、正常的调用某个接口
# pytest在运行的时候【会自动】先调起这个文件,后面还具体调用这个方法

# 方法:获取token
# 1. 正常的请求对应的接口并且提取数据
# 2. 告诉别人这个是一个测试夹具(测试前置、后置操作):@pytest.fixture()

from day09.P02_ApiFrameProject.config import *

@pytest.fixture(scope= "session")
def token_fix():
    """
    代表我们这个夹具运行一次
    :return:
    """
    print("开始运行:token_fix")
    print("USERNAME:",USERNAME)
    # 1. 实例化对象:ApiKey
    ak = ApiKey()
    # 2. 通过对应的类调用对应的方法 --四要素
    # url = "http://shop-xo.hctestedu.com/index.php?s=/api/user/login"
    url = PROJECT_URL+"?s=/api/user/login"
    pulic_data = PULIC_DATA
    data = {"accounts": USERNAME, "pwd": PASSWORD, "type": LOGINTYPE}
    # 3. 发送请求
    res = ak.post(url=url, params=pulic_data, data=data)
    # 4. 提取数据
    token = ak.get_text(res.json(), "$..token")
    # 5. 返回数据
    return ak,token

# 当执行一个case的时候会自动的调用这个方法:把对应的数据传过来给到call
@pytest.hookimpl(hookwrapper=True, tryfirst=True)
def pytest_runtest_makereport(item, call):
    # 通过 out = yield 定义了一个生成器。在生成器中,res = out.get_result() 获取了测试结果对象。
    out = yield
    res = out.get_result()
    #  res.when == "call":表示正在运行调用测试函数的阶段。
    if res.when == "call":
        logging.info(f"用例ID:{res.nodeid}")
        logging.info(f"测试结果:{res.outcome}")
        logging.info(f"故障表示:{res.longrepr}")
        logging.info(f"异常:{call.excinfo}")
        logging.info(f"用例耗时:{res.duration}")
        logging.info("**************************************")

main.run.py

# -*- coding: utf-8 -*-
# @Time : 2023/11/5 21:09
# @Author : Hami
# https://pypi.tuna.tsinghua.edu.cn/simple 清华镜像
import os

# 框架的运行入口

import pytest

if __name__ == '__main__':
    #  通过pytest 运行 并且生成allure报告【默认】
    # 第一步: 指定运行文件,通过文件会生成运行结果后的数据放在:./result ; --clean-alluredir(每次运行之前清空这个文件夹的历史数据)
    pytest.main(["-vs", "./testcase/Test_Excel_08.py", "--alluredir", "./result", "--clean-alluredir"])
    # 第二步:把数据转成测试报告(html):allure generate ./result -o ./report_allure --clean
    # os.system() # 在cmd(终端)去运行命令
    os.system("allure generate ./result -o ./report_allure --clean")

pytest.ini

[pytest]
log_cli= false
log_level=NOTSET
log_format = %(asctime)s %(levelname)s %(message)s %(filename)s %(funcName)s %(lineno)d
log_date_format = %Y-%m-%d %H:%M:%S

log_file = ./logdata/log.log
log_file_level = info
log_file_format = %(asctime)s %(levelname)s %(message)s %(filename)s %(funcName)s %(lineno)d
log_file_date_format = %Y-%m-%d %H:%M:%S

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值