接口自动化框架搭建,持续更新中。。。
简介:
一个excel文件代表一个项目,一个项目中的每个sheet就是一个模块,一个模块中写正常用例和异常用例,extract列用来提取返回参数中的数据,提取后将保存于variables.ini配置文件中,引用时用${变量}引用变量或者${func()}引用自定义方法,如随机数生成方法。运行前先指定环境,根据环境匹配对应环境的域名,可以选择项目、模块、具体用例进行运行。运行后生成测试报告。django实现web前端页面,不用再关注代码进行接口自动化。用jenkins实现定时运行,异常时发邮件告警。
分层:
一个excel文件就是一个项目,一个项目中的每个sheet就是一个模块,一个模块中写正常用例和异常用例
✅用一列来专门提取数据,格式是字典,key为被赋值的变量,value为jsonpath要查询的接口返回的字段key,运行时
判断如果有需要提取数据,则将数据进行保存到ini配置文件中
✅引用时通过${变量}方式提取,用re库操作,re.sub()或直接replace()进行替换
✅引用自定义函数(如要生成随机数等),${func()}进行引用,用反射原理来执行函数并返回内容,然后re.sub() 或者 replace()进行替换。
提取变量:
# 提取参数
def extract(extract, res_text):
vaiable_ini = '%s/config/variable.ini' % BASEDIR
try:
if extract and isinstance(eval(extract), dict):
extract = eval(extract)
print(type(res_text))
for k, v in extract.items():
value = str(jsonpath.jsonpath(json.loads(res_text), '$..%s' % v))[1:-1] # jsonpath进行模糊匹配
Config(vaiable_ini).write_varables(section='response',
option=extract.get(k),
value=value
)
except NameError:
print('返回参数提取异常:', traceback.format_exc())
引用变量和自定义方法变量(有部分代码写的冗余,暂不进行优化):
# 参数引用${variable}、${func()}
def quote(headers, payloads)->tuple:
"""re.sub()进行动态替换"""
"""
1.re.findall() 匹配到所有的${} 和 ${func()} 各返回一个列表,
2.检查是否存在于ini配置文件中,存在则re.sub替换,重新赋值给新的headers或payloads变量
3.用反射的方式检查类中是否有这个方法,有则执行,返回结果,然后再re.sub替换或replace(),重新赋值给headers或payloads变量
"""
# 正则匹配到所有引用变量列表,格式如:['random()', 'token', 'name']
headers_virables_quotes = re.findall(r'\$\{(.*?)\}', headers)
payloads_virables_quotes = re.findall(r'\$\{(.*?)\}', payloads)
valiadate_ini = '%s/config/variable.ini' % BASEDIR
# 对headers的操作
for hvq in headers_virables_quotes:
if hvq.endswith('()'):
# 进行反射操作
if hasattr(CustomFunc(), hvq[:-2]):
fun = getattr(CustomFunc(), hvq[:-2])
value = fun()
headers = headers.replace('${%s}' % hvq, "\'%s\'" % value) # 注意:header的value必须都是字符串
else:
raise Exception('没有找到对应变量${%s}' % hvq)
else:
# 在ini文件中判断是否存在变量
if Config(valiadate_ini).has_option('custom', hvq):
quote_value = Config(valiadate_ini).read_variables('custom', hvq)
headers = headers.replace('${%s}' % hvq, quote_value)
elif Config(valiadate_ini).has_option('response', hvq):
quote_value = Config(valiadate_ini).read_variables('response', hvq)
headers = headers.replace('${%s}' % hvq, quote_value)
else:
raise Exception('没有找到对应变量${%s}' % hvq)
# 对payloads的操作
for pvq in payloads_virables_quotes:
if pvq.endswith('()'):
# 进行映射操作
if hasattr(CustomFunc(), pvq[:-2]):
fun = getattr(CustomFunc(), pvq[:-2])
value = fun()
payloads = payloads.replace('${%s}' % pvq, str(value))
else:
raise Exception('没有找到对应变量${%s}' % pvq)
else:
# 在ini文件中判断是否存在变量
if Config(valiadate_ini).has_option('custom', pvq):
quote_value = Config(valiadate_ini).read_variables('custom', pvq)
payloads = payloads.replace('${%s}' % pvq, quote_value)
elif Config(valiadate_ini).has_option('response', pvq):
quote_value = Config(valiadate_ini).read_variables('response', pvq)
payloads = payloads.replace('${%s}' % pvq, quote_value)
else:
raise Exception('没有找到对应变量${%s}' % pvq)
return headers, payloads
excel表格中用path,运行时通过输入环境参数,和表中的服务名,在ini配置文件中进行匹配对应的域名,和path组合成完整的url
env_host.ini:
url_util():
# 根据环境,选择对应的域名
def url_util(host_dev, svr_name, path)->str:
if host_dev == 'dev':
host = Config(Run.env_ini).read_variables('host-dev', svr_name)
elif host_dev == 'test':
host = Config(Run.env_ini).read_variables('host-test', svr_name)
else:
host = Config(Run.env_ini).read_variables('host-yace', svr_name)
url = 'http://%s%s' % (host, path)
指定环境:
Run函数运行是,先确定环境:fixfure函数通过读取ini配置文件中的配置信息,进行传入环境参数,进而在测试用例里通过服务名,组合成对应环境的url
指定范围:
# 用于指定项目、模块运行
def handel_data(self, *args) ->list:
# 格式校验
if len(args) <= 0:
raise Exception("请输人正确的运行用例范围,如:全部-->all,项目-->project,项目、模块-->project, module1, module2")
all_cases = [] # 全部用例,格式[{},{},{}]
excel_files = os.listdir('%s/data/excelcases' % BASEDIR) # 用例文件 【文件1, 文件2】
excel_files.pop(excel_files.index('__init__.py'))
# 运行全部项目用例
if args[0] in ['all', '全部']:
# 获取所有excel用例文件目录
for excel_file in excel_files:
path = '%s/data/excelcases/%s' % (BASEDIR, excel_file)
result_values = list(self.read_excel(path).values())
# print(result_values)
for result_value in result_values:
for value in result_value:
all_cases.append(value)
return all_cases
# 运行全部冒烟(正常)用例
elif args[0] in ['冒烟', '正常']:
print('走冒烟')
# 获取所有excel用例文件目录
for excel_file in excel_files:
path = '%s/data/excelcases/%s' % (BASEDIR, excel_file)
result_values = list(self.read_excel(path).values())
# print(result_values)
for result_value in result_values:
for value in result_value:
# print(value)
if '冒烟' in value['case_name'] or '正常' in value['case_name']:
all_cases.append(value)
print("all cases:\n", all_cases)
return all_cases
# 根据项目、模块自定义选择运行
else:
if args[0] not in excel_files:
raise Exception('%s项目excel文件不存在,请核实!', args[0])
else:
path = '%s/data/excelcases/%s' % (BASEDIR, args[0])
result = self.read_excel(path)
print(result)
result_new = {}
args_modules = args[1:]
# print(args)
result_keys = list(result.keys())
# print(result_keys)
if len(args_modules) == 0: # 指定项目运行(运行项目下的全部)
result_values = list(result.values())
for result_value in result_values:
for case in result_value:
all_cases.append(case)
else: # 指定项目-模块运行
for arg in args_modules:
if arg not in result_keys:
raise Exception('%s项目中不存在模块%s' % (args[0], arg))
else:
result_new[arg] = result[arg]
result_values = list(result_new.values())
for result_value in result_values:
for value in result_value:
# print(value)
all_cases.append(value)
# print("all cases:\n", all_cases)
return all_cases
# 根据接口名,或接口id,运行指定的接口:todo
使用conftest.py 和 fixture来实现
# 用例运行前后置的操作
@pytest.fixture(scope='module', autouse=True)
def setup_teardown(request):
variable_ini = '%s/config/variable.ini' % BASEDIR # 保存变量文件
# 1.所有用例运行前,清空variable.ini中[response]下的变量
options = Config(variable_ini).get_options('response')
print("运行前操作:\n1.删除response下的optons变量%s" % options)
if options:
for option in options:
Config(variable_ini).rm_option('response', option)
yield
print("运行后操作:\n暂无。")
生成html的测试报告,报告优化,将接口返回都给展示
生成allure测试报告,优化报告