Python接口自动化框架+测试报告+发送邮箱(详细)

本文涉及的产品
日志服务 SLS,月写入数据量 50GB 1个月
简介: Python接口自动化框架+测试报告+发送邮箱(详细)unittest+requests接口自动化测试

Python环境

python版本:3.8

依赖包:requests,unittest,HTMLTestReportCN,ddt;

工具:pycharm 2020.1;

python环境设置参考:https://www.runoob.com/python/python-install.html

安装依赖包:pip install 包名(如果找不到或者网络不好可以使用下面这一段下载)

pip install 包 -i http://pypi.douban.com/simple/ --trusted-host pypi.douban.com

pycharm可以在file>settings>Python Interpreter里面点加号增加,也会有下载失败的情况,目前还没找到原因,推荐使用豆瓣的下载。

pythoninterpreter

HTMLTestReportCN:https://blog.csdn.net/lssrain/article/details/80109214

框架设计

base

创建Pythonpackage类型的base包,用于存储基类,各种请求方法,这里的封装方式比较直接明了,根据传入参数调用对应的请求方式,相信使用过requests的都比较容易理解。

import requests
import json

class Base():
    def method_post(self,url,params =None,data = None,headers = None,files = None):
        return requests.post(url = url,params = params, data = data,headers = headers,files =files)
    def method_get(self,url,params = None,data = None,headers = None,files = None ):
        return  requests.get(url = url,params = params,data = data,headers = headers,files = files)
    def method_put(self,url,params = None,data = None, headers = None,files = None):
        return  requests.put(url = url,params = params,data = data,headers = headers,files = files)
    def method_delete(self,url,params = None,data = None,headers = None,files = None):
        return  requests.delete(url = url, params = params,data = data,headers = headers,files =files)

    def requests_type(self,method,url,params = None,data = None,headers = None,files = None):
        if method == 'post' or method == 'POST':
            return self.method_post(url = url,params = params,data = data,headers = headers,files = files )
        elif method == 'get' or method == 'GET':
            return self.method_get(url = url,params = params,data = data,headers = headers,files =files)
        elif method == 'put' or method == 'PUT':
            return self.method_put(url = url,params = params,data = data,headers = headers,files = files)
        elif method == 'delete' or method == 'DELETE':
            return  self.method_delete(url = url,params = params,data = data,headers = headers,files = files)
        else: return print("不支持该方法!!")

common

同样创建Pythonpackage类型的common包,用于存放各种共用方法。

目前这里存放的有创建对应文件下年月日文件夹,封装日志,读取excel,读取json文件,生成测试报告以及发送邮件等方法

基础文件路径(需要提前在目录中创建好):

base_pa = os.path.abspath(os.path.dirname(os.path.dirname(__file__)))
base_path = base_pa.replace('\\','/') # 生成的路径用的是'\',后面读取文件的时候会找不到,所以换成'/'
log_path = base_path + '/' + 'report' + '/' + 'logs' # 日志路径
report_html = base_path + '/' + 'report' + '/' + 'html' # 报告路径
read_xlrd = base_path + '/' + 'data' #用例路径

创建Commom类,封装各种方法。

考虑到日志和报告会比较多,每次发送报告只需要当天的就可以,所以用一个方法在传入的路径下创建对应的年月日文件夹(如果路径已存在,不会重复创建)

import os,time
class Commom():
    def mkdir_path(self,base_page):
        local_time1 = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(time.time()))
        year = time.strftime('%Y', time.localtime(time.time())) # 获取年份
        month = time.strftime('%m', time.localtime(time.time())) # 获取日期
        day = time.strftime('%d', time.localtime(time.time())) # 获取日期
        base_page = base_page
        fileyear = base_page + '/' + year
        filemonth = fileyear + '/' + month
        fileday = filemonth + '/' + day
        if not os.path.exists(fileyear):
            os.mkdir(fileyear)
            os.mkdir(filemonth)
            os.mkdir(fileday)
        else:
            if not os.path.exists(filemonth):
                os.mkdir(filemonth)
                os.mkdir(fileday)
            else:
                if not os.path.exists(fileday):
                    os.mkdir(fileday)
        file_years = fileyear.replace('\\', '/')
        file_days = fileday.replace('\\', '/')
        file_months = filemonth.replace('\\', '/')  # 将路径中'\'转译为‘/’
        return file_days,file_months,file_years # 路径全部返回,一般只用到日的路径

