• Unittest框架+ddt数据驱动+HTMLTestRunner+sendmail(自动发送测试报告)+git+Jenkins


    本次写的是针对有代码基础的,没基础建议先去学基础,以下所有描述内容都是我已经在公司项目实践成功的!仅供参考

    整体思路:
    1、接口自动化用的是Python中unittest框架
    2、所有的测试数据用例存放Excel表
    3、封装一套读取和写入的Excel方法
    4、重写request方法(为了从Excel读取数据后对数据作分析和判断并在测试报告生成相关信息)

    5、通过HTMLTestRunner运行测试用例生成网页版报告
    6、将自动化脚本放到公司git上,方便其他人员获取你的代码进行编写脚本,后面会具体讲如何获取代码和提交代码(让运维人员给你开通个git账号,自己注册登录就可以了)
    7、通过Jenkins构建任务,定时自动运行git上自动化脚本,后面会具体讲如何配置Jenkins

    先看看我的接口自动化整个目录结构和每个文件具体是干嘛的:

    一、读取表格的代码:

      1 import xlrd,os,copy
      2 
      3 import sys,os
      4 
      5 #这三行代码是解决文件路径问题
      6 
      7 curPath = os.path.abspath(os.path.dirname(__file__))
      8 
      9 rootPath = os.path.split(curPath)[0]
     10 
     11 sys.path.append(rootPath)
     12 
     13 class ExcelUtil():
     14 
     15     list=[]
     16 
     17     list1 = []
     18 
     19     def __init__(self,excelpath,sheetname='Sheet1'):
     20 
     21         self.data=xlrd.open_workbook(excelpath)
     22 
     23         self.table=self.data.sheet_by_name(sheetname)
     24 
     25         #获取第一行作为key值
     26 
     27         self.keys=self.table.row_values(0)
     28 
     29         #获取总行数
     30 
     31         self.rowNum=self.table.nrows
     32 
     33         #获取总列数
     34 
     35         self.colNum=self.table.ncols
     36 
     37        
     38 
     39     #此方法有俩个用处:1、获取关闭或打开的case 2、不同模块的用例也可以通过该方法区分开来
     40 
     41     def open_case(self):
     42 
     43         exceldata = ExcelUtil(os.path.abspath(rootPath+'/Case/Ddt_case_api/TestCase.xlsx'), 'Sheet1')
     44 
     45         list = []
     46 
     47         list1 = []
     48 
     49         list2=[]
     50 
     51         for i in exceldata.dict_data():
     52 
     53             a = i['control_case']  # 这是在Excel加的开关case的字段
     54 
     55             if a == '':
     56 
     57                 list.append(i)
     58 
     59             elif a=='mobile_请先登录':
     60 
     61                 list1.append(i)
     62 
     63             elif a=='B_请先登录':
     64 
     65                 list2.append(i)
     66 
     67         return  list,list1,list2
     68 
     69         # print(len(list))
     70 
     71     def dict_data(self):
     72 
     73         if self.rowNum<=1:
     74 
     75             print('总行数小于1')
     76 
     77         else:
     78 
     79             r=[]
     80 
     81             j=1
     82 
     83             for i in range(self.rowNum-1):
     84 
     85                 s={}
     86 
     87                 s['rowNum']=i+2
     88 
     89                 values=self.table.row_values(j)
     90 
     91                 for x in range(self.colNum):
     92 
     93                     s[self.keys[x]]=values[x]
     94 
     95                 r.append(s)
     96 
     97                 j+=1
     98 
     99             return r
    100 
    101 #通过下面这个入口进行调试
    102 
    103 if __name__=='__main__':
    104 
    105     a=ExcelUtil('D:Business_ManageMentCaseDdt_case_apiTestCase.xlsx')
    106 
    107     b=a.open_case()
    108 
    109     print(b)

    二、写入表格代码:

     1 from openpyxl import load_workbook
     2 
     3 import openpyxl
     4 
     5 import xlrd
     6 
     7  
     8 
     9 def Copy_excel(exclepath1,excelpath2):
    10 
    11 #把excle1数据复制到excel2
    12 
    13     wb2=openpyxl.Workbook()
    14 
    15     wb2.save(excelpath2)
    16 
    17     wb1=load_workbook(exclepath1)
    18 
    19     wb2=load_workbook(excelpath2)
    20 
    21     sheets1=wb1.sheetnames
    22 
    23     sheets2=wb2.sheetnames
    24 
    25     sheet1=wb1[sheets1[0]]
    26 
    27     sheet2=wb2[sheets2[0]]
    28 
    29     maxrow=sheet1.max_row
    30 
    31     maxcolum=sheet1.max_column
    32 
    33     for m in range(1,maxrow+1):
    34 
    35         for n in range(97,97+maxcolum):
    36 
    37             n=chr(n)
    38 
    39             i='%s%d'%(n,m)
    40 
    41             cell1=sheet1[i].value
    42 
    43             sheet2[i]=cell1
    44 
    45     wb2.save(excelpath2)
    46 
    47     wb1.close()
    48 
    49     wb2.close()
    50 
    51 class Write_excel():
    52 
    53     def __init__(self,filename):
    54 
    55         self.filename=filename
    56 
    57         self.wb=load_workbook(self.filename)
    58 
    59 #激活sheet
    60 
    61         self.ws=self.wb.active
    62 
    63     def write(self,row_n,col_n,value):
    64 
    65         self.ws.cell(row_n,col_n).value=value
    66 
    67         self.wb.save(self.filename)
    68 
    69  
    70 
    71  
    72 
    73 #通过下面入口进行调试
    74 
    75 if  __name__=='__main__':
    76 
    77     Copy_excel('D:Business_ManageMentCaseDdt_case_apiTestCase.xlsx','D:Business_ManageMentCase\result.xlsx')
    78 
    79     wt=Write_excel('D:Business_ManageMentCase\result.xlsx')
    80 
    81     wt.write(1,2,'hello')


     

    三、重写request方法及将接口返回结果根据自己的需要写入拷贝的那张Excel表中

     1 import json
     2 import requests
     3 from Tool_class.read_excel_fz import ExcelUtil
     4 from Tool_class.writeexcel_fz import Copy_excel, Write_excel
     5 
     6 
     7 def send_requests(s, testdata):
     8     '''封装requests请求'''
     9     #获取Excel标格中表头为method的数据
    10     method = testdata["method"]
    11     # 获取Excel标格中表头为url的数据
    12     url = testdata["url"]
    13     try:
    14         #eval函数可以将读取到的参数内容转化成字典格式
    15         # 获取Excel标格中表头为params的数据
    16         params = eval(testdata["params"])
    17     except:
    18         params = None
    19     # 请求头部headers
    20     try:
    21         # 获取Excel标格中表头为headers的数据
    22         headers = eval(testdata["headers"])
    23         print("请求头部:%s" % headers)
    24     except:
    25         headers = None
    26 
    27 
    28     # post请求body类型
    29     # 获取Excel标格中表头为type的数据
    30     type = testdata["type"]
    31     # 获取Excel标格中表头为id的数据
    32     test_nub = testdata['id']
    33     print("*******正在执行用例:-----  %s  ----**********" % test_nub)
    34     print("请求方式:%s, 请求url:%s" % (method, url))
    35     print("请求params:%s" % params)
    36     # post请求body内容
    37     try:
    38         # 获取Excel标格中表头为body的数据
    39          bodydata = eval(testdata["body"])
    40     except:
    41          bodydata = {}
    42     # 判断传data数据还是json
    43     if type == "json":
    44             #json.dumps将字典数据转成json格式,因为不同的接口传递参数是不同的,有的是传data,有的传json
    45             body= json.dumps(bodydata)
    46     elif type == "data":
    47             body = bodydata
    48     else:
    49             body = bodydata
    50 
    51     if type=='json':
    52         print("post请求body类型为:%s ,body内容为:%s" % (type,body))
    53     elif method=='post':
    54         print("post请求body类型为:%s ,body内容为:%s" % (type,body))
    55     verify = False
    56     res = {}   # 接受返回数据
    57 
    58     try:
    59         r = s.request(method=method,
    60                       url=url,
    61                       params=params,
    62                       headers=headers,
    63                       data=body,
    64                       verify=verify
    65                        )
    66         print("页面返回信息:%s" % r.json())
    67         res['id'] = testdata['id']
    68         res['rowNum'] = testdata['rowNum']
    69         res["statuscode"] = str(r.status_code)  # 状态码转成str
    70         res["text"] = r.json()
    71         res["times"] = str(r.elapsed.total_seconds())   # 接口请求时间转str
    72         if res["statuscode"] != "200":
    73             res["error"] = "服务错误,接口未请求成功"
    74             res["msg"] = str(res["text"])
    75         else:
    76             res["error"] = ""
    77             res["msg"] = ""
    78         if testdata["checkpoint"] == res["text"]['msg']:
    79             res["result"] = "pass"
    80             print("用例测试结果:   %s---->%s" % (test_nub, res["result"]))
    81         else:
    82             res["result"] = "fail"
    83         return res
    84     except Exception as msg:
    85         res["msg"] = str(msg)
    86         return res
    87 #将运行返回的结果,根据自己需要,需要要哪些结果就把哪些结果写入拷贝的那份Excel中
    88 def wirte_result(result, filename="D:Business_ManageMentCase\result.xlsx"):
    89     # 返回结果的行数row_nub
    90     row_nub = result['rowNum']
    91     # 写入statuscode
    92     wt = Write_excel(filename)
    93     wt.write(row_nub, 7, result['statuscode'])        # 写入返回状态码statuscode,第8列
    94     wt.write(row_nub, 12, result['times'])             # 耗时
    95     wt.write(row_nub, 14, result['error'])            # 状态码非200时的返回信息
    96     wt.write(row_nub, 13, result['result'])           # 测试结果 pass 还是fail
    97     wt.write(row_nub, 15, result['msg'])              # 抛异常

    四、定义发送邮件和创建测试用例套件

     1 import time,os
     2 import smtplib
     3 import  unittest
     4 from Commons import HTMLTestRunner_jpg
     5 from email.header import Header
     6 from email.mime.text import MIMEText
     7 from email.mime.multipart import MIMEMultipart
     8 import email.mime.multipart
     9 from email.mime.application import MIMEApplication
    10 from Case import *
    11 
    12 # test_dir = os.path.abspath(os.path.join(os.getcwd(), ".."))
    13 # now = time.strftime('%y_%m_%d %H_%M_%S')
    14 # filename = (os.path.abspath('../Report') + '\' + now + 'result.html')
    15 # print(filename)
    16 # fp = open(filename, 'wb')
    17 import sys,os
    18 curPath = os.path.abspath(os.path.dirname(__file__))
    19 rootPath = os.path.split(curPath)[0]
    20 sys.path.append(rootPath)
    21 #定义发送邮件
    22 class Send():
    23     def __init__(self):
    24         self.flie_dir=rootPath+'Report'
    25         self.lists = os.listdir(self.flie_dir)
    26         #将所有的报告按时间从小到大大排序
    27         self.lists.sort(key=lambda fn: os.path.getmtime(self.flie_dir + '\' + fn))
    28         #取报告集合中最后一个报告即为最新的测试报告
    29         self.new_file = os.path.join(self.flie_dir, self.lists[-1])
    30     def send_mail(self):
    31                 now=time.strftime('%y:%m:%d:%H:%M:%S')
    32                 sender = '1063126729@qq.com'
    33                 recever= 'steve@wemart.cn'
    34                 msg = MIMEMultipart()
    35                 content = '最新接口测试报告,详情请下载附件查看,生成时间为:%s'%(now)
    36                 txt = email.mime.text.MIMEText(content, 'plain', 'utf-8')
    37                 msg.attach(txt)
    38                 msg['Subject']='接口自动化测试报告'
    39                 msg['date']=now
    40                 #添加附件
    41                 att = MIMEText(open(self.new_file, "rb").read(), "base64", "utf-8")
    42                 att["Content-Type"] = "application/octet-stream"
    43                 att["Content-Disposition"] = 'attachment; filename= "Report.html"'
    44                 msg.attach(att)
    45                 server=smtplib.SMTP_SSL(port=465)
    46                 server.connect('smtp.qq.com')
    47                 server.login('1063126729@qq.com','lhrcqszwzqafbcjf')
    48                 server.sendmail(sender,recever,msg.as_string())
    49                 server.quit()
    50                 print('邮件已发送')
    51     #创建一个测试套件,将所有的用例添加到测试套件
    52     def creatsuit(slef):
    53             test_dir =rootPath+'Case'
    54             suit=unittest.TestSuite()
    55             discover=unittest.defaultTestLoader.discover(test_dir,pattern='test*.py',top_level_dir=None)
    56             for test_suit in discover:
    57                 for case in test_suit:
    58                     suit.addTest(case)
    59             return suit

     这里说明一下,excel表格自己新建一份,名字命名好,表头各个字段自己喜欢命名啥就命名啥,但注意代码中涉及到表头字段的也要相应调整,保持俩者一致。下面我贴出我存放用例的excel表格样式,供参考:

    A:控制开关case    B:case名称   C:接口请求方法  D:接口地址  E:传参类型    F:请求头   G:状态码   H:检查点    I:get请求是传的参数     J:post请求时传的参数   K可以不要,这是我当时调试用的

    L:接口响应时间  M:运行结果失败获成功   N:提示接口报错     O:接口返回的报错信息     其中G、L、M、N、O是不用填写的,这是为了复制该份表格时,要写入数据到复制的那份表格中用的

    五、前四部分都是一些封装的东西,都是为了第五部分调用准备的,下面的代码是利用unittest框架去组织测试用例,因为我通过ddt数据驱动的,所以这部分代码就比较简洁了,如果你把测试数据都写在代码中,这是不利于维护的,看上去也很繁琐,几千条测试用例那要写多少

     1 import unittest
     2 import ddt
     3 import os
     4 import requests
     5 from Commons import base_api
     6 from Tool_class import read_excel_fz
     7 from Tool_class import writeexcel_fz
     8 from Case.Hand_code_case.Login_case.Test_stor_login02 import *
     9 import os,copy
    10 
    11 #获取当前文件位置路径
    12 curpath = os.path.dirname(os.path.realpath(__file__))
    13 # 获取TestCase.xlsx路径
    14 testxlsx = os.path.join(curpath, "TestCase.xlsx")
    15 #获取curpath位置的上一层目录位置
    16 report_path = os.path.join(os.path.dirname(curpath))
    17 #获取测试结果表格的目录位置
    18 reportxlsx = os.path.join(report_path, "result.xlsx")
    19 #创建读取表格的对象
    20 testdata = read_excel_fz.ExcelUtil(testxlsx)
    21 #获取表格中需要运行的数据或不需要运行的数据
    22 cases=testdata.open_case()
    23 #表格中打开的case
    24 case=cases[0]
    25 #表格中关闭的case
    26 case_mobile=cases[1]
    27 
    28 
    29 @ddt.ddt
    30 class Test_api(unittest.TestCase):
    31       u'''B2C-API'''
    32       @classmethod
    33       def setUpClass(cls):
    34           # 如果有登录的话,就在这里先登录了
    35           cls.s = requests.Session()
    36           # 复制xlsx
    37           writeexcel_fz.Copy_excel(testxlsx, reportxlsx)
    38 
    39       #采用装饰器,在运行case之前都会先运行这个,这里的case是表格里打开的用例,也就是我需要运行的数据
    40       @ddt.data(*case)
    41       def test_api_01(self, data):
    42         #先复制excel数据到Case文件夹下面
    43         res = base_api.send_requests(self.s, data)
    44         base_api.wirte_result(res, filename=reportxlsx)
    45         #检查点 checkpoint
    46         check = data["checkpoint"]
    47         print("检查点->:%s"%check)
    48         #返回结果
    49         res_text = res["text"]
    50         print("返回实际结果->:%s"%res_text)
    51         #将接口返回的结果的键和值取到,通过键是否存在,再利用返回的值去和预期结果做断言
    52         for m,n in res_text.items():
    53              if m=='data' and m=='msg':
    54                   self.assertTrue(res_text['data']!=None)
    55                   self.assertTrue(res_text['msg'] == check)
    56              elif 'data' not in m:
    57                     self.assertTrue(res_text['msg']==check)

    六、上面将用例组织好了,接下来就是通过HTMLTestRunner模块运行并生成报告了,注:HTMLTestRunner模块自己去百度下载,可以将内容直接复制,然后在你的工程目录下建一个py文件将内容拷贝进去即可

    
    
     1 import sys,os
     2 curPath = os.path.abspath(os.path.dirname(__file__))
     3 rootPath = os.path.split(curPath)[0]
     4 sys.path.append(rootPath)
     5 
     6 import  unittest,time,os
     7 from  Commons import HTMLTestRunner_jpg
     8 from email.header import Header
     9 from email.mime.text import MIMEText
    10 from  Commons.send_mail_report import Send
    11
    12 #定义一个当前时间戳
    13 now = time.strftime('%y_%m_%d %H_%M_%S')
    14 #以时间给报告命名,这样就不会重复了
    15 filename = rootPath+'Report'+'\'+ now + 'result.html'
    16 fp=open(filename,'wb')
    17 a = Send()
    18 runner = HTMLTestRunner_jpg.HTMLTestRunner(stream=fp, title='测试报告', description='执行情况:')
    19 runner.run(a.creatsuit())
    20 fp.close()
    21 #调用发送邮件的方法
    22 a.send_mail()
    
    
    
    
    

     七、Jenkins配置

    然后回到任务首页,点击任务后面按钮立即构建,打开控制台即可查看运行记录和结果,如图:

    以上所有的步骤已经完成了整个项目的构建,大家针对自己的项目可以拿上面的代码加以修改,其实最重要的是思路和良好的代码基础,我没有针对工具安装进行详细说,这个自己百度!自动化并不难,教程也很多,要学会加以总结并融汇贯通。后期主要跟新我学Python的历程,从基础到高级,再到利用Python实战做项目,欢迎关注

  • 相关阅读:
    URL和URI之间的区别
    nom Uncaught Error: Cannot find module 'babel-runtime/regenerator'
    PHP中使用curl获取头信息headers的一些笔记
    Typora中自定义命令上传图片
    iOS链接big sur弹出“文件找不到”
    云开发cannot find module wx-server-sdk
    关于Laravel框架中Guard的底层实现
    PHP8中字符串与数字的比较更智能
    git refusing to merge unrelated histories
    k8s入门-资源文件实现
  • 原文地址:https://www.cnblogs.com/lz-tester/p/9020664.html
Copyright © 2020-2023  润新知