接口测试框架-[pytest+requests+excel]读取excel表格+requests参数化+pytest测试报告
用例示范
目录结构
1.demo
demo1.py
'''
pip install xlrd
pip install xlwt
'''
import xlrd
import json
import requests
from bs4 import BeautifulSoup
import xlrd
# row 是行
# col 是列
file_path = r'接口测试示例.xlsx'
# 拿到book对象 xlrd.open_workbook方法
book = xlrd.open_workbook(file_path)
print(book)
# 拿到表格中对象 按索引获取
sheet = book.sheet_by_index(0)
print(sheet)
# 按名字获取
sheet1 = book.sheet_by_name('接口自动化用例')
print(sheet1)
# 行,列 获取到所有的行和列 sheet.nrows方法, sheet.ncols方法
rows, cols = sheet.nrows, sheet.ncols
print(rows, cols)
# for循环出每一行
for row in range(rows):
print(sheet.row_values(row))
# for循环出每一列
for col in range(cols):
print(sheet.col_values(col))
# 按索引取出行列
print(sheet.row_values(2))
print(sheet.col_values(0))
print(sheet.cell(0, 0))
# 将行和列的值拼接在一起 组合成[{},{}]的样式 zip拉在一起 dict转一下
l = []
title = sheet.row_values(0)
for i in range(1, rows):
l.append(dict(zip(title, sheet.row_values(i))))
print(l)
def get_excel_data():
file_path = r'接口测试示例.xlsx'
# 获取到book对象
book = xlrd.open_workbook(file_path)
# print(book)
# 获取sheet对象
sheet = book.sheet_by_index(0)
# sheet = book.sheet_by_name('接口自动化用例')
# sheets = book.sheets() # 获取所有的sheet对象
rows, cols = sheet.nrows, sheet.ncols
l = []
# print(sheet.row_values(0))
title = sheet.row_values(0)
# print(title)
# 获取其他行
for i in range(1, rows):
# print(sheet.row_values(i))
l.append(dict(zip(title, sheet.row_values(i))))
return l
r = requests.get('https://www.cnblogs.com/Neeo/articles/11667962.html')
s = BeautifulSoup(r.text, 'html.parser')
print(s.find('title').text)
2.uti
ExcelHandler
'''
关于Excel表的操作
'''
import xlrd
from settings import conf
class ExcelHandler(object):
@property
def get_excel_data(self):
# 获取到book对象
book = xlrd.open_workbook(conf.TEST_CASE_PATH)
# print(book)
# 获取sheet对象
sheet = book.sheet_by_index(0)
# sheet = book.sheet_by_name('接口自动化用例')
# sheets = book.sheets() # 获取所有的sheet对象
rows, cols = sheet.nrows, sheet.ncols
l = []
# print(sheet.row_values(0))
title = sheet.row_values(0)
# print(title)
# 获取其他行
for i in range(1, rows):
# print(sheet.row_values(i))
l.append(dict(zip(title, sheet.row_values(i))))
return l
RequestsHandler.py
'''
请求相关
'''
import json
import requests
from bs4 import BeautifulSoup
from uti.LoggerHandler import logger
class RequestHandler(object):
def __init__(self, case):
self.case = case
try:
self.case_expect = json.loads(self.case['case_expect'])
except:
self.case_expect = self.case['case_expect']
@property
def get_response(self):
""" 获取请求结果 """
response = self.send_request()
return response
def send_request(self):
""" 发请求 """
try:
response = requests.request(
method=self.case['case_method'],
url=self.case['case_url'],
params=self._check_params()
)
content_type = response.headers['Content-Type']
content_type = content_type.split(";")[0].split('/')[-1] if ';' in content_type else \
content_type.split("/")[-1]
if hasattr(self, '_check_{}_response'.format(content_type)):
response = getattr(self, '_check_{}_response'.format(content_type))(response)
else:
raise '返回类型为: {}, 无法解析'.format(content_type)
except:
logger().error({'response': "请求发送失败,详细信息: url={}".format(self.case['case_url'])})
return {'response': "请求发送失败,详细信息: url={}".format(self.case['case_url'])}, self.case['case_expect']
return response
def _check_json_response(self, response):
""" 处理json类型的返回值 """
response = response.json() # {'success': True}
for key in self.case_expect:
if self.case_expect[key] != response[key]: # 用例执行失败的
return {key: self.case_expect[key]}, {key: response[key]}
else: # 执行成功
logger("发送请求").info('{} 执行成功'.format(self.case['case_url']))
return {key: self.case_expect[key]}, {key: response[key]}
def _check_html_response(self, response):
""" 校验html类型的数据"""
soup_obj = BeautifulSoup(response.text, 'html.parser')
title = soup_obj.find('title').text
return title, self.case_expect
def _check_params(self):
""" 整理参数 """
if self.case['case_params']:
"""
做扩展
"""
pass
else:
return {}
AllureHandler
import subprocess
from settings import conf
class AllureHandler(object):
def execute_command(self):
import time
time.sleep(1)
subprocess.call(conf.ALLURE_COMMAND, shell=True)
EmailHandler
'''
发邮件
'''
import smtplib
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
from email.header import Header
from settings import conf
from uti.LoggerHandler import logger
class EmailHandler(object):
def read_report(self):
f = open(conf.TEST_CASE_REPORT_PATH, 'rb')
return f.read()
def send_email(self):
""" 发送邮件 """
# 第三方 SMTP 服务
mail_host = "smtp.qq.com" # 设置服务器
mail_user = "xxx@qq.com" # 用户名
mail_pass = "xxx" # 口令
# 获取口令地址 https://www.cnblogs.com/zhangshan33/p/11943755.html
# 设置收件人和发件人
sender = 'xxx@qq.com'
receivers = ['xxx@qq.com', ] # 接收邮件,可设置为你的QQ邮箱或者其他邮箱
# 创建一个带附件的实例对象
message = MIMEMultipart()
# 邮件主题、收件人、发件人
subject = '请查阅--测试报告' # 邮件主题
message['Subject'] = Header(subject, 'utf-8')
message['From'] = Header("{}".format(sender), 'utf-8') # 发件人
message['To'] = Header("{}".format(';'.join(receivers)), 'utf-8') # 收件人
# 邮件正文内容 html 形式邮件
send_content = self.read_report() # 获取测试报告
html = MIMEText(_text=send_content, _subtype='html', _charset='utf-8') # 第一个参数为邮件内容
# 构造附件
att = MIMEText(_text=send_content, _subtype='base64', _charset='utf-8')
att["Content-Type"] = 'application/octet-stream'
file_name = 'report.html'
att["Content-Disposition"] = 'attachment; filename="{}"'.format(file_name) # # filename 为邮件附件中显示什么名字
message.attach(html)
message.attach(att)
try:
smtp_obj = smtplib.SMTP()
smtp_obj.connect(mail_host, 25) # 25 为 SMTP 端口号
smtp_obj.login(mail_user, mail_pass)
smtp_obj.sendmail(sender, receivers, message.as_string())
smtp_obj.quit()
logger().info("邮件发送成功")
except smtplib.SMTPException:
logger().error("Error: 无法发送邮件")
LoggerHandler
import logging
from settings import conf
class LoggerHandler:
""" 日志操作 """
_logger_level = {
'debug': logging.DEBUG,
'info': logging.INFO,
'warning': logging.WARNING,
'error': logging.ERROR,
'critical': logging.CRITICAL
}
def __init__(self, log_name, file_name, logger_level, stream_level='info', file_level='warning'):
self.log_name = log_name
self.file_name = file_name
self.logger_level = self._logger_level.get(logger_level, 'debug')
self.stream_level = self._logger_level.get(stream_level, 'info')
self.file_level = self._logger_level.get(file_level, 'warning')
# 创建日志对象
self.logger = logging.getLogger(self.log_name)
# 设置日志级别
self.logger.setLevel(self.logger_level)
if not self.logger.handlers:
# 设置日志输出流
f_stream = logging.StreamHandler()
f_file = logging.FileHandler(self.file_name)
# 设置输出流级别
f_stream.setLevel(self.stream_level)
f_file.setLevel(self.file_level)
# 设置日志输出格式
formatter = logging.Formatter(
"%(asctime)s %(name)s %(levelname)s %(message)s"
)
f_stream.setFormatter(formatter)
f_file.setFormatter(formatter)
self.logger.addHandler(f_stream)
self.logger.addHandler(f_file)
@property
def get_logger(self):
return self.logger
def logger(log_name='接口测试'):
return LoggerHandler(
log_name=log_name,
logger_level=conf.LOG_LEVEL,
file_name=conf.LOG_FILE_NAME,
stream_level=conf.LOG_STREAM_LEVEL,
file_level=conf.LOG_FILE_LEVEL
).get_logger
if __name__ == '__main__':
logger().debug('aaaa')
logger().info('aaaa')
logger().warning('aaaa')
3.scripts
test_case
import pytest
import allure
from uti.ExcelHandler import ExcelHandler
from uti.RequestHandler import RequestHandler
from uti.AllureHandler import AllureHandler
from uti.EmailHandler import EmailHandler
'''
1. 拿到Excel数据
2. 发请求
3. 生成测试用例报告
4. 发邮件
5. 断言
'''
class Test_case(object):
@pytest.mark.parametrize('case', ExcelHandler().get_excel_data)
def test_case(self, case):
""" 执行断言 """
# print(case)
# 发请求
response = RequestHandler(case).get_response
# 制作 allure 报告
allure.dynamic.title(case['case_project'])
allure.dynamic.description('<font color="red">请求URL:</font>{}<br />'
'<font color="red">期望值:</font>{}'.format(case['case_url'], case['case_description']))
allure.dynamic.feature(case['case_project'])
allure.dynamic.story(case['case_method'])
assert response[0] == response[1]
def teardown_class(self):
""" 执行alllure命令 """
AllureHandler().execute_command()
# 发邮件
EmailHandler().send_email()
4.settings
conf.py
import os
import datetime
BASE_PATH = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
# 脚本路径
file_path = '接口测试示例.xlsx'
TEST_CASE_PATH = os.path.join(BASE_PATH, 'data', file_path)
# 报告路径
TEST_CASE_REPORT_PATH = os.path.join(BASE_PATH, 'report', 'report.html')
# CASE_METHOD = 'case_method'
# ------------ allure 相关配置 -----------
result_path = os.path.join(BASE_PATH, 'report', 'result')
allure_html_path = os.path.join(BASE_PATH, 'report', 'allure_html')
ALLURE_COMMAND = 'allure generate {} -o {} --clean'.format(result_path, allure_html_path)
# ---------------- 日志相关 --------------------
# 日志级别
LOG_LEVEL = 'debug'
LOG_STREAM_LEVEL = 'debug' # 屏幕输出流
LOG_FILE_LEVEL = 'info' # 文件输出流
# 日志文件命名
LOG_FILE_NAME = os.path.join(BASE_PATH, 'logs', datetime.datetime.now().strftime('%Y-%m-%d') + '.log')
if __name__ == '__main__':
print(TEST_CASE_PATH)
5.pytest.ini
[pytest]
addopts = -s -v --html=report/report.html --alluredir ./report/result
testpaths = ./scripts
python_files = test_*.py
python_classes = Test*
python_functions = test_*
6.run.py
import pytest
if __name__ == '__main__':
pytest.main()
7.直接右击运行run.py文件
8.终端运行allure
allure generate report/result -o report/allure_html --clean
生成的测试报告
用浏览器打开
要发送的报告
用浏览器打开
9.邮箱收到的文件