封装日志方法:

    def get_logs(self, path=log_path):
        import logging, time
        logs = logging.getLogger()
        logs.setLevel(logging.DEBUG)
        path1 = self.mkdir_path(path)
        path = path1[0] + '/' + time.strftime('%Y-%m-%d-%H-%M-%S') + '.log'
        write_file = logging.FileHandler(path, 'a+', encoding='utf-8')
        write_file.setLevel(logging.DEBUG)
        set_logs = logging.Formatter('%(asctime)s -- %(funcName)s - %(levelname)s - %(message)s')
        write_file.setFormatter(set_logs)
        pycharm_text = logging.StreamHandler()
        pycharm_text.setFormatter(set_logs)
        if not logs.handlers:
            logs.addHandler(write_file)
            logs.addHandler(pycharm_text)
        return logs

读取Excel,转换为字典格式:

    def ReadExcelTypeDict(self, file_name, path=read_xlrd):
        path = path + '/' + file_name
        import xlrd
        work_book = xlrd.open_workbook(path)  # 打开excel表格
        sheets = work_book.sheet_names()  # 读取所有的sheet页
        DataList = []
        for sheet in sheets:
            sheets = work_book.sheet_by_name(sheet)
            nrows = sheets.nrows
            for i in range(0, nrows):
                values = sheets.row_values(i)
                DataList.append(values)
        title_list = DataList[0]
        cotent_list = DataList[1:]
        new_list = []
        for content in cotent_list:
            dic = {}
            for i in range(len(content)):
                dic[title_list[i]] = content[i]
            new_list.append(dic)
        return new_list

生成HTML报告:

    def GETHtmlResult(self, suite, title, path=report_html):
        import HTMLTestReportCN
        path = self.mkdir_path(path)
        fileday = (path[0])
        path = fileday + '/' + time.strftime('%Y-%m-%d-%H-%M-%S') + '.html'
        #print(path)
        with open(path,'wb+') as f:
            run = HTMLTestReportCN.HTMLTestRunner(stream=f, description='用户相关接口测试报告', tester='mingzhu', title=title)
            run.run(suite)

发送邮件这里因为只需要发送即可,所以这里的方法是直接发送qq邮箱,并且需要在邮箱中开启SMTP服务,生成授权码加在代码里才可以发送(也可以使用密码登录,可以自行选择)

qq邮箱

#coding:utf-8
import  time
import smtplib

from email import  encoders
from email.header import Header
from email.mime.base import MIMEBase
from email.mime.text import MIMEText
from email.utils import parseaddr,formataddr
from email.mime.multipart import  MIMEMultipart
from common import  commons as common
import os

def _foemat_addr(s):
    name,addr = parseaddr(s)
    return  formataddr((Header(name,'utf-8').encode(),addr))

def send_mail():
    from_addr = "发送人邮箱"
    recive_addr = ['收件人邮箱','收件人邮箱']
    smtp_server = "smtp.qq.com"
    License_Key = "登录授权码"
    locatime = time.strftime('%Y-%m-%d %H:%M:%S')
    logs = common.Commom().get_logs()
    info =  '''
            这是今日的测试报告!!!
            邮件需要下载后打开!!!
    '''
    content = f'''
        Dear All:
                {info}
                邮件发送时间:{locatime}
'''
    msg = MIMEMultipart()
    body = MIMEText(content.encode(),'plain','utf-8')
    msg.attach(body)
    file_path = os.path.abspath(common.Commom().mkdir_path(os.path.abspath(common.report_html))[0])
    all_file = os.listdir(file_path) # 读取当天所有测试报告
    for file in all_file:
        file_code = file_path + '/' + file
        attachment = MIMEBase('application','octet-stream')
        attachment.set_payload(open(file_code,'rb').read())
        encoders.encode_base64(attachment)
        attachment.add_header('Content-Disposition','attachment',filename = file_code)
        msg.attach(attachment)
    msg['From'] = _foemat_addr('发件人名称 <%s>' % from_addr)
    msg['To'] = _foemat_addr('收件人名称 <%s>'% recive_addr)
    msg['Subject'] = Header('邮件标题','utf-8').encode()
    server = smtplib.SMTP_SSL(smtp_server)
    server.connect(smtp_server,465)
    server.login(from_addr,License_Key)
    server.sendmail(from_addr,recive_addr, msg= msg.as_string())
    logs.debug('收件人是:'+recive_addr[0:]+'发件人是:'+from_addr+' ,邮件发送成功!!!')
    server.quit()

case

case包是我们存放执行测试用例的类和方法的地方,这里分别设计了对比响应码,对比相应文本的断言方式,根据开发接口文档的详细程度自行选择,可以设置多个断言。

