• http接口自动化测试框架实现


    一、测试需求描述
    对服务后台一系列的http接口功能测试。

    输入:根据接口描述构造不同的参数输入值

    输出:XML文件

    eg:http://xxx.com/xxx_product/test/content_book_list.jsp?listid=1

    二、实现方法
    1、选用Python脚本来驱动测试

    2、采用Excel表格管理测试数据,包括用例的管理、测试数据录入、测试结果显示等等,这个需要封装一个Excel的类即可。

    3、调用http接口采用Python封装好的API即可

    4、测试需要的http组装字符转处理即可

    5、设置2个检查点,XML文件中的返回值字段(通过解析XML得到);XML文件的正确性(文件对比)

    6、首次执行测试采用半自动化的方式,即人工检查输出的XML文件是否正确,一旦正确将封存XML文件,为后续回归测试的预期结果,如果发现错误手工修正为预期文件。(注意不是每次测试都人工检查该文件,只首次测试的时候才检查)

    三、Excel表格样式
    四、实现代码(代码才是王道,有注释很容易就能看明白的)
    1、测试框架代码

    1. #****************************************************************
    2. # TestFrame.py
    3. # Author : Vince
    4. # Version : 1.1.2
    5. # Date : 2011-3-14
    6. # Description: 自动化测试平台
    7. #****************************************************************
    8. import os,sys, urllib, httplib, profile, datetime, time
    9. from xml2dict import XML2Dict
    10. import win32com.client
    11. from win32com.client import Dispatch
    12. import xml.etree.ElementTree as et
    13. #import MySQLdb
    14. #Excel表格中测试结果底色
    15. OK_COLOR=0xffffff
    16. NG_COLOR=0xff
    17. #NT_COLOR=0xffff
    18. NT_COLOR=0xC0C0C0
    19. #Excel表格中测试结果汇总显示位置
    20. TESTTIME=[1, 14]
    21. TESTRESULT=[2, 14]
    22. #Excel模版设置
    23. #self.titleindex=3 #Excel中测试用例标题行索引
    24. #self.casebegin =4 #Excel中测试用例开始行索引
    25. #self.argbegin =3 #Excel中参数开始列索引
    26. #self.argcount =8 #Excel中支持的参数个数
    27. class create_excel:
    28. def __init__(self, sFile, dtitleindex=3, dcasebegin=4, dargbegin=3, dargcount=8):
    29. self.xlApp = win32com.client.Dispatch('et.Application') #MS:Excel WPS:et
    30. try:
    31. self.book = self.xlApp.Workbooks.Open(sFile)
    32. except:
    33. print_error_info()
    34. print "打开文件失败"
    35. exit()
    36. self.file=sFile
    37. self.titleindex=dtitleindex
    38. self.casebegin=dcasebegin
    39. self.argbegin=dargbegin
    40. self.argcount=dargcount
    41. self.allresult=[]
    42. self.retCol=self.argbegin+self.argcount
    43. self.xmlCol=self.retCol+1
    44. self.resultCol=self.xmlCol+1
    45. def close(self):
    46. #self.book.Close(SaveChanges=0)
    47. self.book.Save()
    48. self.book.Close()
    49. #self.xlApp.Quit()
    50. del self.xlApp
    51. def read_data(self, iSheet, iRow, iCol):
    52. try:
    53. sht = self.book.Worksheets(iSheet)
    54. sValue=str(sht.Cells(iRow, iCol).Value)
    55. except:
    56. self.close()
    57. print('读取数据失败')
    58. exit()
    59. #去除'.0'
    60. if sValue[-2:]=='.0':
    61. sValue = sValue[0:-2]
    62. return sValue
    63. def write_data(self, iSheet, iRow, iCol, sData, color=OK_COLOR):
    64. try:
    65. sht = self.book.Worksheets(iSheet)
    66. sht.Cells(iRow, iCol).Value = sData.decode("utf-8")
    67. sht.Cells(iRow, iCol).Interior.Color=color
    68. self.book.Save()
    69. except:
    70. self.close()
    71. print('写入数据失败')
    72. exit()
    73. #获取用例个数
    74. def get_ncase(self, iSheet):
    75. try:
    76. return self.get_nrows(iSheet)-self.casebegin+1
    77. except:
    78. self.close()
    79. print('获取Case个数失败')
    80. exit()
    81. def get_nrows(self, iSheet):
    82. try:
    83. sht = self.book.Worksheets(iSheet)
    84. return sht.UsedRange.Rows.Count
    85. except:
    86. self.close()
    87. print('获取nrows失败')
    88. exit()
    89. def get_ncols(self, iSheet):
    90. try:
    91. sht = self.book.Worksheets(iSheet)
    92. return sht.UsedRange.Columns.Count
    93. except:
    94. self.close()
    95. print('获取ncols失败')
    96. exit()
    97. def del_testrecord(self, suiteid):
    98. try:
    99. #为提升性能特别从For循环提取出来
    100. nrows=self.get_nrows(suiteid)+1
    101. ncols=self.get_ncols(suiteid)+1
    102. begincol=self.argbegin+self.argcount
    103. #提升性能
    104. sht = self.book.Worksheets(suiteid)
    105. for row in range(self.casebegin, nrows):
    106. for col in range(begincol, ncols):
    107. str=self.read_data(suiteid, row, col)
    108. #清除实际结果[]
    109. startpos = str.find('[')
    110. if startpos>0:
    111. str = str[0:startpos].strip()
    112. self.write_data(suiteid, row, col, str, OK_COLOR)
    113. else:
    114. #提升性能
    115. sht.Cells(row, col).Interior.Color = OK_COLOR
    116. #清除TestResul列中的测试结果,设置为NT
    117. self.write_data(suiteid, row, self.argbegin+self.argcount+1, ' ', OK_COLOR)
    118. self.write_data(suiteid, row, self.resultCol, 'NT', NT_COLOR)
    119. except:
    120. self.close()
    121. print('清除数据失败')
    122. exit()
    123. #执行调用
    124. def HTTPInvoke(IPPort, url):
    125. conn = httplib.HTTPConnection(IPPort)
    126. conn.request("GET", url)
    127. rsps = conn.getresponse()
    128. data = rsps.read()
    129. conn.close()
    130. return data
    131. #获取用例基本信息[Interface,argcount,[ArgNameList]]
    132. def get_caseinfo(Data, SuiteID):
    133. caseinfolist=[]
    134. sInterface=Data.read_data(SuiteID, 1, 2)
    135. argcount=int(Data.read_data(SuiteID, 2, 2))
    136. #获取参数名存入ArgNameList
    137. ArgNameList=[]
    138. for i in range(0, argcount):
    139. ArgNameList.append(Data.read_data(SuiteID, Data.titleindex, Data.argbegin+i))
    140. caseinfolist.append(sInterface)
    141. caseinfolist.append(argcount)
    142. caseinfolist.append(ArgNameList)
    143. return caseinfolist
    144. #获取输入
    145. def get_input(Data, SuiteID, CaseID, caseinfolist):
    146. sArge=''
    147. #参数组合
    148. for j in range(0, caseinfolist[1]):
    149. if Data.read_data(SuiteID, Data.casebegin+CaseID, Data.argbegin+j) != "None":
    150. sArge=sArge+caseinfolist[2][j]+'='+Data.read_data(SuiteID, Data.casebegin+CaseID, Data.argbegin+j)+'&'
    151. #去掉结尾的&字符
    152. if sArge[-1:]=='&':
    153. sArge = sArge[0:-1]
    154. sInput=caseinfolist[0]+sArge #组合全部参数
    155. return sInput
    156. #结果判断
    157. def assert_result(sReal, sExpect):
    158. sReal=str(sReal)
    159. sExpect=str(sExpect)
    160. if sReal==sExpect:
    161. return 'OK'
    162. else:
    163. return 'NG'
    164. #将测试结果写入文件
    165. def write_result(Data, SuiteId, CaseId, resultcol, *result):
    166. if len(result)>1:
    167. ret='OK'
    168. for i in range(0, len(result)):
    169. if result[i]=='NG':
    170. ret='NG'
    171. break
    172. if ret=='NG':
    173. Data.write_data(SuiteId, Data.casebegin+CaseId, resultcol,ret, NG_COLOR)
    174. else:
    175. Data.write_data(SuiteId, Data.casebegin+CaseId, resultcol,ret, OK_COLOR)
    176. Data.allresult.append(ret)
    177. else:
    178. if result[0]=='NG':
    179. Data.write_data(SuiteId, Data.casebegin+CaseId, resultcol,result[0], NG_COLOR)
    180. elif result[0]=='OK':
    181. Data.write_data(SuiteId, Data.casebegin+CaseId, resultcol,result[0], OK_COLOR)
    182. else: #NT
    183. Data.write_data(SuiteId, Data.casebegin+CaseId, resultcol,result[0], NT_COLOR)
    184. Data.allresult.append(result[0])
    185. #将当前结果立即打印
    186. print 'case'+str(CaseId+1)+':', Data.allresult[-1]
    187. #打印测试结果
    188. def statisticresult(excelobj):
    189. allresultlist=excelobj.allresult
    190. count=[0, 0, 0]
    191. for i in range(0, len(allresultlist)):
    192. #print 'case'+str(i+1)+':', allresultlist[i]
    193. count=countflag(allresultlist[i],count[0], count[1], count[2])
    194. print 'Statistic result as follow:'
    195. print 'OK:', count[0]
    196. print 'NG:', count[1]
    197. print 'NT:', count[2]
    198. #解析XmlString返回Dict
    199. def get_xmlstring_dict(xml_string):
    200. xml = XML2Dict()
    201. return xml.fromstring(xml_string)
    202. #解析XmlFile返回Dict
    203. def get_xmlfile_dict(xml_file):
    204. xml = XML2Dict()
    205. return xml.parse(xml_file)
    206. #去除历史数据expect[real]
    207. def delcomment(excelobj, suiteid, iRow, iCol, str):
    208. startpos = str.find('[')
    209. if startpos>0:
    210. str = str[0:startpos].strip()
    211. excelobj.write_data(suiteid, iRow, iCol, str, OK_COLOR)
    212. return str
    213. #检查每个item (非结构体)
    214. def check_item(excelobj, suiteid, caseid,real_dict, checklist, begincol):
    215. ret='OK'
    216. for checkid in range(0, len(checklist)):
    217. real=real_dict[checklist[checkid]]['value']
    218. expect=excelobj.read_data(suiteid, excelobj.casebegin+caseid, begincol+checkid)
    219. #如果检查不一致测将实际结果写入expect字段,格式:expect[real]
    220. #将return NG
    221. result=assert_result(real, expect)
    222. if result=='NG':
    223. writestr=expect+'['+real+']'
    224. excelobj.write_data(suiteid, excelobj.casebegin+caseid, begincol+checkid, writestr, NG_COLOR)
    225. ret='NG'
    226. return ret
    227. #检查结构体类型
    228. def check_struct_item(excelobj, suiteid, caseid,real_struct_dict, structlist, structbegin, structcount):
    229. ret='OK'
    230. if structcount>1: #传入的是List
    231. for structid in range(0, structcount):
    232. structdict=real_struct_dict[structid]
    233. temp=check_item(excelobj, suiteid, caseid,structdict, structlist, structbegin+structid*len(structlist))
    234. if temp=='NG':
    235. ret='NG'
    236. else: #传入的是Dict
    237. temp=check_item(excelobj, suiteid, caseid,real_struct_dict, structlist, structbegin)
    238. if temp=='NG':
    239. ret='NG'
    240. return ret
    241. #获取异常函数及行号
    242. def print_error_info():
    243. """Return the frame object for the caller's stack frame."""
    244. try:
    245. raise Exception
    246. except:
    247. f = sys.exc_info()[2].tb_frame.f_back
    248. print (f.f_code.co_name, f.f_lineno)
    249. #测试结果计数器,类似Switch语句实现
    250. def countflag(flag,ok, ng, nt):
    251. calculation = {'OK':lambda:[ok+1, ng, nt],
    252. 'NG':lambda:[ok, ng+1, nt],
    253. 'NT':lambda:[ok, ng, nt+1]}
    254. return calculation[flag]()

    2、项目测试代码

    1. # -*- coding: utf-8 -*-
    2. #****************************************************************
    3. # xxx_server_case.py
    4. # Author : Vince
    5. # Version : 1.0
    6. # Date : 2011-3-10
    7. # Description: 内容服务系统测试代码
    8. #****************************************************************
    9. from testframe import *
    10. from common_lib import *
    11. httpString='http://xxx.com/xxx_product/test/'
    12. expectXmldir=os.getcwd()+'/TestDir/expect/'
    13. realXmldir=os.getcwd()+'/TestDir/real/'
    14. def run(interface_name, suiteid):
    15. print '【'+interface_name+'】' + ' Test Begin,please waiting...'
    16. global expectXmldir, realXmldir
    17. #根据接口名分别创建预期结果目录和实际结果目录
    18. expectDir=expectXmldir+interface_name
    19. realDir=realXmldir+interface_name
    20. if os.path.exists(expectDir) == 0:
    21. os.makedirs(expectDir)
    22. if os.path.exists(realDir) == 0:
    23. os.makedirs(realDir)
    24. excelobj.del_testrecord(suiteid) #清除历史测试数据
    25. casecount=excelobj.get_ncase(suiteid) #获取case个数
    26. caseinfolist=get_caseinfo(excelobj, suiteid) #获取Case基本信息
    27. #遍历执行case
    28. for caseid in range(0, casecount):
    29. #检查是否执行该Case
    30. if excelobj.read_data(suiteid,excelobj.casebegin+caseid, 2)=='N':
    31. write_result(excelobj, suiteid, caseid, excelobj.resultCol, 'NT')
    32. continue #当前Case结束,继续执行下一个Case
    33. #获取测试数据
    34. sInput=httpString+get_input(excelobj, suiteid, caseid, caseinfolist)
    35. XmlString=HTTPInvoke(com_ipport, sInput) #执行调用
    36. #获取返回码并比较
    37. result_code=et.fromstring(XmlString).find("result_code").text
    38. ret1=check_result(excelobj, suiteid, caseid,result_code, excelobj.retCol)
    39. #保存预期结果文件
    40. expectPath=expectDir+'/'+str(caseid+1)+'.xml'
    41. #saveXmlfile(expectPath, XmlString)
    42. #保存实际结果文件
    43. realPath=realDir+'/'+str(caseid+1)+'.xml'
    44. saveXmlfile(realPath, XmlString)
    45. #比较预期结果和实际结果
    46. ret2= check_xmlfile(excelobj, suiteid, caseid,expectPath, realPath)
    47. #写测试结果
    48. write_result(excelobj, suiteid, caseid, excelobj.resultCol, ret1, ret2)
    49. print '【'+interface_name+'】' + ' Test End!'

    3、测试入口

    1. # -*- coding: utf-8 -*-
    2. #****************************************************************
    3. # main.py
    4. # Author : Vince
    5. # Version : 1.0
    6. # Date : 2011-3-16
    7. # Description: 测试组装,用例执行入口
    8. #****************************************************************
    9. from testframe import *
    10. from xxx_server_case import *
    11. import xxx_server_case
    12. #产品系统接口测试
    13. #设置测试环境
    14. xxx_server_case.excelobj=create_excel(os.getcwd()+'/TestDir/xxx_Testcase.xls')
    15. xxx_server_case.com_ipport=xxx.com'
    16. #Add testsuite begin
    17. run("xxx_book_list", 4)
    18. #Add other suite from here
    19. #Add testsuite end
    20. statisticresult(xxx_server_case.excelobj)
    21. xxx_server_case.excelobj.close()
    总结:

    感谢每一个认真阅读我文章的人!!!

    作为一位过来人也是希望大家少走一些弯路,如果你不想再体验一次学习时找不到资料,没人解答问题,坚持几天便放弃的感受的话,在这里我给大家分享一些自动化测试的学习资源,希望能给你前进的路上带来帮助。

     文档获取方式:

    加入我的软件测试交流群:632880530免费获取~(同行大佬一起学术交流,每晚都有大佬直播分享技术知识点)

    这份文档,对于想从事【软件测试】的朋友来说应该是最全面最完整的备战仓库,这个仓库也陪伴我走过了最艰难的路程,希望也能帮助到你!

    以上均可以分享,只需要你搜索vx公众号:程序员雨果,即可免费领取

  • 相关阅读:
    大家都说单测没啥用,这是真的吗?
    「功能实现」我封装了一个表单组件,感觉离财富自由又近了一步
    万界星空科技低代码平台+协同制造MES产品
    图搜索算法 - 深度优先搜索法(DFS)
    【Python Web】Flask框架(四)Bootstrap的使用及案例
    从0到1,企业如何快速构建自己的销售体系
    00后整顿职场!因面试时公司让填“紧急联系人”,觉得侵犯隐私,举报公司消防措施不合理,导致公司被停业整顿!...
    java毕业设计盘山县智慧项目管理系统源码+lw文档+mybatis+系统+mysql数据库+调试
    [计算机效率] 文本编辑工具:Notepad++
    Android native层Hander原理分析
  • 原文地址:https://blog.csdn.net/2301_79535544/article/details/133910538