本文为博主原创,未经许可严禁转载。
本文链接:https://blog.csdn.net/zyooooxie/article/details/113653811
纪念总访问量破10w,加更!!!
接口自动化测试框架的 category,有兴趣,可以看看。
之前写完基础篇的第七篇,我就觉得非常ok了,咱啥情景都考虑到了,脚本完美适用 现有财务的需求;
这2天就看公司大佬的接口脚本【不同项目】时,发现有些思路可以 ‘偷师’ :
下图是c老师 设计的接口用例:
需求
- sql语句的参数化:
【基础篇 我设计的 sql语句是用来 查询某字段的值,sql都是直接写死的】
- 后置sql语句:
上图数据库后置处理是 更改订单状态 》》我设计的 本就有teardown用例,这儿新增 sql 物理删除【delete接口是逻辑删除】
- 请求header
【基础篇 未设计】
4.响应header | body提取某字段
【基础篇 未设计】
大佬做的是header某字段值 提取+更新 》》我想到的:body提取某字段值,后续请求使用
此外,本身脚本的优化:
-
读取excel方法优化 + 读取excel全部数据时 前后置用例的bug修复
-
封装 用例的执行方法
Excel设计
新增列名:request header、sql_teardown、re_extractor
代码优化
- 参数化:url、sql、body
思路:
某条用例执行时,定义params_dict【空字典】;
前置用例+本条用例+后置用例:
- 处理请求体:remark+request data 定义的某变量 (update );
- 处理url, 定义的某变量(get);
- 发请求;
- 普通断言;
- sql断言:sql定义的某变量 (get);
- body提取:正则定义的某变量(update );
- 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
- 响应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
- 请求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))
-
sql删除
-
前、后置用例规范
之前,前后置用例setup | teardown的值都是1,2 ,3【往后排序】,但读取excel所有数据时,就会出现(不同sheet的) 多个1,多个2;
所以重新规范:sheetIndex(从1开始) + 执行顺序
【这样的设计,是由隐患的(index1:排序15 值为115;index11:排序5 值为115;但我觉得不会有这么多前、后置用例)】
- 执行方法封装与调用
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)
- 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
执行结果、日志:
- 2个excel【2个系统】
- 日志分析+抓包分析
A. 举例
B. 举例
C. 举例
- Jenkins执行
这次分享就主要这些内容;
交流技术 欢迎+QQ 153132336 zy
个人博客 https://blog.csdn.net/zyooooxie