#coding:utf-8
import  unittest
import  ddt
import time
import  common.commons as common
import common.send_emails as send_mail
from base.Base_Page import  Base
import json

r = common.Commom().ReadExcelTypeDict('test.xlsx')    #读取具体的EXCEL表格
d = common.Commom().ReadExcelTypeDict('data_test.xlsx')
j = common.Commom().ReadJson('test_login.json')
# print('json内容:',j)
@ddt.ddt        #导入ddt模块
class Testlogin(unittest.TestCase):
    @classmethod
    def setUpClass(cls) -> None:#setupclass类方法,全部开始用例前执行一次
        cls.logs = common.Commom().get_logs() # 导入日志方法
        cls.logs.debug('自动化测试用例开始!!!')

    @classmethod
    def tearDownClass(cls) -> None:
        cls.logs.debug('自动化测试用例结束!!!')

    def setUp(self) -> None:
        self.start_time = time.time()
        self.logs.debug('开始执行本条用例')

    def tearDown(self) -> None:
        end_time = time.time()
        self.logs.debug('本条用例执行完毕,用例执行时间:'+str(self.start_time-end_time))

    @ddt.data(*r)
    # 引入ddt模块,读取拿到的数据
    def test_status(self,pars):  #用例方法名必须以test开头,pars参数为接受的表数据值
        dic = json.loads(pars['body参数值'])  #将参数值转变为json格式
        url = pars['接口地址']      #获取url
        test_name = pars['用例标题']
        header = json.loads(pars['请求头'])
        yuqi = pars['预期结果']
        fs = pars['请求方式']
        self.logs = common.Commom().get_logs()
        result = Base().requests_type(method= fs, url = url,headers = header, data= dic)
        self.logs.debug('result:' + result.text)
        # self.assertEqual(len(result.headers),17,msg='响应头长度不符')
        self.assertEqual(result.status_code,yuqi,msg='响应状态码错误!'+str(yuqi)+'!='+str(result.status_code)) # 实际响应码,预期响应码,不符时返回的错误信息(msg可以为空)

    @ddt.data(*d)
    def test_data(self,pars):
        dic = json.loads(pars["body参数值"])
        url = pars["接口地址"]
        test_name = pars['用例标题']
        header = json.loads(pars['请求头'])
        yuqi = str(pars['预期结果'])
        fs = pars['请求方式']
        self.logs = common.Commom().get_logs()
        self.logs.debug('开始执行本条用例:' + test_name)
        result = Base().requests_type(method=fs, url=url, headers=header, data=dic)
        self.logs.debug('result:' + result.text)
        result1 = result.json()
        #self.logs.debug(result1)
        self.assertEqual(result1['data']['openId'],yuqi,msg='openID比对错误!')

    @ddt.data(*j)
    # @unittest.skipIf(1==1)
    @unittest.skip # 跳过测试用例
    def test_json(self,par):
        par = json.loads(par)
        header = par["headers"]
        url = par["url"]
        body = par["body"]
        fs = par["fs"]
        expect = par["expect"]
        #print(url,body,header)
        self.logs = common.Commom().get_logs()
        result = Base().requests_type(url=url,method = fs,data= body,headers=header)
        self.logs.debug('result:'+result.text)
        result1 = result.json()
        t = result.elapsed     # 响应时间
        # print(t)
        self.assertLess (t,1,msg='响应时间大于1秒')    # lessequal小于等于则返回TRUE
        self.assertGreater(t,1,msg='响应时间小于1秒')  # greaterequal大于等于则返回TRUE
        # 各种断言方法可以选择,全部通过本条用例才算pass
        self.assertEqual(result1['data']['openId'],expect,msg='openId比对错误!')


if __name__ == '__main__':
    load = unittest.TestLoader().loadTestsFromTestCase(Testlogin)   #使用loader加载方式,来寻找所有以test开头的用例
    suite = unittest.TestSuite([load,])     #执行测试用例
    common.Commom().GETHtmlResult(suite,'登录测试用例')   #生成测试报告
    send_mail.send_mail() #发送测试报告到邮箱

data

创建data文件夹,主要用于存放测试用例,,我使用的有excel和json两种,参数和请求头注意使用json格式,预期结果根据代码的断言进行填写

report

case

如有问题欢迎评论,互相学习提升!

