python 接口自动化实战
项目框架:
安装pytest: pip install pytest
安装pytest-html :pip install pytest-html
安装pytest-allure :pip install pytest-allure
安装pytest-rerunfailures :pip install pytest-rerunfailures (执行失败后继续执行指定的次数 --reruns 2)
下载allure: https://www.cnblogs.com/desireyang/p/12517269.html
注意:用例保存为Microsoft excel 切勿保存为wps加密的,python读取时可能会报错
执行方式:pytest
send_request.py
import requests import json from utils.log_handle import logger class SendRequest(object): def __init__(self, case): self.method = case["method"] if case else None self.url = case["url"] if case else None self.description = case["description"] if case["description"] else None self.param = json.loads(case["param"], strict=False) if case["param"] else None self.headers = json.loads(case["headers"], strict=False) if case["headers"] else None self.data = json.loads(case["data"], strict=False) if case["data"] else None self.except_result = json.loads(case["except_result"], strict=False) if case["except_result"] else None self.Pass = True self.row = case["number"] if case["number"] else None # 处理是get还是post请求 def request_handle(self): if self.method == "get": return self.get_request() else: self.post_request() # 发送get请求 def get_request(self): rep = requests.get(url=self.url, headers=self.headers, params=self.param) real_resutl = rep.json() return self.assert_result(real_resutl) # 发送post def post_request(self): rep = requests.post(url=self.url, headers=self.headers, data=self.param, ) real_result = rep.json() return self.assert_result(real_result) # 断言返回结果 def assert_result(self, real_result): try: assert real_result == self.except_result logger().info("%s 断言成功" % self.description) except: self.Pass = False logger().info("%s 断言失败,预计结果:%s,实际结果:%s" % (self.description,self.except_result,real_result)) raise AssertionError finally: from cases.bai_api import execel # 将真实结果写入execel execel.writeexecel(self.row, 9, str(real_result)) # 是否通过 execel.writeexecel(self.row, 10, self.Pass) execel.saveexecel() return real_result, self.Pass
bai_api.py
import json import os import pytest from api.send_request import SendRequest from config import caseexecel_name, ALLURE_COMMAND from utils.email_handle import EmailHandler from utils.excel_handle import ExecelHandle from utils.log_handle import logger execel = ExecelHandle(caseexecel_name) class TestBaiDu(): @pytest.mark.parametrize("case", execel.readexecel()) def test_baidu(self, case): logger().info("开始执行用例%s" % case["number"]) # 执行api 请求 SendRequest(case).request_handle() # 生成allure报告 try: # popen可以执行cmd命令 os.popen(ALLURE_COMMAND) logger().warning("allure报告生成成功") except: logger().warning("输入报告错误") #发送邮件 try: EmailHandler().send_email() logger().warning("邮件报告生成功") except: logger().warning("邮件报告生成失败") if __name__ == '__main__': pytest.main(["-s", "./bai_api.py"])
students.xls
这里建议保存为Microsoft excel 切勿保存为wps加密的,会报错
生成的allure 报告:
生成的pytest-html 报告:
将测试结果填入表格:
email_handle.py
import smtplib from email.mime.multipart import MIMEMultipart from email.mime.text import MIMEText from email.utils import formataddr from config import PYTEST_HTML_PATH from utils.log_handle import logger class EmailHandler(): def __init__(self): self.my_sender = 'kx113578@163.com' # 发件人邮箱账号 self.my_pass = '##########' # 发件人邮箱密码,此处为授权码 self.my_user = '1135790024@qq.com' # 收件人邮箱账号,我这边发送给自己 def email_content(self): with open(PYTEST_HTML_PATH, "rb") as f: return f.read() def send_email(self): f = self.email_content() ret = True try: # 创建一个带附件的实例 message = MIMEMultipart() msg = MIMEText('这是接口测试结果……', 'plain', 'utf-8') # 这里面写的是发送的是html message['From'] = formataddr(["ke1", self.my_sender]) # 括号里的对应发件人邮箱昵称、发件人邮箱账号 message['To'] = formataddr(["ke2", self.my_user]) # 括号里的对应收件人邮箱昵称、收件人邮箱账号 message['Subject'] = "美丽商城接口测试" # 邮件的主题,也可以说是标题 message.attach(msg) # 邮件正文内容 # 添加附件 att1 = MIMEText(f, 'html', 'utf-8') att1["Content-Type"] = 'application/octet-stream' # 这里的filename可以任意写,写什么名字,邮件中显示什么名字 att1["Content-Disposition"] = 'attachment; filename="report.html"' message.attach(att1) # 发送邮件 server = smtplib.SMTP_SSL("smtp.163.com", 465) # 发件人邮箱中的SMTP服务器,端口是25 server.login(self.my_sender, self.my_pass) # 括号中对应的是发件人邮箱账号、邮箱密码 server.sendmail(self.my_sender, [self.my_user, ], message.as_string()) # 括号中对应的是发件人邮箱账号、收件人邮箱账号、发送邮件 server.quit() # 关闭连接 except Exception: # 如果 try 中的语句没有执行,则会执行下面的 ret=False ret = False logger().info("邮件发送失败") logger().info("邮件发送成功") return ret if __name__ == '__main__': EmailHandler().send_email()
execel_handle.py
import xlrd from xlutils.copy import copy from config import caseexecel_name, result_execel class ExecelHandle(): # 读取excel用例 def __init__(self,name,sheet="Sheet1"): self.name = name self.rb = xlrd.open_workbook(self.name) # 不能多次copy,否则只保存一次结果 self.wb = copy(self.rb) self.ws = self.wb.get_sheet(0) self.sheet=sheet def readexecel(self): self.rb = xlrd.open_workbook(self.name) rt = self.rb.sheet_by_name(self.sheet) self.list1=[] for row in range(0,rt.nrows): if row != 0: self.list1.append(dict(zip(rt.row_values(0),rt.row_values(row)))) return self.list1 def writeexecel(self,row,col,content): self.ws.write(row,col,content) def saveexecel(self): self.wb.save(result_execel) if __name__ == '__main__': ExecelHandle(caseexecel_name).readexecel()
log_handle.py
import logging import config 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, encoding="utf-8") # 设置输出流级别 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=config.LOG_LEVEL, file_name=config.LOG_FILE_NAME, stream_level=config.LOG_STREAM_LEVEL, file_level=config.LOG_FILE_LEVEL ).get_logger if __name__ == '__main__': logger().debug('aaaa') logger().info('aaaa') logger().warning('aaaa')
config.py
import os import datetime BASENAEM= os.path.dirname(os.path.abspath(__file__)) caseexecel_name = os.path.join(BASENAEM,"data","students.xls") result_execel = os.path.join(BASENAEM,"results","students_result.xls") result_path = os.path.join(BASENAEM, 'reports', 'result') # ------------ allure 相关配置 ----------- allure_html_path = os.path.join(BASENAEM, 'reports', 'allure_html') ALLURE_COMMAND = 'allure generate {} -o {} --clean'.format(result_path, allure_html_path) #pytest-html 生成的报告 PYTEST_HTML_PATH = allure_html_path = os.path.join(BASENAEM, 'reports', 'report1.html') #日志配置 LOG_LEVEL = 'debug' LOG_STREAM_LEVEL = 'debug' # 屏幕输出流 LOG_FILE_LEVEL = 'info' # 文件输出流 # 日志文件命名 LOG_FILE_NAME = os.path.join(BASENAEM, 'log', datetime.datetime.now().strftime('%Y-%m-%d') + '.log') pytest.ini [pytest] addopts = -s --html ./reports/report1.html --alluredir ./reports/result --reruns 2 test_paths = ./cases python_classes = Test* python_files = bai*.py python_functions = test*
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?