Python接口自动化测试框架【八】

本文为博主原创,未经许可严禁转载。
本文链接:https://blog.csdn.net/zyooooxie/article/details/113653811

纪念总访问量破10w,加更!!!

接口自动化测试框架的 category,有兴趣,可以看看。

之前写完基础篇的第七篇,我就觉得非常ok了,咱啥情景都考虑到了,脚本完美适用 现有财务的需求;
这2天就看公司大佬的接口脚本【不同项目】时,发现有些思路可以 ‘偷师’ :

下图是c老师 设计的接口用例:

在这里插入图片描述

需求

  1. sql语句的参数化:

【基础篇 我设计的 sql语句是用来 查询某字段的值,sql都是直接写死的】

  1. 后置sql语句:

上图数据库后置处理是 更改订单状态 》》我设计的 本就有teardown用例,这儿新增 sql 物理删除【delete接口是逻辑删除】

  1. 请求header

【基础篇 未设计】

4.响应header | body提取某字段

【基础篇 未设计】

大佬做的是header某字段值 提取+更新 》》我想到的:body提取某字段值,后续请求使用

此外,本身脚本的优化:

  1. 读取excel方法优化 + 读取excel全部数据时 前后置用例的bug修复

  2. 封装 用例的执行方法

Excel设计

在这里插入图片描述

在这里插入图片描述

新增列名:request header、sql_teardown、re_extractor

代码优化

  1. 参数化:url、sql、body

思路:

某条用例执行时,定义params_dict【空字典】;
前置用例+本条用例+后置用例:

  1. 处理请求体:remark+request data 定义的某变量 (update );
  2. 处理url, 定义的某变量(get);
  3. 发请求;
  4. 普通断言;
  5. sql断言:sql定义的某变量 (get);
  6. body提取:正则定义的某变量(update );
  7. sql删除:sql定义的某变量 (get);

    @staticmethod
    def change_params_dictValue(exe_str, params_dict):
        re_str = r'\${\w+}'
        result_list = re.findall(re_str, exe_str)

        for r in result_list:

            new_pa = re.search(r'\w+', r)
            key = new_pa.group()
            exe_str = re.sub(re_str, '{}'.format(params_dict[str(key)]), exe_str, count=1)

        Log.info('执行: {}'.format(exe_str))
        return exe_str
  1. 响应body提取,更新 params_dict
    @staticmethod
    def response_re_extractor(res_body, re_extractor, params_dict):
        for r in re_extractor:
            new = CommonFun.change_params_dictValue(r[1].replace(r'\\', '\\'), params_dict)
            result = re.search(new, res_body)
            key, value = r[0], result.group(1)
            params_dict.update({
    
    key: value})

        return params_dict
  1. 请求header

    def req(self, test_url, request_type: str, test_data, header, file=None, file_parameter=None):
        request_type = request_type.lower()

        if request_type == 'json':
            return self.send_post_json(test_url, test_data, test_header=header)

        elif request_type == 'get':
            return self.send_get(test_url, test_data, test_header=header)

        elif request_type == 'form':
            return self.send_post_data(test_url, test_data, test_header=header)

        elif request_type == 'file':
            return self.send_post_file(test_url, new_data=test_data, file_name=file, file_parameter=file_parameter, test_header=header)

        else:
            raise Exception('请求方法 不合法:{}'.format(request_type))
  1. sql删除

  2. 前、后置用例规范

之前,前后置用例setup | teardown的值都是1,2 ,3【往后排序】,但读取excel所有数据时,就会出现(不同sheet的) 多个1,多个2;
所以重新规范:sheetIndex(从1开始) + 执行顺序

【这样的设计,是由隐患的(index1:排序15 值为115;index11:排序5 值为115;但我觉得不会有这么多前、后置用例)】

  1. 执行方法封装与调用

File:common_fun.py