相关实践学习
日志服务之使用Nginx模式采集日志
本文介绍如何通过日志服务控制台创建Nginx模式的Logtail配置快速采集Nginx日志并进行多维度分析。
相关文章
|
1月前
|
API Python
【02】优雅草央央逆向技术篇之逆向接口协议篇-以小红书为例-python逆向小红书将用户名转换获得为uid-优雅草央千澈
【02】优雅草央央逆向技术篇之逆向接口协议篇-以小红书为例-python逆向小红书将用户名转换获得为uid-优雅草央千澈
96 1
|
2月前
|
Python
自动化微信朋友圈:Python脚本实现自动发布动态
本文介绍如何使用Python脚本自动化发布微信朋友圈动态,节省手动输入的时间。主要依赖`pyautogui`、`time`、`pyperclip`等库,通过模拟鼠标和键盘操作实现自动发布。代码涵盖打开微信、定位朋友圈、准备输入框、模拟打字等功能。虽然该方法能提高效率,但需注意可能违反微信使用条款,存在风险。定期更新脚本以适应微信界面变化也很重要。
213 61
|
5天前
|
人工智能 自然语言处理 测试技术
AxBench:斯坦福大学推出评估语言模型控制方法的基准测试框架
AxBench 是由斯坦福大学推出,用于评估语言模型可解释性方法的基准测试框架,支持概念检测和模型转向任务,帮助研究者系统地比较不同控制技术的有效性。
35 5
AxBench:斯坦福大学推出评估语言模型控制方法的基准测试框架
|
11天前
|
API 文件存储 数据安全/隐私保护
python 群晖nas接口(一)
这段代码展示了如何通过群晖NAS的API获取认证信息(SID)并列出指定文件夹下的所有文件。首先,`get_sid()`函数通过用户名和密码登录NAS,获取会话ID(SID)。接着,`list_file(filePath, sid)`函数使用该SID访问FileStation API,列出给定路径`filePath`下的所有文件。注意需替换`yourip`、`username`和`password`为实际值。
53 18
|
9天前
|
API Python
python泛微e9接口开发
通过POST请求向指定IP的API注册设备以获取`secrit`和`spk`。请求需包含`appid`、`loginid`、`pwd`等头信息。响应中包含状态码、消息及`secrit`(注意拼写)、`secret`和`spk`字段。示例代码使用`curl`命令发送请求,成功后返回相关信息。
30 5
|
9天前
|
API 文件存储 Python
python 群晖nas接口(二)
这段代码展示了如何通过API将文件上传到群晖NAS。它使用`requests`库发送POST请求,指定文件路径、创建父级目录及覆盖同名文件的参数,并打印上传结果。确保替换`yourip`和`sid`为实际值。
29 2
|
1月前
|
人工智能 运维 Prometheus
AIOpsLab:云服务自动化运维 AI,微软开源云服务 AI 框架,覆盖整个生命周期
AIOpsLab 是微软等机构推出的开源框架,支持云服务自动化运维,涵盖故障检测、根本原因分析等完整生命周期。
146 13
AIOpsLab:云服务自动化运维 AI,微软开源云服务 AI 框架,覆盖整个生命周期
|
1月前
|
人工智能 编解码 自然语言处理
AGUVIS:指导模型实现 GUI 自动化训练框架,结合视觉-语言模型进行训练,实现跨平台自主 GUI 交互
AGUVIS 是香港大学与 Salesforce 联合推出的纯视觉 GUI 自动化框架,能够在多种平台上实现自主 GUI 交互,结合显式规划和推理,提升复杂数字环境中的导航和交互能力。
118 8
AGUVIS:指导模型实现 GUI 自动化训练框架,结合视觉-语言模型进行训练,实现跨平台自主 GUI 交互
|
2月前
|
人工智能 Linux API
PromptWizard:微软开源 AI 提示词自动化优化框架,能够迭代优化提示指令和上下文示例,提升 LLMs 特定任务的表现
PromptWizard 是微软开源的 AI 提示词自动化优化框架,通过自我演变和自我适应机制,迭代优化提示指令和上下文示例,提升大型语言模型(LLMs)在特定任务中的表现。本文详细介绍了 PromptWizard 的主要功能、技术原理以及如何运行该框架。
254 8
PromptWizard:微软开源 AI 提示词自动化优化框架,能够迭代优化提示指令和上下文示例,提升 LLMs 特定任务的表现
|
1月前
|
存储 测试技术 API
pytest接口自动化测试框架搭建
通过上述步骤,我们成功搭建了一个基于 `pytest`的接口自动化测试框架。这个框架具备良好的扩展性和可维护性,能够高效地管理和执行API测试。通过封装HTTP请求逻辑、使用 `conftest.py`定义共享资源和前置条件,并利用 `pytest.ini`进行配置管理,可以大幅提高测试的自动化程度和执行效率。希望本文能为您的测试工作提供实用的指导和帮助。
116 15