51Testing软件测试论坛

 找回密码
 (注-册)加入51Testing

QQ登录

只需一步,快速开始

微信登录,快人一步

手机号码,快捷登录

查看: 6661|回复: 2
打印 上一主题 下一主题

利用fitnesse实现api接口自动化测试

[复制链接]

该用户从未签到

跳转到指定楼层
1#
发表于 2018-3-6 13:46:45 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
看了不少小伙伴们分享的接口测试方面的知识,仔细想想,我做接口测试也有几个年头了,大家所叙
述到的一些经验或多或少,我也曾遇到过,突然意识到知识的点滴积累是多么的重要,我记得我最早
接触接口测试的时候,就是只在浏览器里人工测试单个接口的返回结果,后来用python的unittest自
己写测试框架,和现在大多数小伙伴们的方法差不多,测试用例也是存放在excle表中,这对于单人
测试来说都还ok,但是如果是多人协同测试时,问题就出来了,因为按目录存放在不同的excle表中
的测试用例,维护起来比较麻烦,而且不便于多人查询或共同维护测试用例。所以之前的公司老大给
我们推荐了一个工具——fitnesse,它是用wiki方式在界面上管理测试用例,驱动后台脚本进行测试,
因为测试用例界面是个wiki地址,可以很方便的和大家一起协同工作,而且用例查询和维护起来都方
便的多。
      我在之前的公司用了将近一年这个工具,但是因为刚休完产假的自己工作状态非常不好,其实并
没有在这方面很尽心,只到来了新公司后,才又一次重新学习使用起这个工具来,还是api接口测试,
年前我从不同的角度分别写了几个不同的demo,虽然现在大多数用fitnesse的人都是用的slim引擎,
但是因为java helloworld水平的我一直用的python,找不到合适的支持slim的python插件,所以还是
用的fitnesse的fit引擎,用PyFIT支持起来。

     对于api接口功能测试,我个人认为需要关注的有这几个方面:接口状态,响应时间,字段格式,
返回数据,想起来之前面试阿里时,提到接口测试,他问了我很多http协议类的,具体问题记不清
了,大概是几次握手交互那类的,额,不知道是不是关注点不同的缘故,他问得几个关于接口测试的
问题我都没用过,其实到现在我还是有些疑惑的,不知道是他理解的接口测试和我理解的有偏差,不
过也许是我涉猎的领域太窄,对于接口应该怎么充分测试我是比较怀疑了,也经常会在网上翻阅关于
接口测试的文档,但是感觉收效甚微,嗯,对于未知领域的探索仍在继续,对于已知领域的小果子,
拿出来和小伙伴一起分享下吧

     所测接口:api接口,返回结果json格式

     所用工具:fitnesse,fit引擎,python

第一种方式:

   测试思想:在页面上初始化测试数据,将接口的返回结果按每个字段逐一填写期望结果,和接口
的实际结果比较

   测试数据准备:在界面上利用sql语句初始化测试数据,然后在测试用例页面included 该页面


测试用例:将json各个字段拆开填写到测试用例表格中,用ColumnFixture,测试用例格式如下

baseurl:接口基本不变的部分,这一部分可以在表格外参数化然后传值到表格里,account和pass
word是接口的两个输入参数,带?标识的是要验证的结果,将json返回结果的每个字段都拆开填写
在表格中。

