需求:
-
从Excel中读取记录行(每一行就是一个API(url,请求类型,名称,描述,参数,预期值))
-
使用参数化对每一次的请求,要使用requests发请求,获取请求结果,从结果中提取字段,跟预期值做断言,
-
使用allure生成测试报告
-
为每一个请求用例添加title和description
-
-
将测试报告发邮件
-
问题:发邮件的时候,能发文件夹吗?答案是不能,
-
解决办法是:将allure报告文件夹打包成zip
-
发送zip文件
-
-
在你认为的关键点添加上log日志
-
请求的时候
-
断言的时候
-
可选打包的时候
-
读Excel的时候
-
-
为了解耦合,需要遵循软件开发规范
-
数据文件夹
-
配置文件夹
-
脚本文件夹
-
等等
-
-
如果写的困难得,可以在一个文件中实现
用到的知识点:
-
requests
-
pytest
-
Excel表格操作
-
发邮件
-
日志功能
-
项目开发规范的目录应用
-
allure
-
title知识点
-
一、1.把软件开发规范的相关目录建立起来:
二、配置settings:
import osimport datetimeimport shutilimport sysbase_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))# print(base_dir) #D:\s27\day69\nb# 定义文件名字:file_name = "接口测试示例.xlsx"# 路径拼接:file_path = os.path.join(base_dir, "data", file_name)# print(file_path) #D:\s27\day69\nb\data\接口测试示例.xlsx# ---------------- 日志相关 --------------------# 日志级别LOG_LEVEL = 'debug'# 屏幕输出流LOG_STREAM_LEVEL = 'debug'# 文件输出流LOG_FILE_LEVEL = 'info'# 日志文件命名LOG_FILE_NAME = os.path.join(base_dir, 'logs', datetime.datetime.now().strftime('%Y-%m-%d') + '.log')# allure报告相关:report_path = os.path.join(base_dir, "report")result_path = os.path.join(base_dir, "report", "result")allure_html_path = os.path.join(base_dir, "report", "allure_html")command = "allure generate {} -o {} --clean".format(result_path, allure_html_path)# 要打包的根目录:zip_case_result_path = allure_html_path# 要打包的文件名:zip_file_name = "allure_report.zip"# 将打包文件存放哪去:zip_save_path = report_path# 邮箱相关:# 第三方 SMTP 服务# 设置服务器mail_host = "smtp.qq.com"# 用户名mail_user = "1320685524@qq.com"# 获取授权码mail_pass = "mpaocydzpzfjidge"# 发件人账号sender = '1320685524@qq.com'# 接收邮件,可设置为你的QQ邮箱或者其他邮箱和多个收件人receivers = ['1320685524@qq.com']# 邮件主题:subject = 'Python发送带附件的邮件示例'# 邮件正文内容send_content = '这是今天的测试用例报告,请下载附件并使用pycharm打开'# 邮件附件路径:send_file_path = os.path.join(zip_save_path, zip_file_name)send_file_name = zip_file_nameif __name__ == '__main__': # print(send_file_path) pass # os.remove(report_path) # shutil.rmtree(report_path) #查看路径: # print(sys.path) #查看包: # print(sys.modules)
三、配置pytest.ini:
[pytest]addopts = -s -v --alluredir ./report/resulttestpaths = ./scriptspython_files = test_*.pypython_classes = Test*python_functions = test_*
四、data数据文件接口测试示例.xlsx:
五、logs日志文件:
六、report报告:
七、scripts测试用例:
import pytestimport allurefrom utils.ExcelHandler import Excelfrom utils.LogHandler import loggerfrom utils.RequestHandler import RequestHandlerfrom utils.AllureHandler import AllureHandler@pytest.mark.parametrize("d", Excel().get_excel_data())def test_case(d): # print(d) result = RequestHandler(d).get_response() # logger().info(d) allure.dynamic.title(d["case_project"]) allure.dynamic.description("<font color='red'>请求的URL:</font>{}<hr />" "<font color='red'>请求的类型:</font>{}<hr />" "<font color='red'>实际值:</font>{}<hr />" "<font color='red'>预期值:</font>{}<hr />" "".format(d["case_url"], d["case_method"], result[1], result[0], )) assert result[0] == result[1] # def teardown_module(): # allure_obj = AllureHandler() """生成allure测试报告""" # allure_obj.execute_command() """打包文件""" # allure_obj.zip() """发送邮件""" # allure_obj.send_email()
八、AllureHandler.py报告功能:
"""执行allure命令生成报告打包allure报告发送压缩包"""import os# 导入压缩文件用的模块import zipfileimport smtplibimport shutilimport timefrom email.mime.text import MIMETextfrom email.mime.multipart import MIMEMultipartfrom email.header import Headerfrom conf import settingsfrom utils.LogHandler import loggerfrom subprocess import call, Popenclass AllureHandler(object): def __init__(self): """清空report目录,以便后续方便操作""" # shutil.rmtree(settings.report_path) pass def execute_command(self): """执行allure命令""" # time.sleep(5) logger().info("执行生成allure报告:{}".format(settings.command)) # os.system(settings.command) # shell=True是可以识别字符串的命令: call(settings.command, shell=True) def zip(self): """打包allure报告""" # 日志: logger().info("打包文件名:{},打包到:{}".format(settings.zip_file_name, settings.zip_save_path)) # 要压缩文件夹的根路径并拼接: base_dir = settings.zip_case_result_path zip_file_name = settings.zip_file_name # 保存打包文件的路径: f = zipfile.ZipFile(os.path.join(settings.zip_save_path, zip_file_name), 'w', zipfile.ZIP_DEFLATED) for dir_path, dir_name, file_names in os.walk(base_dir): # 要是不replace,就从根目录开始复制 file_path = dir_path.replace(base_dir, '') # 实现当前文件夹以及包含的所有文件 file_path = file_path and file_path + os.sep or '' for file_name in file_names: f.write(os.path.join(dir_path, file_name), file_path + file_name) f.close() def send_email(self): """将打包的allure文件发送给指定邮箱""" logger().info("正在向{}发送邮件,请稍后.......".format(settings.receivers)) # 第三方SMTP服务 # 设置服务器 mail_host = settings.mail_host # 用户名 mail_user = settings.mail_user # 获取授权码 mail_pass = settings.mail_pass # 发件人账号 sender = settings.sender # 接收邮件,可设置为你的QQ邮箱或者其他邮箱和多个收件人 receivers = settings.receivers # 创建一个带附件的实例 message = MIMEMultipart() # 发件人 message['From'] = Header("我是发件人", 'utf-8') # 收件人 message['To'] = Header("我是收件人", 'utf-8') # 邮件主题 subject = settings.subject message['Subject'] = Header(subject, 'utf-8') # 邮件正文内容 send_content = settings.send_content content_obj = MIMEText(send_content, 'plain', 'utf-8') message.attach(content_obj) # 构造附件1,发送当前目录下的文件 att1 = MIMEText(open(settings.send_file_path, 'rb').read(), "base64", "utf-8") # 基于流的模式 att1["Content-Type"] = "application/octet-stream" # 文件描述、filename是附件中显示的名字 att1["Content-Disposition"] = "attachment;filename = '{}'".format(settings.send_file_name) message.attach(att1) # 构造附件2,发送当前目录下的t2.py文件 # att2 = MIMEText(open("t2.py", "rb").read(), "base64", "utf-8") # # # 基于流的模式 # att2["Content-Type"] = "application/octet-stream" # # # 文件描述、filename是附件中显示的名字 # att2["Content-Disposition"] = "attachment;filename = 't2.py'" # message.attach(att2) try: smtpObj = smtplib.SMTP() # 25 为 SMTP 端口号 smtpObj.connect(mail_host, 25) smtpObj.login(mail_user, mail_pass) smtpObj.sendmail(sender, receivers, message.as_string()) logger().info("向{}发送邮件成功".format(settings.receivers)) except smtplib.SMTPException as e: logger().error("向{}发送邮件失败,可能的原因:{}".format(settings.receivers, e))
九、ExcelHandler.py文件表功能:
import xlrdfrom conf import settingsfrom utils.LogHandler import loggerclass Excel(object): def get_excel_data(self): """获取excel表格数据""" logger().info("读取Excel表格{} {}".format(settings.file_name,settings.file_path)) book = xlrd.open_workbook(filename=settings.file_path) sheet = book.sheet_by_index(0) # print(sheet.nrows) #8 title = sheet.row_values(0) l = [] for row in range(1, sheet.nrows): # print(sheet.row_values(row)) l.append(dict(zip(title, sheet.row_values(row)))) # print(l) return lif __name__ == '__main__': Excel().get_excel_data()
十、LogHandler.py日志功能:
import loggingfrom conf import settingsclass 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): """伪装成属性返回logger对象""" return self.loggerdef logger(log_name='接口测试'): return LoggerHandler( log_name=log_name, logger_level=settings.LOG_LEVEL, file_name=settings.LOG_FILE_NAME, stream_level=settings.LOG_STREAM_LEVEL, file_level=settings.LOG_FILE_LEVEL ).get_loggerif __name__ == '__main__': logger().debug('aaaa') logger().info('aaaa') logger().warning('aaaa')
十一、RequestHandler.py请求功能:
import requestsimport jsonfrom utils.LogHandler import loggerfrom bs4 import BeautifulSoupclass RequestHandler(object): def __init__(self, d): self.d = d def get_response(self): """获取请求结果""" return self.send_msg() def _response_application(self, response): """校验json类型的返回""" response = response.json() expect = json.loads(self.d.get("case_expect")) for k in expect: if expect[k] != response.get(k, "没有这个key:{}".format(k)): return {k: expect[k]}, {k: response.get(k, "没有这个key:{}".format(k))} return {k: expect[k]}, {k: response.get(k, "没有这个key:{}".format(k))} def _response_text(self, response): """校验文本类型的返回""" # response.title() soup = BeautifulSoup(response.text, "html.parser") title = soup.find(name="title").text # print(title.text) logger().info("文本类型的请求结果,预期值:{} | 实际值:{}".format(self.d.get("case_expect"), title)) return title, self.d.get("case_expect") def send_msg(self): """发请求""" logger().info("请求URL:{},类型:{}".format(self.d.get("case_url"), self.d.get("case_method"))) response = requests.request( method=self.d.get("case_method"), url=self.d.get("case_url"), # params=self._check_params(), # data=self._check_data() ) header = response.headers["Content-Type"].split("/", 1)[0] # header = _response_application if hasattr(self, "_response_{}".format(header)): a = getattr(self, "_response_{}".format(header)) result = a(response) logger().info("预期值:{} 实际值:{}".format(result[0], result[1])) return result def _check_params(self): """检查请求参数""" params = self.d.get("case_params") if params: return {} else: return {} def _check_data(self): """ 检查请求参数 """ params = self.d.get("case_params") if params: return {} else: return {}if __name__ == '__main__': # r1 = requests.get("https://www.cnblogs.com/Neeo/articles/10951734.html") # print(r1.text) # print(r1.headers) # print(r1.title) pass # r2 = requests.get("https://www.v2ex.com/api/site/info.json") # print(r2.headers) # r3 = requests.post("https://cnodejs.org/api/v1/message/mark_all") # print(r3.headers)
十二、run.py程序执行入口:
import shutilimport pytestfrom utils.AllureHandler import AllureHandlerfrom conf import settingsif __name__ == '__main__': pytest.main() # shutil.rmtree(r"D:\s27\day69包含log日志功能\nb\report") a = AllureHandler() a.execute_command() a.zip() a.send_email()
十三、效果展示:
来源:https://www.cnblogs.com/zhang-da/p/12305082.html