python+requests+excel+unittest+ddt接口自动化数据驱动并生成html报告

Posted jason89

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了python+requests+excel+unittest+ddt接口自动化数据驱动并生成html报告相关的知识,希望对你有一定的参考价值。

前言

1.环境准备:

  • python3.6
  • requests
  • xlrd
  • openpyxl
  • HTMLTestRunner_api

2.目前实现的功能:

  • 封装requests请求方法
  • 在excel填写接口请求参数
  • 运行完后,重新生成一个excel报告,结果写入excel
  • 用unittest+ddt数据驱动模式执行
  • HTMLTestRunner生成可视化的html报告
  • 对于没有关联的单个接口请求是可以批量执行的,需要登录的话写到setUpclass里的session里保持cookies
  • token关联的不能实现
  • logging日志文件暂时未加入

3.目前已知的缺陷:

  • 无法实现参数关联:上个请求的结果是下个请求的参数,如token
  • 接口请求参数名有重复的,目前未处理,如key1=value1&key1=value2,两个key都一样,这种需要用元组存储,目前暂时未判断
  • 生成的excel样式未处理,后期慢慢优化样式
  • python新手可能遇到模块导入报错问题

项目结构

技术分享图片

excel测试数据

技术分享图片

xlrd读excel数据

1.先从excel里面读取测试数据,返回字典格式

技术分享图片

 1 # coding:utf-8
 2 
 3 # 作者:上海-悠悠
 4 # QQ群:226296743
 5 
 6 import xlrd
 7 class ExcelUtil():
 8     def __init__(self, excelPath, sheetName="Sheet1"):
 9         self.data = xlrd.open_workbook(excelPath)
10         self.table = self.data.sheet_by_name(sheetName)
11         # 获取第一行作为key值
12         self.keys = self.table.row_values(0)
13         # 获取总行数
14         self.rowNum = self.table.nrows
15         # 获取总列数
16         self.colNum = self.table.ncols
17 
18     def dict_data(self):
19         if self.rowNum <= 1:
20             print("总行数小于1")
21         else:
22             r = []
23             j = 1
24             for i in list(range(self.rowNum-1)):
25                 s = {}
26                 # 从第二行取对应values值
27                 s[rowNum] = i+2
28                 values = self.table.row_values(j)
29                 for x in list(range(self.colNum)):
30                     s[self.keys[x]] = values[x]
31                 r.append(s)
32                 j += 1
33             return r
34 
35 if __name__ == "__main__":
36     filepath = "debug_api.xlsx"
37     sheetName = "Sheet1"
38     data = ExcelUtil(filepath, sheetName)
39     print(data.dict_data())
40 openpyxl写入数据
41 
42 1.再封装一个写入excel数据的方法
43 
44 # coding:utf-8
45 from openpyxl import load_workbook
46 import openpyxl
47 
48 # 作者:上海-悠悠
49 # QQ群:226296743
50 
51 def copy_excel(excelpath1, excelpath2):
52     ‘‘‘复制excek,把excelpath1数据复制到excelpath2‘‘‘
53     wb2 = openpyxl.Workbook()
54     wb2.save(excelpath2)
55     # 读取数据
56     wb1 = openpyxl.load_workbook(excelpath1)
57     wb2 = openpyxl.load_workbook(excelpath2)
58     sheets1 = wb1.sheetnames
59     sheets2 = wb2.sheetnames
60     sheet1 = wb1[sheets1[0]]
61     sheet2 = wb2[sheets2[0]]
62     max_row = sheet1.max_row         # 最大行数
63     max_column = sheet1.max_column   # 最大列数
64 
65     for m in list(range(1,max_row+1)):
66         for n in list(range(97,97+max_column)):   # chr(97)=‘a‘
67             n = chr(n)                            # ASCII字符
68             i =%s%d% (n, m)                     # 单元格编号
69             cell1 = sheet1[i].value               # 获取data单元格数据
70             sheet2[i].value = cell1               # 赋值到test单元格
71 
72     wb2.save(excelpath2)                 # 保存数据
73     wb1.close()                          # 关闭excel
74     wb2.close()
75 
76 class Write_excel(object):
77     ‘‘‘修改excel数据‘‘‘
78     def __init__(self, filename):
79         self.filename = filename
80         self.wb = load_workbook(self.filename)
81         self.ws = self.wb.active  # 激活sheet
82 
83     def write(self, row_n, col_n, value):
84         ‘‘‘写入数据,如(2,3,"hello"),第二行第三列写入数据"hello"‘‘‘
85         self.ws.cell(row_n, col_n).value = value
86         self.wb.save(self.filename)
87 
88 if __name__ == "__main__":
89     copy_excel("debug_api.xlsx", "testreport.xlsx")
90     wt = Write_excel("testreport.xlsx")
91     wt.write(4, 5, "HELLEOP")
92     wt.write(4, 6, "HELLEOP")

 