后台脚本:
  1. class LoginTest(ColumnFixture):
  2.       _typeDict = {
  3.         "description":"String",
  4.         "BaseUrl":"String",
  5.         "account":"String",
  6.         "password":"String",
  7.         "status":"Int",
  8.         "retMsg":"String",
  9.         "token":"String",
  10.         "uClen":"Int",
  11.         "uCuserId":"Int",
  12.         "uCamount":"String",
  13.         }

  14.       def __init__(self):   
  15.           ColumnFixture.__init__(self)
  16.           self.account=''
  17.           self.password=''
  18.           self.BaseUrl=''
  19.           self.jsonData=''
  20.           self.ret=''      
  21.       def getRes(self):               
  22.           url=self.BaseUrl+"account="+self.account+"&password="+self.password
  23.           tmp=res.fetch_res(url)   
  24.           result = json.loads(tmp)
  25.           return result
  26.       def retMsg(self):
  27.           self.jsonData=self.getRes()
  28.           self.ret=self.jsonData["ret"]
  29.           result=str(self.jsonData["ret"])+self.jsonData["msg"]
  30.           return result
  31.       def status(self):
  32.           url=self.BaseUrl+"account="+self.account+"&password="+self.password
  33.           result=res.fetch_status(url)
  34.           return result   
  35.       def token(self):
  36.           result=''
  37.           if self.ret==1:
  38.               result= self.jsonData["data"]["token"]
  39.           return result
  40.       def uClen(self):
  41.           result=''
  42.           if self.ret==1:
  43.               result=len(self.jsonData["data"]["userCapital"])
  44.           return result
  45.       def uCuserId(self):
  46.           result=''
  47.           if self.ret==1:  
  48.               result=self.jsonData["data"]["userCapital"]["userId"]
  49.           return result
  50.       def uCamount(self):
  51.           result=''
  52.           if self.ret==1:
  53.               result=str(self.jsonData["data"]["userCapital"]["amount"])
  54.           return result
复制代码
优点:测试脚本结构简单,测试用例格式清晰,缺点:如果接口返回层级或字段较多时,不便于测试
用例维护,需要初始化测试数据并清除增加的数据,部分动态字段(比如creattime)无法准确校验

第二种方式:

测试思想:在已有数据库基础上,无需每次添加测试数据,在测试脚本中根据需求用sql语句检索出
对应字段的数据,作为期望结果,和接口的实际结果比较

测试数据:已有数据库基础上

测试用例:

测试脚本部分示例:

  1. def retMsg(self):
  2.             if self.status==200:
  3.                 self.jsonData=self.getRes()
  4.                 isUserSql="SELECT * FROM hcm_user WHERE NAME LIKE \'"+self.account+"\' AND PASSWORD LIKE \'"+self.password+ "\'AND TYPE=0"
  5.                 self.isUser=db.queryDb(isUserSql)              
  6.                 self.ret=self.jsonData["ret"]
  7.                 result=str(self.jsonData["ret"])+self.jsonData["msg"]
  8.                 return result
  9.             else:
  10.                 return ''
  11.         def securityStatusCheck(self):
  12.             symbol="="
  13.             list=['userId','userName','emailStatus','mobileStatus','realNameAuthStatus','autoTransfer','trusteeshipAccountStatus']
  14.             dataJson=[]
  15.             dataCase=[]
  16.             if self.isUser:
  17.                 sql="SELECT a.id,a.`name`,IF(a.email!='',1,0),IF(a.`mobile`,1,0),b.`yeepay_account_status`,b.auto_transfer,b.`yeepay_account_status` FROM hcm_user a,hcm_user_auth b WHERE NAME LIKE '"+self.account+"' AND a.id=b.user_id"               
  18.                 data=db.queryDb(sql)
  19.                 if data:
  20.                     for i in range (0,len(list)):
  21.                         dataCase.append(list[i]+symbol+str(data[0][i]))
  22.             if self.ret==1:
  23.                 tmp=self.jsonData["data"]["securityStatus"]
  24.                 for i in range (0,len(list)):
  25.                     dataJson.append(list[i]+symbol+str(tmp[list[i]]))
  26.             result=Check(dataJson, dataCase)
  27.             return result
复制代码
测试结果:


突然发现给自己写优缺点好二啊,反正就是上面两种都没有满足老大们的要求,他们希望我能写一个
通用的框架,让没有任何编码能力的人也能进行接口测试,即只需要前台编写测试用例,不用管后台
脚本就能进行测试,于是乎有了下面第三种方式

第三种方式:

测试思想:满足老大们的要求,不用编写任何脚本即可进行接口测试

测试数据:固定初始化好的数据库

测试用例:


其中,firsturl是被依赖的登录接口,url是所测接口,blackLIst是希望过滤de返回字段的黑名单(比如
ordeId,每次都是变化的,无法准确校验,添加到黑名单中即可不对其校验),data是期望结果,因
为所测接口需要先登录然后保持session,才能返回正常结果,所以此处采用的是fit的Actionfixture