class CommonFun(object):

    def interchangeable_check(self, session_host, data, gl_excel_data, db, cur):

        Log.info(data)

        session, host = session_host

        data_feature, data_story, data_title, data_url, data_method, data_request_header, data_request_data, data_expected_data, \
        data_remark, data_description, data_setup, data_teardown, data_need_s_t, data_sql_select, data_sql_delete, \
        data_re_extractor = data

        allure.dynamic.feature(data_feature)
        allure.dynamic.story(data_story)
        allure.dynamic.title(data_title)
        allure.dynamic.description(data_description)

        if isinstance(data_need_s_t, float):
            if int(data_need_s_t) == 0:
                Log.info('当前用例 不需要前置、后置')
                setup_case, teardown_case = list(), list()
            else:
                raise Exception('前后置 传参不合法')

        elif data_need_s_t == '':
            Log.info('当前用例 不需要前置、后置')
            setup_case, teardown_case = list(), list()

        elif isinstance(eval(data_need_s_t), list):
            setup_case, teardown_case = eval(data_need_s_t)
            Log.info('当前用例需要前置:{}、后置:{}'.format(setup_case, teardown_case))

        else:
            raise Exception('前后置 传参不合法')

        params_dict = dict()

        for s in setup_case:
            set_up_case = [u for u in gl_excel_data if u[10] == s]
            if len(set_up_case) != 1:
                raise Exception('前置case 数据量不对: {}'.format(set_up_case))
            feature, story, title, url, method, req_header, req_data, expected_data, remark, description, setup, teardown, need_s_t, \
            sql_select, sql_delete, re_extractor = set_up_case[0]
            pd_setup = self.run_case(session, host, url, method, req_header, req_data, expected_data, remark, sql_select, setup_teardown='setup', params_dict=params_dict, sql_delete=sql_delete, re_extractor=re_extractor, db=db, cur=cur)
            params_dict.update(pd_setup)

        pd_run = self.run_case(session, host, data_url, data_method, data_request_header, data_request_data, data_expected_data, data_remark, data_sql_select, sql_delete=data_sql_delete, re_extractor=data_re_extractor, setup_teardown=data_need_s_t, params_dict=params_dict, db=db, cur=cur)
        params_dict.update(pd_run)

        for t in teardown_case:
            tear_down_case = [x for x in gl_excel_data if x[11] == t]
            if len(tear_down_case) != 1:
                raise Exception('后置case 数据量不对: {}'.format(tear_down_case))
            feature, story, title, url, method, req_header, req_data, expected_data, remark, description, setup, teardown, need_s_t, \
            sql_select, sql_delete, re_extractor = tear_down_case[0]
            pd_teardown = self.run_case(session, host, url, method, req_header, req_data, expected_data, remark, sql_select, setup_teardown='teardown', params_dict=params_dict, sql_delete=sql_delete, re_extractor=re_extractor, db=db, cur=cur)
            params_dict.update(pd_teardown)

    def run_case(self, session, host, url, method, header, req_data, res_data, remark, sql_select, params_dict, db, cur, sql_delete=None, re_extractor=None, setup_teardown=None):
        if setup_teardown == 'setup':
            Log.info('前置 执行')
        elif setup_teardown == 'teardown':
            Log.info('后置 执行')
        else:
            Log.info('测试用例 run')

        # url拼接
        new_url = ''.join([host, url])

        # 请求体的处理
        if remark != '':                    # 修改req_data
            new_req_data, params_actual_value_list = self.change_excel_paramsValue_new(remark, req_data, params_dict)
            params_actual_value_dict = dict(params_actual_value_list)
            params_actual_value_dict.update(params_dict)
        else:                               # 不修改req_data
            new_req_data = None if req_data == '' else json.loads(req_data)
            params_actual_value_dict = params_dict

        # 响应体的处理
        new_res_data = self.change_excel_assertValue(res_data)

        # url的参数化处理
        if new_url.find('${') != -1:
            new_url = self.change_params_dictValue(new_url, params_actual_value_dict)

        # 请求头的处理
        header = self.change_header(header)

        # 发请求
        res = SessionSendReq(session=session).req(test_url=new_url, request_type=method, test_data=new_req_data, header=header)

        # # 打印请求信息
        # Log.info(dump.dump_all(res).decode('utf-8'))

        # 断言
        if sql_select == '':
            self.assert_fun(new_res_data, res)
        else:
            sql_select = self.change_params_dictValue(sql_select, params_actual_value_dict)
            self.sql_assert_fun(res_data, sql_select, res, db, cur)

        # 后置提取
        if re_extractor != '':
            res_text = res.text
            re_extractor = eval(re_extractor)

            params_actual_value_dict = self.response_re_extractor(res_text, re_extractor, params_actual_value_dict)

        # 后置sql
        if sql_delete != '':
            exe_sql = self.change_params_dictValue(sql_delete, params_actual_value_dict)
            cur.execute(exe_sql)

        # 打印:参数dict
        Log.info(params_actual_value_dict)
        return params_actual_value_dict