封装request请求方法

1.把从excel读处理的数据作为请求参数,封装requests请求方法,传入请求参数,并返回结果

2.为了不污染测试的数据,出报告的时候先将测试的excel复制都应该新的excel

3.把测试返回的结果,在新的excel里面写入数据

 1 # coding:utf-8
 2 import json
 3 import requests
 4 from excelddtdriver.common.readexcel import ExcelUtil
 5 from excelddtdriver.common.writeexcel import copy_excel, Write_excel
 6 
 7 # 作者:上海-悠悠
 8 # QQ群:226296743
 9 
10 
11 def send_requests(s, testdata):
12     ‘‘‘封装requests请求‘‘‘
13     method = testdata["method"]
14     url = testdata["url"]
15     # url后面的params参数
16     try:
17         params = eval(testdata["params"])
18     except:
19         params = None
20     # 请求头部headers
21     try:
22         headers = eval(testdata["headers"])
23         print("请求头部:%s" % headers)
24     except:
25         headers = None
26     # post请求body类型
27     type = testdata["type"]
28 
29     test_nub = testdata[id]
30     print("*******正在执行用例:-----  %s  ----**********" % test_nub)
31     print("请求方式:%s, 请求url:%s" % (method, url))
32     print("请求params:%s" % params)
33 
34     # post请求body内容
35     try:
36         bodydata = eval(testdata["body"])
37     except:
38         bodydata = {}
39 
40     # 判断传data数据还是json
41     if type == "data":
42         body = bodydata
43     elif type == "json":
44         body = json.dumps(bodydata)
45     else:
46         body = bodydata
47     if method == "post": print("post请求body类型为:%s ,body内容为:%s" % (type, body))
48 
49     verify = False
50     res = {}   # 接受返回数据
51 
52     try:
53         r = s.request(method=method,
54                       url=url,
55                       params=params,
56                       headers=headers,
57                       data=body,
58                       verify=verify
59                        )
60         print("页面返回信息:%s" % r.content.decode("utf-8"))
61         res[id] = testdata[id]
62         res[rowNum] = testdata[rowNum]
63         res["statuscode"] = str(r.status_code)  # 状态码转成str
64         res["text"] = r.content.decode("utf-8")
65         res["times"] = str(r.elapsed.total_seconds())   # 接口请求时间转str
66         if res["statuscode"] != "200":
67             res["error"] = res["text"]
68         else:
69             res["error"] = ""
70         res["msg"] = ""
71         if testdata["checkpoint"] in res["text"]:
72             res["result"] = "pass"
73             print("用例测试结果:   %s---->%s" % (test_nub, res["result"]))
74         else:
75             res["result"] = "fail"
76         return res
77     except Exception as msg:
78         res["msg"] = str(msg)
79         return res
80 
81 def wirte_result(result, filename="result.xlsx"):
82     # 返回结果的行数row_nub
83     row_nub = result[rowNum]
84     # 写入statuscode
85     wt = Write_excel(filename)
86     wt.write(row_nub, 8, result[statuscode])       # 写入返回状态码statuscode,第8列
87     wt.write(row_nub, 9, result[times])            # 耗时
88     wt.write(row_nub, 10, result[error])            # 状态码非200时的返回信息
89     wt.write(row_nub, 12, result[result])           # 测试结果 pass 还是fail
90     wt.write(row_nub, 13, result[msg])           # 抛异常
91 
92 if __name__ == "__main__":
93     data = ExcelUtil("debug_api.xlsx").dict_data()
94     print(data[0])
95     s = requests.session()
96     res = send_requests(s, data[0])
97     copy_excel("debug_api.xlsx", "result.xlsx")
98     wirte_result(res, filename="result.xlsx")

 