测试脚本示例:
  1. from fit.Fixture import Fixture
  2. import urllib2,cookielib,urllib
  3. import module,json
  4. import sys
  5. reload(sys)
  6. sys.setdefaultencoding('UTF-8')

  7. class ActionTest(Fixture):
  8.     _typeDict = {}

  9.     def __init__(self):#初始化参数
  10.         Fixture.__init__(self)
  11.         self.__firstUrl  = ''   #< Private attributes (Python convention).
  12.         self.__url = ''
  13.         self.__parameter = ''
  14.         self.__blackList=''
  15.         self.__data=''
  16.         self.res=''
  17.         self.status=''
  18.         self.expectedList=''
  19.         self.actualList=''
  20.         self.test=''

  21.     _typeDict["firstUrl"] = "String"
  22.     def firstUrl(self, s):
  23.         self.__firstUrl = s
  24.     _typeDict["url"] = "String"
  25.     def url(self, s):
  26.         self.__url = s
  27.     _typeDict["parameter"] = "Dict"
  28.     def parameter(self, s):
  29.         self.__parameter = s
  30.     _typeDict["blackList"] = "List"
  31.     def blackList(self, s):
  32.         self.__blackList = s
  33.     _typeDict["data"] = "String"
  34.     def data(self, s):
  35.         self.__data = s
  36.     _typeDict["do"] = "Default"      #< AUTO-DETECT: None = void
  37.    
  38.     def do(self):#访问接口并保存结果
  39.         cookie=cookielib.CookieJar()
  40.         opener=urllib2.build_opener(urllib2.HTTPCookieProcessor(cookie))
  41.         try:
  42.             req=opener.open(self.__firstUrl)
  43.             self.status=req.code
  44.         except urllib2.HTTPError, e:
  45.             self.status= e.code
  46.         if self.status==200:
  47.             for cj in cookie:
  48.                 if cj.name=='JSESSIONID':
  49.                     session= cj.value
  50.             req=urllib2.Request(self.__url)
  51.             data=urllib.urlencode(self.__parameter)
  52.             try:
  53.                 tmp = opener.open(req,data)
  54.                 self.status=tmp.code
  55.             except urllib2.HTTPError, e:
  56.                 self.status=e.code
  57.             if self.status==200:
  58.                 self.res= tmp.read()
  59.             else:
  60.                 self.res='{"status":"no 200"}'
  61.         else:
  62.             self.res='{"loginStatus":"no 200"}'
  63.     _typeDict["status"] = "Int"
  64.     def status(self):
  65.         return self.status  
  66.     _typeDict["expect"] = "String"
  67.     def expect(self):#调用module函数比较测试结果
  68.         self.expectedList=[]
  69.         self.actualList=[]
  70.         module.resultList(self.__blackList,self.__data, self.res, self.expectedList, self.actualList)#比较后将结果存放到输出数组中
  71.         result=module.outPut(self.expectedList)
  72.         #tmp=unicode(self.__data, 'utf-8')
  73.         #return str(self.actualList
  74.         return result
  75.     _typeDict["actual"] = "String"
  76.     def actual(self):#调用module函数比较测试结果
  77.         result=module.outPut(self.actualList)  
  78.         return result
  79. 复制代码
  80. 测试结果:
复制代码


本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?(注-册)加入51Testing

x
分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏1
回复

使用道具 举报

  • TA的每日心情
    郁闷
    2017-1-11 15:48
  • 签到天数: 1 天

    连续签到: 1 天

    [LV.1]测试小兵

    3#
    发表于 2018-4-30 12:23:12 | 只看该作者
    谢谢分享。 能介绍和Jmeter做接口测试的区别吗?
    回复 支持 反对

    使用道具 举报

    本版积分规则

    关闭

    站长推荐上一条 /1 下一条

    小黑屋|手机版|Archiver|51Testing软件测试网 ( 沪ICP备05003035号 关于我们

    GMT+8, 2024-11-23 08:42 , Processed in 0.070728 second(s), 23 queries .

    Powered by Discuz! X3.2

    © 2001-2024 Comsenz Inc.

    快速回复 返回顶部 返回列表