http接口自动化测试框架实现
作者:张元礼
http://blog.csdn.net/vincetest
一、测试需求描述
对服务后台一系列的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、测试框架代码
view plaincopy to clipboardprint?01.#****************************************************************02.# TestFrame.py03.# Author : Vince04.# Version : 1.1.205.# Date : 2011-3-1406.# Description: 自动化测试平台07.#****************************************************************08.09.import os,sys, urllib, httplib, profile, datetime, time10.from xml2dict import XML2Dict
11.import win32com.client
12.from win32com.client import Dispatch
13.import xml.etree.ElementTree as et
14.#import MySQLdb
15.16.#Excel表格中测试结果底色17.OK_COLOR=0xffffff18.NG_COLOR=0xff19.#NT_COLOR=0xffff20.NT_COLOR=0xC0C0C021.22.#Excel表格中测试结果汇总显示位置23.TESTTIME=[1, 14]24.TESTRESULT=[2, 14]25.26.#Excel模版设置27.#self.titleindex=3 #Excel中测试用例标题行索引28.#self.casebegin =4 #Excel中测试用例开始行索引29.#self.argbegin =3 #Excel中参数开始列索引30.#self.argcount =8 #Excel中支持的参数个数31.class create_excel:
32. def __init__(self, sFile, dtitleindex=3, dcasebegin=4, dargbegin=3, dargcount=8):
33. self.xlApp = win32com.client.Dispatch('et.Application') #MS:Excel WPS:et34. try:35. self.book = self.xlApp.Workbooks.Open(sFile)36. except:37. print_error_info()38. print "打开文件失败"39. exit()
40. self.file=sFile41. self.titleindex=dtitleindex42. self.casebegin=dcasebegin43. self.argbegin=dargbegin44. self.argcount=dargcount45. self.allresult=[]46.47. self.retCol=self.argbegin+self.argcount48. self.xmlCol=self.retCol+149. self.resultCol=self.xmlCol+150.51. def close(self):52. #self.book.Close(SaveChanges=0)53. self.book.Save()54. self.book.Close()55. #self.xlApp.Quit()56. del self.xlApp57.58. def read_data(self, iSheet, iRow, iCol):
59. try:60. sht = self.book.Worksheets(iSheet)61. sValue=str(sht.Cells(iRow, iCol).Value)62. except:63. self.close()
64. print('读取数据失败')
65. exit()
66. #去除'.0'67. if sValue[-2:]=='.0':
68. sValue = sValue[0:-2]69. return sValue
70.71. def write_data(self, iSheet, iRow, iCol, sData, color=OK_COLOR):
72. try:73. sht = self.book.Worksheets(iSheet)74. sht.Cells(iRow, iCol).Value = sData.decode("utf-8")75. sht.Cells(iRow, iCol).Interior.Color=color76. self.book.Save()77. except:78. self.close()
79. print('写入数据失败')
80. exit()
81.82. #获取用例个数83. def get_ncase(self, iSheet):
84. try:85. return self.get_nrows(iSheet)-self.casebegin+1
86. except:87. self.close()
88. print('获取Case个数失败')
89. exit()
90.91. def get_nrows(self, iSheet):
92. try:93. sht = self.book.Worksheets(iSheet)94. return sht.UsedRange.Rows.Count
95. except:96. self.close()
97. print('获取nrows失败')
98. exit()
99.100. def get_ncols(self, iSheet):
101. try:102. sht = self.book.Worksheets(iSheet)103. return sht.UsedRange.Columns.Count
104. except:105. self.close()
106. print('获取ncols失败')
107. exit()
108.109. def del_testrecord(self, suiteid):
110. try:111. #为提升性能特别从For循环提取出来112. nrows=self.get_nrows(suiteid)+1113. ncols=self.get_ncols(suiteid)+1114. begincol=self.argbegin+self.argcount115.116. #提升性能117. sht = self.book.Worksheets(suiteid)118.119. for row in range(self.casebegin, nrows):120. for col in range(begincol, ncols):121. str=self.read_data(suiteid, row, col)122. #清除实际结果[]123. startpos = str.find('[')
124. if startpos>0:
125. str = str[0:startpos].strip()
126. self.write_data(suiteid, row, col, str, OK_COLOR)127. else:
128. #提升性能129. sht.Cells(row, col).Interior.Color = OK_COLOR130. #清除TestResul列中的测试结果,设置为NT131. self.write_data(suiteid, row, self.argbegin+self.argcount+1, ' ', OK_COLOR)132. self.write_data(suiteid, row, self.resultCol, 'NT', NT_COLOR)133. except:134. self.close()
135. print('清除数据失败')
136. exit()
137.138.#执行调用139.def HTTPInvoke(IPPort, url):
140. conn = httplib.HTTPConnection(IPPort)141. conn.request("GET", url)
142. rsps = conn.getresponse()143. data = rsps.read()144. conn.close()
145. return data146.147.#获取用例基本信息[Interface,argcount,[ArgNameList]]148.def get_caseinfo(Data, SuiteID):
149. caseinfolist=[]150. sInterface=Data.read_data(SuiteID, 1, 2)151. argcount=int(Data.read_data(SuiteID, 2, 2))152.153. #获取参数名存入ArgNameList154. ArgNameList=[]155. for i in range(0, argcount):156. ArgNameList.append(Data.read_data(SuiteID, Data.titleindex, Data.argbegin+i))157.158. caseinfolist.append(sInterface)159. caseinfolist.append(argcount)160. caseinfolist.append(ArgNameList)161. return caseinfolist
162.163.#获取输入164.def get_input(Data, SuiteID, CaseID, caseinfolist):
165. sArge=''166. #参数组合167. for j in range(0, caseinfolist[1]):168. if Data.read_data(SuiteID, Data.casebegin+CaseID, Data.argbegin+j) != "None":169. sArge=sArge+caseinfolist[2][j]+'='+Data.read_data(SuiteID, Data.casebegin+CaseID, Data.argbegin+j)+'&'170.171. #去掉结尾的&字符172. if sArge[-1:]=='&':
173. sArge = sArge[0:-1]174. sInput=caseinfolist[0]+sArge #组合全部参数175. return sInput
176.177.#结果判断178.def assert_result(sReal, sExpect):
179. sReal=str(sReal)180. sExpect=str(sExpect)181. if sReal==sExpect:
182. return 'OK'
183. else:
184. return 'NG'
185.186.#将测试结果写入文件187.def write_result(Data, SuiteId, CaseId, resultcol, *result):
188. if len(result)>1:
189. ret='OK'190. for i in range(0, len(result)):191. if result[i]=='NG':
192. ret='NG'193. break194. if ret=='NG':
195. Data.write_data(SuiteId, Data.casebegin+CaseId, resultcol,ret, NG_COLOR)196. else:
197. Data.write_data(SuiteId, Data.casebegin+CaseId, resultcol,ret, OK_COLOR)198. Data.allresult.append(ret)199. else:
200. if result[0]=='NG':
201. Data.write_data(SuiteId, Data.casebegin+CaseId, resultcol,result[0], NG_COLOR)202. elif result[0]=='OK':203. Data.write_data(SuiteId, Data.casebegin+CaseId, resultcol,result[0], OK_COLOR)204. else: #NT
205. Data.write_data(SuiteId, Data.casebegin+CaseId, resultcol,result[0], NT_COLOR)206. Data.allresult.append(result[0])207.208. #将当前结果立即打印209. print 'case'+str(CaseId+1)+':', Data.allresult[-1]
210.211.#打印测试结果212.def statisticresult(excelobj):
213. allresultlist=excelobj.allresult214. count=[0, 0, 0]
215. for i in range(0, len(allresultlist)):216. #print 'case'+str(i+1)+':', allresultlist[i]
217. count=countflag(allresultlist[i],count[0], count[1], count[2])218. print 'Statistic result as follow:'
219. print 'OK:', count[0]220. print 'NG:', count[1]221. print 'NT:', count[2]222.223.#解析XmlString返回Dict224.def get_xmlstring_dict(xml_string):
225. xml = XML2Dict()226. return xml.fromstring(xml_string)
227.228.#解析XmlFile返回Dict229.def get_xmlfile_dict(xml_file):
230. xml = XML2Dict()231. return xml.parse(xml_file)
232.233.#去除历史数据expect[real]234.def delcomment(excelobj, suiteid, iRow, iCol, str):
235. startpos = str.find('[')
236. if startpos>0:
237. str = str[0:startpos].strip()
238. excelobj.write_data(suiteid, iRow, iCol, str, OK_COLOR)239. return str
240.241.#检查每个item (非结构体)242.def check_item(excelobj, suiteid, caseid,real_dict, checklist, begincol):
243. ret='OK'244. for checkid in range(0, len(checklist)):245. real=real_dict[checklist[checkid]]['value']246. expect=excelobj.read_data(suiteid, excelobj.casebegin+caseid, begincol+checkid)247.248. #如果检查不一致测将实际结果写入expect字段,格式:expect[real]249. #将return NG250. result=assert_result(real, expect)251. if result=='NG':
252. writestr=expect+'['+real+']'253. excelobj.write_data(suiteid, excelobj.casebegin+caseid, begincol+checkid, writestr, NG_COLOR)254. ret='NG'255. return ret
256.257.#检查结构体类型258.def check_struct_item(excelobj, suiteid, caseid,real_struct_dict, structlist, structbegin, structcount):
259. ret='OK'260. if structcount>1: #传入的是List
261. for structid in range(0, structcount):262. structdict=real_struct_dict[structid]263. temp=check_item(excelobj, suiteid, caseid,structdict, structlist, structbegin+structid*len(structlist))264. if temp=='NG':
265. ret='NG'266.267. else: #传入的是Dict
268. temp=check_item(excelobj, suiteid, caseid,real_struct_dict, structlist, structbegin)269. if temp=='NG':
270. ret='NG'271.272. return ret
273.274.#获取异常函数及行号275.def print_error_info():
276. """Return the frame object for the caller's stack frame."""
277. try:278. raise Exception279. except:280. f = sys.exc_info()[2].tb_frame.f_back281. print (f.f_code.co_name, f.f_lineno)
282.283.#测试结果计数器,类似Switch语句实现284.def countflag(flag,ok, ng, nt):
285. calculation = {'OK':lambda:[ok+1, ng, nt],286. 'NG':lambda:[ok, ng+1, nt],287. 'NT':lambda:[ok, ng, nt+1]}288. return calculation[flag]()
2、项目测试代码
view plaincopy to clipboardprint?01.# -*- coding: utf-8 -*-02.#****************************************************************03.# xxx_server_case.py04.# Author : Vince05.# Version : 1.006.# Date : 2011-3-1007.# Description: 内容服务系统测试代码08.#****************************************************************09.10.from testframe import *
11.from common_lib import *
12.13.httpString='http://xxx.com/xxx_product/test/'
14.expectXmldir=os.getcwd()+'/TestDir/expect/'
15.realXmldir=os.getcwd()+'/TestDir/real/'
16.17.def run(interface_name, suiteid):
18. print '【'+interface_name+'】' + ' Test Begin,please waiting...'
19. global expectXmldir, realXmldir20.21. #根据接口名分别创建预期结果目录和实际结果目录22. expectDir=expectXmldir+interface_name23. realDir=realXmldir+interface_name24. if os.path.exists(expectDir) == 0:25. os.makedirs(expectDir)
26. if os.path.exists(realDir) == 0:27. os.makedirs(realDir)
28.29. excelobj.del_testrecord(suiteid) #清除历史测试数据30. casecount=excelobj.get_ncase(suiteid) #获取case个数31. caseinfolist=get_caseinfo(excelobj, suiteid) #获取Case基本信息32.33. #遍历执行case34. for caseid in range(0, casecount):35. #检查是否执行该Case36. if excelobj.read_data(suiteid,excelobj.casebegin+caseid, 2)=='N':
37. write_result(excelobj, suiteid, caseid, excelobj.resultCol, 'NT')38. continue #当前Case结束,继续执行下一个Case39.40. #获取测试数据41. sInput=httpString+get_input(excelobj, suiteid, caseid, caseinfolist)42. XmlString=HTTPInvoke(com_ipport, sInput) #执行调用43.44. #获取返回码并比较45. result_code=et.fromstring(XmlString).find("result_code").text46. ret1=check_result(excelobj, suiteid, caseid,result_code, excelobj.retCol)47.48. #保存预期结果文件49. expectPath=expectDir+'/'+str(caseid+1)+'.xml'50. #saveXmlfile(expectPath, XmlString)51.52. #保存实际结果文件53. realPath=realDir+'/'+str(caseid+1)+'.xml'54. saveXmlfile(realPath, XmlString)55.56. #比较预期结果和实际结果57. ret2= check_xmlfile(excelobj, suiteid, caseid,expectPath, realPath)58.59. #写测试结果60. write_result(excelobj, suiteid, caseid, excelobj.resultCol, ret1, ret2)61. print '【'+interface_name+'】' + ' Test End!'
3、测试入口
view plaincopy to clipboardprint?01.# -*- coding: utf-8 -*-02.#****************************************************************03.# main.py04.# Author : Vince05.# Version : 1.006.# Date : 2011-3-1607.# Description: 测试组装,用例执行入口08.#****************************************************************09.10.from testframe import *
11.from xxx_server_case import *
12.import xxx_server_case
13.14.#产品系统接口测试15.#设置测试环境16.xxx_server_case.excelobj=create_excel(os.getcwd()+'/TestDir/xxx_Testcase.xls')
17.xxx_server_case.com_ipport=xxx.com'18.19.#Add testsuite begin20.run("xxx_book_list", 4)
21.#Add other suite from here22.#Add testsuite end
23.24.statisticresult(xxx_server_case.excelobj)25.xxx_server_case.excelobj.close()
最后感谢我的同事Roger为此做了一些优化,后续优化的东东还很多,我们一直在努力!
欢迎转载此文,转载时请注明文章来源:张元礼的博客 http://blog.csdn.net/vincetest
本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/vincetest/archive/2011/04/22/6341658.aspx#1655236