Foreword:
Interface automation refers to the automation of the analog program interface level. Because the interface is not easy to change and the maintenance cost is less, it is deeply loved by major companies.
Entry of the first edition: Interface Automation Framework (Pytest+request+Allure)
This version has made some upgrades, adding automatic testcase generation, etc. Let’s take a look! ~~
One, brief introduction
Environment: Mac+Python 3+Pytest+Allure+Request
Process: Charles export interface Data-automatically generate test cases-modify test cases-execute test cases-generate Allure report
Open source: Click here to jump to github
Remarks??: Charles export interface should select the file type as JSON Session File(.chlsj)
Important module introduction:
1. writeCase.py: automatically read the new Charles file and automatically generate test cases
2. apiMethod.py: package request method, can support multi-protocol extension (get\post\put)
3. checkResult.py: package verification response method
4. setupMain.py: core code, define and execute set of use cases , Generate report
___
Second, Catalog Introduction
three, code analysis
1, test data yml( Automatically generated yml file)
# Basic information of the use case
test_info:
# The title of the use case, displayed as a first-level directory in the report
title: blogpost
# Use case ID
id: test_reco_01
# The requested domain name can be written to death, or it can be written as a template associated host configuration file.
host: ${host}$
# Request address selection Fill in (not filled here, required for each use case)
address: /api/v2/recomm/blogpost/reco
# Precondition, the interface that needs to be associated before the case
premise:
# Test case
test_case:
-test_name: reco_1
# The first case, info can be left blank
info: reco
# Request protocol
http_type: https
# Request type
request_type: POST
# Parameter type
parameter_type: application/json
# Request address
address: /api/v2/recomm/blogpost/reco< br /> # Request header
headers:
# When parameter is a file path
parameter: reco.json
# Do you need to get cookies?
cookies: False
# Is it an interface for uploading files?
file: false
# Timeout time
timeout: 20
# Checklist list or dict
# No Both expected_code and expected_request can be left blank during verification
check:
expected_request: result_reco.json
check_type: only_check_status
expected_code: 503
# Association key< br /> relevance:
2, test case (auto-generated case.py)
@allure.feature(case_dict["test_info"]["title"])
class TestReco:
@pytest.mark.parametrize("case_data", cas e_dict["test_case"], ids=[])
@allure.story("reco")
@pytest.mark.flaky(reruns=3, reruns_delay=3)
def test_reco (self, case_data):
"""
:param case_data: test case
:return:
"""
self.init_relevance = ini_request (case_dict, PATH)
# Send test request
api_send_check(case_data, case_dict, self.init_relevance, PATH)
3, writeCase.py (encapsulation method: automatically generate 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 (encapsulation method: http multi-protocol)
def post(header, address, request_parameter_type, timeout =8, data=None, files=None):
"""
post request
:param header: request header
:param address: request address
: param request_parameter_type: request parameter format (form_data, raw)
:param timeout: timeout time
:param d ata: request parameters
:param files: file path
: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:< br /> 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 (encapsulation method: check the response result)
def check_result(test_name, case, code, data, _path, relevance=None):
"""
Verify test result
:param test_name: test name
:param case: test case
:param code: HTTP status
:param data: returned interface json data
:param relevance: associated value object
:param _path: case path
:return:
"""
# Do not check the result
if case["check_type"] =='no_check':
with allure.step("Do not check Test result"):
pass
# json format check
elif case["check_type"] =='json':
expected_request = case["expected_request"]
if isinstance(case["expected_request"], str):
expected_request = rea dExpectedResult.read_json(test_name, expected_request, _path, relevance)
with allure.step("JSON format verification"):
allure.attach("expected code", str(case["expected_code"] ))
allure.attach('expected data', str(expected_request))
allure.attach("actual code", str(code))
allure.attach('actual data' , str(data))
if int(code) == case["expected_code"]:
if not data:
data = "{}"
check_json(expected_request, data)
else:
raise Exception("http status code error! \n %s != %s"% (code, case["expected_code"]))
# Only check status code
elif case["check_type"] =='only_check_status':
with allure.step("Check HTTP status"):
allure.attach("Expected code", str(case["expected_code"]))
allure.attach("Actual code" , str(code))
allure.attach('actual data', str(data))
if int(code) == case["expected_code"]:
pass
else:
raise Exception("http status code error!\n %s != %s"% (code, case["expected_code"]))
# Complete verification
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("complete verification"):
allure.attach("expected code", str(case["expected_code"]))
allure.attach('expected data', str(expected_request))
allure.attach("actual code", str(code))
allure.attach('actual data', str(data))
if int(code) == case["expected_code"]:
result = operator.eq(expected_request, data)
if result:
pass
else:
raise Exception("Complete verification failed! %s! = %s"% (expected_request, data))
else:
raise Exception("http status code error!\n %s != %s"% (code, case["expected_code "]))
# Regular check
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('Result of verification completed\n',str(result))
else:< br /> result = re.findall(case["expected_request"].replace("\"", "\'"), str(data))
with allure.step("Regular Check"):
allure.attach("expected code", str(case["expected_code"]))
allure.attach('regular expression', str(case["expected_request"]).replace(" \'", "\""))
allure.attach("Actual code" , str(code))
allure.attach('actual data', str(data))
allure.attach(case["expected_request"].replace("\"", "\'" ) +'Verification completed result',
str(result).replace("\'", "\""))
if not result:
raise Exception("Regular is not corrected Content verified! %s"% case["expected_request"])
except KeyError:
raise Exception("Regular verification execution failed! %s\nWhen the regular expression is empty"% case["expected_request"] )
else:
raise Exception("http status code error!\n %s != %s"% (code, case["expected_code"]))
else :
raise Exception("No such check method %s"% case["check_type"])
6, setupMain.py (execute the set of use cases, generate test reports)
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, test report