利用fitnesse實現api接口自動化測試


      上午在園子里亂逛,看了不少小伙伴們分享的接口測試方面的知識,仔細想想,我做接口測試也有幾個年頭了,大家所敘述到的一些經驗或多或少,我也曾遇到過,突然意識到知識的點滴積累是多么的重要,我記得我最早接觸接口測試的時候,就是只在瀏覽器里人工測試單個接口的返回結果,后來用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和password是接口的兩個輸入參數,帶?標識的是要驗證的結果,將json返回結果的每個字段都拆開填寫在表格中。

 

后台腳本:

class LoginTest(ColumnFixture):
      _typeDict = {
        "description":"String",
        "BaseUrl":"String",
        "account":"String",
        "password":"String",
        "status":"Int",
        "retMsg":"String",
        "token":"String",
        "uClen":"Int",
        "uCuserId":"Int",
        "uCamount":"String",
        }

      def __init__(self):   
          ColumnFixture.__init__(self)
          self.account=''
          self.password=''
          self.BaseUrl=''
          self.jsonData='' 
          self.ret=''      
      def getRes(self):                
url=self.BaseUrl+"account="+self.account+"&password="+self.password tmp=res.fetch_res(url) result = json.loads(tmp) return result def retMsg(self): self.jsonData=self.getRes() self.ret=self.jsonData["ret"] result=str(self.jsonData["ret"])+self.jsonData["msg"] return result def status(self): url=self.BaseUrl+"account="+self.account+"&password="+self.password result=res.fetch_status(url) return result def token(self): result='' if self.ret==1: result= self.jsonData["data"]["token"] return result def uClen(self): result='' if self.ret==1: result=len(self.jsonData["data"]["userCapital"]) return result def uCuserId(self): result='' if self.ret==1: result=self.jsonData["data"]["userCapital"]["userId"] return result def uCamount(self): result='' if self.ret==1: result=str(self.jsonData["data"]["userCapital"]["amount"]) return result

優點:測試腳本結構簡單,測試用例格式清晰,缺點:如果接口返回層級或字段較多時,不便於測試用例維護,需要初始化測試數據並清除增加的數據,部分動態字段(比如creattime)無法准確校驗

第二種方式:

測試思想:在已有數據庫基礎上,無需每次添加測試數據,在測試腳本中根據需求用sql語句檢索出對應字段的數據,作為期望結果,和接口的實際結果比較

測試數據:已有數據庫基礎上

測試用例:

 測試腳本部分示例:

 def retMsg(self):
            if self.status==200: 
                self.jsonData=self.getRes()
                isUserSql="SELECT * FROM hcm_user WHERE NAME LIKE \'"+self.account+"\' AND PASSWORD LIKE \'"+self.password+ "\'AND TYPE=0"
                self.isUser=db.queryDb(isUserSql)              
                self.ret=self.jsonData["ret"]
                result=str(self.jsonData["ret"])+self.jsonData["msg"]
                return result
            else:
                return ''
        def securityStatusCheck(self):
            symbol="="
            list=['userId','userName','emailStatus','mobileStatus','realNameAuthStatus','autoTransfer','trusteeshipAccountStatus']
            dataJson=[]
            dataCase=[]
            if self.isUser:
                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"               
                data=db.queryDb(sql)
                if data:
                    for i in range (0,len(list)):
                        dataCase.append(list[i]+symbol+str(data[0][i]))
            if self.ret==1:
                tmp=self.jsonData["data"]["securityStatus"]
                for i in range (0,len(list)):
                    dataJson.append(list[i]+symbol+str(tmp[list[i]]))
            result=Check(dataJson, dataCase)
            return result

測試結果:

突然發現給自己寫優缺點好二啊,反正就是上面兩種都沒有滿足老大們的要求,他們希望我能寫一個通用的框架,讓沒有任何編碼能力的人也能進行接口測試,即只需要前台編寫測試用例,不用管后台腳本就能進行測試,於是乎有了下面第三種方式

第三種方式:

測試思想:滿足老大們的要求,不用編寫任何腳本即可進行接口測試

測試數據:固定初始化好的數據庫

測試用例:

其中,firsturl是被依賴的登錄接口,url是所測接口,blackLIst是希望過濾de返回字段的黑名單(比如ordeId,每次都是變化的,無法准確校驗,添加到黑名單中即可不對其校驗),data是期望結果,因為所測接口需要先登錄然后保持session,才能返回正常結果,所以此處采用的是fit的Actionfixture

測試腳本示例:

from fit.Fixture import Fixture 
import urllib2,cookielib,urllib
import module,json
import sys
reload(sys)
sys.setdefaultencoding('UTF-8')

class ActionTest(Fixture):
    _typeDict = {}

    def __init__(self):#初始化參數
        Fixture.__init__(self)
        self.__firstUrl  = ''   #< Private attributes (Python convention).
        self.__url = ''
        self.__parameter = ''
        self.__blackList=''
        self.__data=''
        self.res=''
        self.status=''
        self.expectedList=''
        self.actualList=''
        self.test=''

    _typeDict["firstUrl"] = "String"
    def firstUrl(self, s):
        self.__firstUrl = s
    _typeDict["url"] = "String"
    def url(self, s):
        self.__url = s
    _typeDict["parameter"] = "Dict"
    def parameter(self, s):
        self.__parameter = s
    _typeDict["blackList"] = "List"
    def blackList(self, s):
        self.__blackList = s
    _typeDict["data"] = "String"
    def data(self, s):
        self.__data = s
    _typeDict["do"] = "Default"      #< AUTO-DETECT: None = void
    
    def do(self):#訪問接口並保存結果
        cookie=cookielib.CookieJar()
        opener=urllib2.build_opener(urllib2.HTTPCookieProcessor(cookie))
        try:
            req=opener.open(self.__firstUrl)
            self.status=req.code
        except urllib2.HTTPError, e:
            self.status= e.code
        if self.status==200:
            for cj in cookie:
                if cj.name=='JSESSIONID':
                    session= cj.value
            req=urllib2.Request(self.__url)
            data=urllib.urlencode(self.__parameter)
            try:
                tmp = opener.open(req,data)
                self.status=tmp.code
            except urllib2.HTTPError, e:
                self.status=e.code
            if self.status==200:
                self.res= tmp.read()
            else:
                self.res='{"status":"no 200"}'
        else:
            self.res='{"loginStatus":"no 200"}'
    _typeDict["status"] = "Int"
    def status(self):
        return self.status  
    _typeDict["expect"] = "String"
    def expect(self):#調用module函數比較測試結果
        self.expectedList=[]
        self.actualList=[]
        module.resultList(self.__blackList,self.__data, self.res, self.expectedList, self.actualList)#比較后將結果存放到輸出數組中 
        result=module.outPut(self.expectedList)
        #tmp=unicode(self.__data, 'utf-8')
        #return str(self.actualList
        return result
    _typeDict["actual"] = "String"
    def actual(self):#調用module函數比較測試結果
        result=module.outPut(self.actualList)  
        return result 

測試結果:

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM