Preface:
Interface Automation refers to the automation of simulation programming interface level, because the interface is not easy to change, the maintenance cost is smaller, so by major companies alike.
The first edition entrance: Interface Automation Framework (Pytest + request + Allure)
This version made some upgrades to increase the automatic generation of testcase, etc., with a look! ~~
First, a brief introduction
Environment: Mac + Python 3 + Pytest + Allure + Request
Process: Charles Exporting interface data - automatically generate test cases - modify test cases - Execute test cases - Allure report generation
open source: Click here to jump to github
Note ⚠️: Charles Export Interface should select the file typeJSON Session File(.chlsj)
Important Module Description:
. 1, writeCase.py: Charles automatically read the new file, and automatically generate test cases
2, apiMethod.py: request packaging methods, can support multi-protocol extension (GET \ POST \ PUT)
. 3, checkResult.py : verify response encapsulation method
4, setupMain.py: core code, define and execute a set of cases, generate reports
___
Second, the catalog description
Third, code analysis
1, test data yml (automatically generated file yml)
# 用例基本信息
test_info:
# 用例标题,在报告中作为一级目录显示
title: blogpost
# 用例ID
id: test_reco_01
# 请求的域名,可写死,也可写成模板关联host配置文件
host: ${host}$
# 请求地址 选填(此处不填,每条用例必填)
address: /api/v2/recomm/blogpost/reco
# 前置条件,case之前需关联的接口
premise:
# 测试用例
test_case:
- test_name: reco_1
# 第一条case,info可不填
info: reco
# 请求协议
http_type: https
# 请求类型
request_type: POST
# 参数类型
parameter_type: application/json
# 请求地址
address: /api/v2/recomm/blogpost/reco
# 请求头
headers:
# parameter为文件路径时
parameter: reco.json
# 是否需要获取cookie
cookies: False
# 是否为上传文件的接口
file: false
# 超时时间
timeout: 20
# 校验列表 list or dict
# 不校验时 expected_code, expected_request 均可不填
check:
expected_request: result_reco.json
check_type: only_check_status
expected_code: 503
# 关联键
relevance:
2, the test case (automatically generated case.py)
@allure.feature(case_dict["test_info"]["title"])
class TestReco:
@pytest.mark.parametrize("case_data", case_dict["test_case"], ids=[])
@allure.story("reco")
@pytest.mark.flaky(reruns=3, reruns_delay=3)
def test_reco(self, case_data):
"""
:param case_data: 测试用例
:return:
"""
self.init_relevance = ini_request(case_dict, PATH)
# 发送测试请求
api_send_check(case_data, case_dict, self.init_relevance, PATH)
3, writeCase.py (packaging method: automatic generation of test case)
def write_case(_path):
yml_list = write_case_yml(_path)
project_path = str(os.path.abspath('.').split('/bin')[0])
test_path = project_path+'/aff/testcase/'
src = test_path+'Template.py'
for case in yml_list:
yml_path = case.split('/')[0]
yml_name = case.split('/')[1]
case_name = 'test_' + yml_name + '.py'
new_case = test_path + yml_path + '/' + case_name
mk_dir(test_path + yml_path)
if case_name in os.listdir(test_path + yml_path):
pass
else:
shutil.copyfile(src, new_case)
with open(new_case, 'r') as fw:
source = fw.readlines()
n = 0
with open(new_case, 'w') as f:
for line in source:
if 'PATH = setupMain.PATH' in line:
line = line.replace("/aff/page/offer", "/aff/page/%s" % yml_path)
f.write(line)
n = n+1
elif 'case_dict = ini_case' in line:
line = line.replace("Template", yml_name)
f.write(line)
n = n + 1
elif 'class TestTemplate' in line:
line = line.replace("TestTemplate", "Test%s" % yml_name.title().replace("_", ""))
f.write(line)
n = n + 1
elif '@allure.story' in line:
line = line.replace("Template", yml_name)
f.write(line)
n = n + 1
elif 'def test_template' in line:
line = line.replace("template", yml_name.lower())
f.write(line)
n = n + 1
else:
f.write(line)
n += 1
for i in range(n, len(source)):
f.write(source[i])
4, apiMethod.py (packaging method: http multi-protocol)
def post(header, address, request_parameter_type, timeout=8, data=None, files=None):
"""
post请求
:param header: 请求头
:param address: 请求地址
:param request_parameter_type: 请求参数格式(form_data,raw)
:param timeout: 超时时间
:param data: 请求参数
:param files: 文件路径
:return:
"""
if 'form_data' in request_parameter_type:
for i in files:
value = files[i]
if '/' in value:
file_parm = i
files[file_parm] = (os.path.basename(value), open(value, 'rb'))
enc = MultipartEncoder(
fields=files,
boundary='--------------' + str(random.randint(1e28, 1e29 - 1))
)
header['Content-Type'] = enc.content_type
response = requests.post(url=address, data=enc, headers=header, timeout=timeout)
else:
response = requests.post(url=address, data=data, headers=header, timeout=timeout, files=files)
try:
if response.status_code != 200:
return response.status_code, response.text
else:
return response.status_code, response.json()
except json.decoder.JSONDecodeError:
return response.status_code, ''
except simplejson.errors.JSONDecodeError:
return response.status_code, ''
except Exception as e:
logging.exception('ERROR')
logging.error(e)
raise
5, checkResult.py (packaging method: check result response)
def check_result(test_name, case, code, data, _path, relevance=None):
"""
校验测试结果
:param test_name: 测试名称
:param case: 测试用例
:param code: HTTP状态
:param data: 返回的接口json数据
:param relevance: 关联值对象
:param _path: case路径
:return:
"""
# 不校验结果
if case["check_type"] == 'no_check':
with allure.step("不校验结果"):
pass
# json格式校验
elif case["check_type"] == 'json':
expected_request = case["expected_request"]
if isinstance(case["expected_request"], str):
expected_request = readExpectedResult.read_json(test_name, expected_request, _path, relevance)
with allure.step("JSON格式校验"):
allure.attach("期望code", str(case["expected_code"]))
allure.attach('期望data', str(expected_request))
allure.attach("实际code", str(code))
allure.attach('实际data', str(data))
if int(code) == case["expected_code"]:
if not data:
data = "{}"
check_json(expected_request, data)
else:
raise Exception("http状态码错误!\n %s != %s" % (code, case["expected_code"]))
# 只校验状态码
elif case["check_type"] == 'only_check_status':
with allure.step("校验HTTP状态"):
allure.attach("期望code", str(case["expected_code"]))
allure.attach("实际code", str(code))
allure.attach('实际data', str(data))
if int(code) == case["expected_code"]:
pass
else:
raise Exception("http状态码错误!\n %s != %s" % (code, case["expected_code"]))
# 完全校验
elif case["check_type"] == 'entirely_check':
expected_request = case["expected_request"]
if isinstance(case["expected_request"], str):
expected_request = readExpectedResult.read_json(test_name, expected_request, _path, relevance)
with allure.step("完全校验"):
allure.attach("期望code", str(case["expected_code"]))
allure.attach('期望data', str(expected_request))
allure.attach("实际code", str(code))
allure.attach('实际data', str(data))
if int(code) == case["expected_code"]:
result = operator.eq(expected_request, data)
if result:
pass
else:
raise Exception("完全校验失败! %s ! = %s" % (expected_request, data))
else:
raise Exception("http状态码错误!\n %s != %s" % (code, case["expected_code"]))
# 正则校验
elif case["check_type"] == 'Regular_check':
if int(code) == case["expected_code"]:
try:
result = ""
if isinstance(case["expected_request"], list):
for i in case[""]:
result = re.findall(i.replace("\"","\""), str(data))
allure.attach('校验完成结果\n',str(result))
else:
result = re.findall(case["expected_request"].replace("\"", "\'"), str(data))
with allure.step("正则校验"):
allure.attach("期望code", str(case["expected_code"]))
allure.attach('正则表达式', str(case["expected_request"]).replace("\'", "\""))
allure.attach("实际code", str(code))
allure.attach('实际data', str(data))
allure.attach(case["expected_request"].replace("\"", "\'") + '校验完成结果',
str(result).replace("\'", "\""))
if not result:
raise Exception("正则未校验到内容! %s" % case["expected_request"])
except KeyError:
raise Exception("正则校验执行失败! %s\n正则表达式为空时" % case["expected_request"])
else:
raise Exception("http状态码错误!\n %s != %s" % (code, case["expected_code"]))
else:
raise Exception("无该校验方式%s" % case["check_type"])
6, setupMain.py (Example execution set, generating a test report)
def invoke(md):
output, errors = subprocess.Popen(md, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate()
o = output.decode("utf-8")
return o
if __name__ == '__main__':
LogConfig(PATH)
write_case(har_path)
args = ['-s', '-q', '--alluredir', xml_report_path]
pytest.main(args)
cmd = 'allure generate %s -o %s' % (xml_report_path, html_report_path)
invoke(cmd)
7, the test report
Above, like it please ❤️ praise it ~
welcome attention to my brief book , blog , TesterHome , Github ~ ~ ~