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

简介: 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

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

相关实践学习
【涂鸦即艺术】基于云应用开发平台CAP部署AI实时生图绘板
【涂鸦即艺术】基于云应用开发平台CAP部署AI实时生图绘板
相关文章
|
10月前
|
Java 数据处理 索引
(Pandas)Python做数据处理必选框架之一!(二):附带案例分析;刨析DataFrame结构和其属性;学会访问具体元素;判断元素是否存在;元素求和、求标准值、方差、去重、删除、排序...
DataFrame结构 每一列都属于Series类型,不同列之间数据类型可以不一样,但同一列的值类型必须一致。 DataFrame拥有一个总的 idx记录列,该列记录了每一行的索引 在DataFrame中,若列之间的元素个数不匹配,且使用Series填充时,在DataFrame里空值会显示为NaN;当列之间元素个数不匹配,并且不使用Series填充,会报错。在指定了index 属性显示情况下,会按照index的位置进行排序,默认是 [0,1,2,3,...] 从0索引开始正序排序行。
709 0
|
10月前
|
存储 Java 数据处理
(numpy)Python做数据处理必备框架!(一):认识numpy;从概念层面开始学习ndarray数组:形状、数组转置、数值范围、矩阵...
Numpy是什么? numpy是Python中科学计算的基础包。 它是一个Python库,提供多维数组对象、各种派生对象(例如掩码数组和矩阵)以及用于对数组进行快速操作的各种方法,包括数学、逻辑、形状操作、排序、选择、I/0 、离散傅里叶变换、基本线性代数、基本统计运算、随机模拟等等。 Numpy能做什么? numpy的部分功能如下: ndarray,一个具有矢量算术运算和复杂广播能力的快速且节省空间的多维数组 用于对整组数据进行快速运算的标准数学函数(无需编写循环)。 用于读写磁盘数据的工具以及用于操作内存映射文件的工具。 线性代数、随机数生成以及傅里叶变换功能。 用于集成由C、C++
733 1
|
10月前
|
Java 数据挖掘 数据处理
(Pandas)Python做数据处理必选框架之一!(一):介绍Pandas中的两个数据结构;刨析Series:如何访问数据;数据去重、取众数、总和、标准差、方差、平均值等;判断缺失值、获取索引...
Pandas 是一个开源的数据分析和数据处理库,它是基于 Python 编程语言的。 Pandas 提供了易于使用的数据结构和数据分析工具,特别适用于处理结构化数据,如表格型数据(类似于Excel表格)。 Pandas 是数据科学和分析领域中常用的工具之一,它使得用户能够轻松地从各种数据源中导入数据,并对数据进行高效的操作和分析。 Pandas 主要引入了两种新的数据结构:Series 和 DataFrame。
768 0
|
10月前
|
Java 数据处理 索引
(numpy)Python做数据处理必备框架!(二):ndarray切片的使用与运算;常见的ndarray函数:平方根、正余弦、自然对数、指数、幂等运算;统计函数:方差、均值、极差;比较函数...
ndarray切片 索引从0开始 索引/切片类型 描述/用法 基本索引 通过整数索引直接访问元素。 行/列切片 使用冒号:切片语法选择行或列的子集 连续切片 从起始索引到结束索引按步长切片 使用slice函数 通过slice(start,stop,strp)定义切片规则 布尔索引 通过布尔条件筛选满足条件的元素。支持逻辑运算符 &、|。
441 0
|
10月前
|
JSON API 数据安全/隐私保护
Python采集淘宝拍立淘按图搜索API接口及JSON数据返回全流程指南
通过以上流程,可实现淘宝拍立淘按图搜索的完整调用链路,并获取结构化的JSON商品数据,支撑电商比价、智能推荐等业务场景。
|
10月前
|
SQL 安全 Linux
Metasploit Pro 4.22.8-20251014 (Linux, Windows) - 专业渗透测试框架
Metasploit Pro 4.22.8-20251014 (Linux, Windows) - 专业渗透测试框架
475 1
Metasploit Pro 4.22.8-20251014 (Linux, Windows) - 专业渗透测试框架
|
10月前
|
Linux 网络安全 iOS开发
Metasploit Framework 6.4.95 (macOS, Linux, Windows) - 开源渗透测试框架
Metasploit Framework 6.4.95 (macOS, Linux, Windows) - 开源渗透测试框架
1080 1
Metasploit Framework 6.4.95 (macOS, Linux, Windows) - 开源渗透测试框架
|
11月前
|
JSON 缓存 供应链
电子元件 item_search - 按关键字搜索商品接口深度分析及 Python 实现
本文深入解析电子元件item_search接口的设计逻辑与Python实现,涵盖参数化筛选、技术指标匹配、供应链属性过滤及替代型号推荐等核心功能,助力高效精准的电子元器件搜索与采购决策。
|
10月前
|
数据采集 运维 监控
爬虫与自动化技术深度解析:从数据采集到智能运维的完整实战指南
本文系统解析爬虫与自动化核心技术,涵盖HTTP请求、数据解析、分布式架构及反爬策略,结合Scrapy、Selenium等框架实战,助力构建高效、稳定、合规的数据采集系统。
1368 62
爬虫与自动化技术深度解析:从数据采集到智能运维的完整实战指南

推荐镜像

更多