1.环境准备:
python3.6requestsxlrdopenpyxlHTMLTestRunner_api
2.目前实现的功能:
封装requests请求方法在excel填写接口请求参数运行完后,重新生成一个excel报告,结果写入excel用unittest+ddt数据驱动模式执行HTMLTestRunner生成可视化的html报告对于没有关联的单个接口请求是可以批量执行的,需要登录的话写到setUpclass里的session里保持cookiestoken关联的不能实现logging日志文件暂时未加入
3.目前已知的缺陷:
无法实现参数关联:上个请求的结果是下个请求的参数,如token接口请求参数名有重复的,目前未处理,如key1=value1&key1=value2,两个key都一样,这种需要用元组存储,目前暂时未判断生成的excel样式未处理,后期慢慢优化样式python新手可能遇到模块导入报错问题
项目结构
excel测试数据
xlrd读excel数据
1.先从excel里面读取测试数据,返回字典格式
1 # coding:utf-82 3 # 作者:上海-悠悠4 # QQ群:2262967435 6 import xlrd7 class ExcelUtil():8def __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.nrows15 # 获取总列数16 self.colNum = self.table.ncols17 18def dict_data(self):19 if self.rowNum <= 1:20 print("总行数小于1")21 else:22 r = []23 j = 124 for i in list(range(self.rowNum-1)):25 s = {}26 # 从第二行取对应values值27 s['rowNum'] = i+228 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 += 133 return r34 35 if __name__ == "__main__":36filepath = "debug_api.xlsx"37sheetName = "Sheet1"38data = ExcelUtil(filepath, sheetName)39print(data.dict_data())40 openpyxl写入数据41 42 1.再封装一个写入excel数据的方法43 44 # coding:utf-845 from openpyxl import load_workbook46 import openpyxl47 48 # 作者:上海-悠悠49 # QQ群:22629674350 51 def copy_excel(excelpath1, excelpath2):52'''复制excek,把excelpath1数据复制到excelpath2'''53wb2 = openpyxl.Workbook()54wb2.save(excelpath2)55# 读取数据56wb1 = openpyxl.load_workbook(excelpath1)57wb2 = openpyxl.load_workbook(excelpath2)58sheets1 = wb1.sheetnames59sheets2 = wb2.sheetnames60sheet1 = wb1[sheets1[0]]61sheet2 = wb2[sheets2[0]]62max_row = sheet1.max_row # 最大行数63max_column = sheet1.max_column # 最大列数64 65for 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 72wb2.save(excelpath2) # 保存数据73wb1.close()# 关闭excel74wb2.close()75 76 class Write_excel(object):77'''修改excel数据'''78def __init__(self, filename):79 self.filename = filename80 self.wb = load_workbook(self.filename)81 self.ws = self.wb.active # 激活sheet82 83def write(self, row_n, col_n, value):84 '''写入数据,如(2,3,"hello"),第二行第三列写入数据"hello"'''85 self.ws.cell(row_n, col_n).value = value86 self.wb.save(self.filename)87 88 if __name__ == "__main__":89copy_excel("debug_api.xlsx", "testreport.xlsx")90wt = Write_excel("testreport.xlsx")91wt.write(4, 5, "HELLEOP")92wt.write(4, 6, "HELLEOP")
封装request请求方法
1.把从excel读处理的数据作为请求参数,封装requests请求方法,传入请求参数,并返回结果
2.为了不污染测试的数据,出报告的时候先将测试的excel复制都应该新的excel
3.把测试返回的结果,在新的excel里面写入数据
1 # coding:utf-82 import json3 import requests4 from mon.readexcel import ExcelUtil5 from mon.writeexcel import copy_excel, Write_excel6 7 # 作者:上海-悠悠8 # QQ群:2262967439 10 11 def send_requests(s, testdata):12'''封装requests请求'''13method = testdata["method"]14url = testdata["url"]15# url后面的params参数16try:17 params = eval(testdata["params"])18except:19 params = None20# 请求头部headers21try:22 headers = eval(testdata["headers"])23 print("请求头部:%s" % headers)24except:25 headers = None26# post请求body类型27type = testdata["type"]28 29test_nub = testdata['id']30print("*******正在执行用例:----- %s ----**********" % test_nub)31print("请求方式:%s, 请求url:%s" % (method, url))32print("请求params:%s" % params)33 34# post请求body内容35try:36 bodydata = eval(testdata["body"])37except:38 bodydata = {}39 40# 判断传data数据还是json41if type == "data":42 body = bodydata43elif type == "json":44 body = json.dumps(bodydata)45else:46 body = bodydata47if method == "post": print("post请求body类型为:%s ,body内容为:%s" % (type, body))48 49verify = False50res = {} # 接受返回数据51 52try:53 r = s.request(method=method,54 url=url,55 params=params,56 headers=headers,57 data=body,58 verify=verify59 )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) # 状态码转成str64 res["text"] = r.content.decode("utf-8")65 res["times"] = str(r.elapsed.total_seconds()) # 接口请求时间转str66 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 res77except Exception as msg:78 res["msg"] = str(msg)79 return res80 81 def wirte_result(result, filename="result.xlsx"):82# 返回结果的行数row_nub83row_nub = result['rowNum']84# 写入statuscode85wt = Write_excel(filename)86wt.write(row_nub, 8, result['statuscode']) # 写入返回状态码statuscode,第8列87wt.write(row_nub, 9, result['times']) # 耗时88wt.write(row_nub, 10, result['error']) # 状态码非200时的返回信息89wt.write(row_nub, 12, result['result']) # 测试结果 pass 还是fail90wt.write(row_nub, 13, result['msg']) # 抛异常91 92 if __name__ == "__main__":93data = ExcelUtil("debug_api.xlsx").dict_data()94print(data[0])95s = requests.session()96res = send_requests(s, data[0])97copy_excel("debug_api.xlsx", "result.xlsx")98wirte_result(res, filename="result.xlsx")
测试用例unittest+ddt
1.测试用例用unittest框架组建,并用ddt数据驱动模式,批量执行用例
1 # coding:utf-82 import unittest3 import ddt4 import os5 import requests6 from mon import base_api7 from mon import readexcel8 from mon import writeexcel9 10 # 作者:上海-悠悠11 # QQ群:22629674312 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.ddt23 class Test_api(unittest.TestCase):24@classmethod25def setUpClass(cls):26 cls.s = requests.session()27 # 如果有登录的话,就在这里先登录了28 writeexcel.copy_excel(testxlsx, reportxlsx) # 复制xlsx29 30@ddt.data(*testdata)31def test_api(self, data):32 # 先复制excel数据到report33 res = base_api.send_requests(self.s, data)34 35 base_api.wirte_result(res, filename=reportxlsx)36 # 检查点 checkpoint37 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__":46unittest.main()
生成报告
1.用HTMLTestRunner生成html报告,我这里改了下名称,改成了HTMLTestRunner_api.py
此文件跟selenium的报告是通用的,github可下载/yoyoketang/selenium_report/tree/master/selenium_report
1 # coding=utf-82 import unittest3 import time4 from mon import HTMLTestRunner_api5 import os6 7 # 作者:上海-悠悠8 # QQ群:2262967439 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方法的参数18discover = unittest.defaultTestLoader.discover(casepath,19 pattern=rule,)20 21return discover22 23 def run_case(all_case, reportpath=report_path):24'''执行所有的用例, 并把结果写入测试报告'''25htmlreport = reportpath+r"\result.html"26print("测试报告生成地址:%s"% htmlreport)27fp = open(htmlreport, "wb")28runner = HTMLTestRunner_api.H全部折叠 折叠标题:29 View Code30 显示行号 行内代码31 TMLTestRunner(stream=fp,32 verbosity=2,33 title="测试报告",34 description="用例执行情况")35 36# 调用add_case函数返回值37runner.run(all_case)38fp.close()39 40 if __name__ == "__main__":41cases = add_case()42run_case(cases)
2.生成的excel报告
3.生成的html报告