File:test_balance.py


# noinspection PyAttributeOutsideInit
class TestBalance(object):

    # gl_excel_data = ExcelConfig.read_excel(os.path.join(excel_dir, 'new1.xlsx'), sheet_index=0)
    # gl_excel_data = ExcelConfig.read_excel(os.path.join(excel_dir, 'new1.xlsx'), sheet_index=1)
    gl_excel_data = ExcelConfig.read_excel(os.path.join(excel_dir, 'new1.xlsx'))

    Log.info('excel读取数据:{}'.format(gl_excel_data))

    excel_data = [g for g in gl_excel_data if g[11] == '' and g[10] == '']
    Log.info('执行用例:{}'.format(excel_data))

    def setup_class(self):
        self.db, self.cur = CommonFun.connect_db(db_name='aku_re_db')

    def teardown_class(self):
        CommonFun.disconnect_db(self.db, self.cur)

    @pytest.mark.parametrize('data', excel_data)
    def test_b(self, session_host, data):
        CommonFun().interchangeable_check(session_host=session_host, data=data, gl_excel_data=self.gl_excel_data,
                                          db=self.db, cur=self.cur)


File:test_supplier.py

gl_excel_data = ExcelConfig.read_excel(os.path.join(excel_dir, 'new.xlsx'), sheet_index=0)
Log.info('excel读取数据:{}'.format(gl_excel_data))

excel_data = [g for g in gl_excel_data if g[11] == '' and g[10] == '']
Log.info('执行用例:{}'.format(excel_data))


# noinspection PyAttributeOutsideInit
class TestSupplier(object):

    def setup_class(self):
        self.db, self.cur = CommonFun.connect_db(db_name='supplier_db')

    def teardown_class(self):
        CommonFun.disconnect_db(self.db, self.cur)

    @pytest.mark.parametrize('data', excel_data)
    def test_s(self, session_host, data):
        CommonFun().interchangeable_check(session_host, data, gl_excel_data, db=self.db, cur=self.cur)


  1. excel读取方法优化
class ExcelConfig(object):

    @staticmethod
    def read_excel(file_absolute_path, sheet_index=None):
        all_data = list()
        book = xlrd.open_workbook(file_absolute_path)

        if sheet_index is None:
            Log.info('获取全部数据')
            all_sheets = book.sheets()

        else:
            Log.info('获取索引为:{}的数据'.format(sheet_index))
            all_sheets = [book.sheet_by_index(sheet_index)]

        for s in all_sheets:
            # s_index = all_sheets.index(s)
            Log.info('当前sheet name为{}'.format(s.name))
            cur_sheet = book.sheet_by_name(s.name)

            for h in range(1, cur_sheet.nrows):                        # 不要表头
                ele = cur_sheet.row_values(h)
                if ele == [''] * cur_sheet.ncols:
                    Log.debug('有毛病吧,整行为空')
                else:
                    all_data.append(ele)

        return all_data

执行结果、日志:

  1. 2个excel【2个系统】

在这里插入图片描述

在这里插入图片描述

  1. 日志分析+抓包分析

A. 举例

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

B. 举例

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

C. 举例

在这里插入图片描述

在这里插入图片描述

  1. Jenkins执行

在这里插入图片描述

这次分享就主要这些内容;

优化篇 【二】马上就来了

交流技术 欢迎+QQ 153132336 zy
个人博客 https://blog.csdn.net/zyooooxie

猜你喜欢

转载自blog.csdn.net/zyooooxie/article/details/113653811