测试用例unittest+ddt

1.测试用例用unittest框架组建,并用ddt数据驱动模式,批量执行用例

 1 # coding:utf-8
 2 import unittest
 3 import ddt
 4 import os
 5 import requests
 6 from excelddtdriver.common import base_api
 7 from excelddtdriver.common import readexcel
 8 from excelddtdriver.common import writeexcel
 9 
10 # 作者:上海-悠悠
11 # QQ群:226296743
12 
13 # 获取demo_api.xlsx路径
14 curpath = os.path.dirname(os.path.realpath(__file__))
15 testxlsx = os.path.join(curpath, "demo_api.xlsx")
16 
17 # 复制demo_api.xlsx文件到report下
18 report_path = os.path.join(os.path.dirname(curpath), "report")
19 reportxlsx = os.path.join(report_path, "result.xlsx")
20 
21 testdata = readexcel.ExcelUtil(testxlsx).dict_data()
22 @ddt.ddt
23 class Test_api(unittest.TestCase):
24     @classmethod
25     def setUpClass(cls):
26         cls.s = requests.session()
27         # 如果有登录的话,就在这里先登录了
28         writeexcel.copy_excel(testxlsx, reportxlsx) # 复制xlsx
29 
30     @ddt.data(*testdata)
31     def test_api(self, data):
32         # 先复制excel数据到report
33         res = base_api.send_requests(self.s, data)
34 
35         base_api.wirte_result(res, filename=reportxlsx)
36         # 检查点 checkpoint
37         check = data["checkpoint"]
38         print("检查点->:%s"%check)
39         # 返回结果
40         res_text = res["text"]
41         print("返回实际结果->:%s"%res_text)
42         # 断言
43         self.assertTrue(check in res_text)
44 
45 if __name__ == "__main__":
46     unittest.main()

 

生成报告

1.用HTMLTestRunner生成html报告,我这里改了下名称,改成了HTMLTestRunner_api.py
此文件跟selenium的报告是通用的,github可下载https://github.com/yoyoketang/selenium_report/tree/master/selenium_report

 1 # coding=utf-8
 2 import unittest
 3 import time
 4 from excelddtdriver.common import HTMLTestRunner_api
 5 import os
 6 
 7 # 作者:上海-悠悠
 8 # QQ群:226296743
 9 
10 curpath = os.path.dirname(os.path.realpath(__file__))
11 report_path = os.path.join(curpath, "report")
12 if not os.path.exists(report_path): os.mkdir(report_path)
13 case_path = os.path.join(curpath, "case")
14 
15 def add_case(casepath=case_path, rule="test*.py"):
16     ‘‘‘加载所有的测试用例‘‘‘
17     # 定义discover方法的参数
18     discover = unittest.defaultTestLoader.discover(casepath,
19                                                   pattern=rule,)
20 
21     return discover
22 
23 def run_case(all_case, reportpath=report_path):
24     ‘‘‘执行所有的用例, 并把结果写入测试报告‘‘‘
25     htmlreport = reportpath+r"\\result.html"
26     print("测试报告生成地址:%s"% htmlreport)
27     fp = open(htmlreport, "wb")
28     runner = HTMLTestRunner_api.H全部折叠 折叠标题:
29 View Code
30   显示行号    行内代码
31 TMLTestRunner(stream=fp,
32                                                verbosity=2,
33                                                title="测试报告",
34                                                description="用例执行情况")
35 
36     # 调用add_case函数返回值
37     runner.run(all_case)
38     fp.close()
39 
40 if __name__ == "__main__":
41     cases = add_case()
42     run_case(cases)

 

2.生成的excel报告

技术分享图片

3.生成的html报告

技术分享图片


以上是关于python+requests+excel+unittest+ddt接口自动化数据驱动并生成html报告的主要内容,如果未能解决你的问题,请参考以下文章

promise封装uniapp中的uni.request

promise封装uniapp中的uni.request

promise封装uniapp中的uni.request

promise封装uniapp中的uni.request

uni-app的uni.request()请求封装

uniapp(uni.request Promise简单封装)