思路:
- 使用excel进行传参
- 通过zip方法将数据转为字典格式
- 通过ddt模块将excel中获取的数据传入接口中
- 通过对请求参数的不同,进行不同方式的接口测试(get、post)
- 通过对请求参数的不同,对获取的response进行不同的处理
- 将response中获取的数据与用例中获取的期望值进行断言比较
- 将实际结果、测试结果填入excel
- 生成测试报告
- 将测试结果与测试报告发送至邮箱
步骤:
1.excel中测试用例的编写
获取excel数据
import os.path
from xlrd import open_workbook
from request.request_res import TestRequest
from Common.get_model_list import Get_model_list
class GetData():
def excel_data(self):
data_list = []
file_path = os.path.join(TestRequest().dir_base_path(),'data','TestCase.xls')
print(file_path)
owb = open_workbook(file_path)
test_sheet = owb.sheet_by_name("register")
try:
module_list2 = []
for i in range(1, test_sheet.nrows): # 获取除标题外其他行的内容
col_datas = dict(zip(test_sheet.row_values(0), test_sheet.row_values(i)))
data_list.append(col_datas)
module_list = Get_model_list().data_list()
for i in data_list:
if i['module'] in module_list:
module_list2.append(i)
data_list = module_list2
return data_list
except Exception as e:
print(e)
2. 通过ddt模块将excel中获取的数据传入接口中
from data.get_exc_data import GetData
from request.request_res import TestRequest
from data.write_to_excel import Write_to
import unittest, ddt, json
final_data = GetData().excel_data()
n = 2
@ddt.ddt # 导入ddt模块
class TestCommon(unittest.TestCase):
@classmethod
def setUpClass(cls) -> None:
cls.logs = TestRequest().get_logs() # 导入日志方法
cls.logs.debug('开始写入接口自动化测试用例')
@classmethod
def tearDownClass(cls) -> None:
cls.logs.debug('自动化接口用例结束')
def setUp(self):
global n
print("======开始执行第",n//2,"条测试用例======")
n += 1
def tearDown(self):
global n
print("======第",n//2,"条测试用例执行完毕======")
n += 1
@ddt.data(*final_data) # 引入ddt模块,读取拿到的数据
def test_run(self, final_data):
case_id = int(final_data['id'])
method = final_data['method']
url = final_data['url']
case_name = final_data['case_name']
if final_data['params'] != '':
params = json.loads(final_data['params'])
else:
params = final_data['params']
expect_res = final_data['expect_res']
print('测试用例名:', case_name)
res = TestRequest().test_http_request(url, params, method)
Write_to().write_to_exc(case_id, method, expect_res, url, res)
if method == 'get':
self.assertEqual(str(expect_res).split('.')[0], str(res), '测试失败')
if method == 'post':
self.assertEqual(expect_res, str(res), '测试失败')
3.通过对请求参数的不同,进行不同方式的接口测试(get、post)
# 对请求进行分类
def test_http_request(self, url, params, http_method):
res = ''
if http_method.upper() == 'POST':
try:
header = {}
data = params
res = requests.post(url, json=data, headers=header)
print("正在进行post请求")
res = res.json()
except Exception as e:
print("post请求出现了异常:{}".format(e))
res = str(e)
elif http_method.upper() == 'GET':
try:
res = requests.get(url, params)
res = res.status_code
print("正在进行get请求")
except Exception as e:
print("get请求出现了异常:{}".format(e))
res = str(e)
return res
4.将实际结果、测试结果填入excel
from xlrd import open_workbook
from xlutils.copy import copy
from request.request_res import TestRequest
import os
file_path = os.path.join(TestRequest().dir_base_path(), 'data', 'TestCase.xls')
class Write_to():
# 将测试结果写入excel
def write_to_exc(self, case_id, method, expect_res, url, accept_res):
if method.upper() == 'GET':
try:
wb = open_workbook(file_path)
new_copy = copy(wb)
sheet = new_copy.get_sheet(0)
if str(accept_res) in str(expect_res):
print(url, "执行结果:pass,预期结果为:", expect_res, ",实际结果为:", accept_res)
sheet.write(case_id, 7, accept_res)
sheet.write(case_id, 8, 'PASS')
new_copy.save(file_path)
else:
print(url, '的测试结果为Failed,预期结果为:', expect_res, ',实际结果为:', accept_res)
sheet.write(case_id, 7, accept_res)
sheet.write(case_id, 8, 'FAIL')
new_copy.save(file_path)
except Exception as e:
print(e)
if method.upper() == 'POST':
try:
wb = open_workbook(file_path)
new_copy = copy(wb)
sheet = new_copy.get_sheet(0)
# accept_res = accept_res['success']
if expect_res == str(accept_res):
print(url, "执行结果:pass,预期结果为:", expect_res, ",实际结果为:", accept_res)
sheet.write(case_id, 7, str(accept_res))
sheet.write(case_id, 8, 'PASS')
new_copy.save(file_path)
else:
print(url, '的测试结果为Failed,预期结果为:', expect_res, ',实际结果为:', accept_res)
sheet.write(case_id, 7, str(accept_res))
sheet.write(case_id, 8, 'FAIL')
new_copy.save(file_path)
except Exception as e:
print(e)
5.生成测试报告
if __name__ == '__main__':
file_name = 'request_' + time.strftime('%Y-%m-%d-%H-%M-%S') + '.html'
file_path = os.path.join(os.getcwd(),'reports')
ts = unittest.TestSuite()
ts.addTest(unittest.makeSuite(TestCommon))
retults = BeautifulReport(ts)
retults.report(description='接口测试报告V.' + time.strftime('%Y%m%d%H%M') ,filename=file_name,report_dir=file_path)
#处理Email配置文件信息
config_path = os.path.join(TestRequest().dir_base_path(),'data','EmailConfig.ini')
config = configparser.ConfigParser() # 调用外部的读取配置文件的方法
config.read(config_path, encoding='utf-8')
GetEmail().email('username','passwd','recv','title','content','on_off')
6.将测试结果与测试报告发送至邮箱
def send_email(self):
msg = MIMEMultipart()
# 发送内容的对象
if self.file: # 处理附件的
file_name = os.path.split(self.file)[-1] # 只取文件名,不取路径
try:
f = open(self.file, 'rb').read()
except Exception as e:
raise Exception('附件打不开!!!!')
else:
att = MIMEText(f, "base64", "utf-8")
att["Content-Type"] = 'application/octet-stream'
# base64.b64encode(file_name.encode()).decode()
new_file_name = '=?utf-8?b?' + base64.b64encode(file_name.encode()).decode() + '?='
# 这里是处理文件名为中文名的,必须这么写
att["Content-Disposition"] = 'attachment; filename="%s"' % (new_file_name)
msg.attach(att)
msg.attach(MIMEText(self.content)) # 邮件正文的内容
msg['Subject'] = self.title # 邮件主题
msg['From'] = self.username # 发送者账号
msg['To'] = ','.join(self.recv) # 接收者账号列表
if self.ssl:
self.smtp = smtplib.SMTP_SSL(self.email_host, port=self.ssl_port)
else:
self.smtp = smtplib.SMTP(self.email_host, port=self.port)
# 发送邮件服务器的对象
self.smtp.login(self.username, self.passwd)
try:
self.smtp.sendmail(self.username, self.recv, msg.as_string())
pass
except Exception as e:
print('邮件发送出错了!', e)
else:
print('邮件发送成功!')
self.smtp.quit()
这里还要对邮件进行配置
class SendEmail(object):
def __init__(self, username, passwd, recv, title, content,
file=None, ssl=False,
email_host='smtp.163.com', port=25, ssl_port=465):
self.username = username # 用户名
self.passwd = passwd # 密码(这里的密码需要使用授权码)
self.recv = recv # 收件人,多个要传list ['a@qq.com','b@qq.com]
self.title = title # 邮件标题
self.content = content # 邮件正文
self.file = file # 附件路径,如果不在当前目录下,要写绝对路径
self.email_host = email_host # smtp服务器地址
self.port = port # 普通端口
self.ssl = ssl # 是否安全链接
self.ssl_port = ssl_port # 安全链接端口
[Email]
username = xxxxxxxx@163.com
passwd = xxxxxxx
recv = ['xxxxxxxxxxx.com']
title = 接口测试报告
content = 测试结果请见附件!
on_off = off
class GetEmail():
def email(self,username,passwd,recv,title,content,on_off):
username = config.get('Email', username)
passwd = config.get('Email', passwd)
recv = config.get('Email', recv)
title = config.get('Email', title)
content = config.get('Email', content)
on_off = config.get('Email', on_off)
if on_off == 'on':
SendEmail(
username=username,
passwd=passwd,
recv=eval(recv),
title=title,
content=content,
file=os.path.join(file_path,file_name),
ssl=True,
).send_email()
else:
print("邮件发送开关配置关闭,请打开开关后可正常自动发送测试报告")
另外还做了一些功能,比如说日志、测试模块
日志:
# 封装日志方法
def get_logs(self):
logs = logging.getLogger()
logs.setLevel(logging.DEBUG)
file_name = time.strftime('%Y-%m-%d-%H-%M-%S') + '.log'
path = os.path.join(self.dir_base_path(), 'report', 'logs', file_name)
write_file = logging.FileHandler(path, 'a+', encoding='utf-8')
write_file.setLevel(logging.DEBUG)
set_logs = logging.Formatter('%(asctime)s - %(filename)s - %(funcName)s - %(levelname)s - %(message)s')
write_file.setFormatter(set_logs)
pycharm_text = logging.StreamHandler()
pycharm_text.setFormatter(set_logs)
logs.addHandler(write_file)
logs.addHandler(pycharm_text)
return logs
测试模块:针对excel中的模块分类,对需要进行测试的模块进行接口测试,不需要测试的,可以在模块前加上‘#’
from request.request_res import TestRequest
import os
file_path = os.path.join(TestRequest().dir_base_path(),'data','modle_test.txt')
class Get_model_list():
def data_list(self):
model_list = []
with open(file_path,'r')as f:
lines_data = f.readlines()
for line_data_old in lines_data:
line_data_new = line_data_old.replace('\n','')
if not line_data_new.startswith('#'):
model_list.append(line_data_new)
return model_list
#For modules that do not need to be executed, please add a hash mark ('#') in front of the module
register
register2
register3
register4
整体结构: