python接口自動化框架整理

這個接口自動化框架是基于unittest單元測試框架來寫的,簡單偶洋,還算可以用吧幽邓,也有些不合理的地方,歡迎大神指正呀撤蟆!

接口自動化框架布局

image

這個框架一共分為6個部分:

common:公共代碼模塊奕塑,包括一些公共的代碼和測試用例的代碼模塊

conf:存放配置文件的模塊,包括數(shù)據(jù)庫配置信息家肯,項目ip地址龄砰,框架所在路徑,郵件的配置等

logs:存放用例執(zhí)行過程中所生成的日志信息

test_data:存放測試數(shù)據(jù)(這里用的excel)

test_report:用于存放生成的測試報告

main執(zhí)行文件:用于執(zhí)行測試,并在測試完成后生成測試報告和發(fā)送郵件

公共代碼

先看下公共代碼的構(gòu)成

發(fā)起http請求的類:

image

這里我調(diào)用的是requests模塊换棚,可發(fā)起post請求或get請求式镐,根據(jù)傳入的method來進(jìn)行判斷,然后我這里直接返回json格式的數(shù)據(jù)固蚤,這個便于之后對數(shù)據(jù)的一些處理

讀取配置文件的類:

image

這個類我調(diào)用的configparser庫娘汞,主要就是用來讀取conf目錄中的配置文件,這樣的話就可以實現(xiàn)數(shù)據(jù)的分離夕玩,數(shù)據(jù)庫配置信息你弦,ip等配置就不用在代碼中寫死,可實現(xiàn)可配置化风秤,以便于進(jìn)行修改

讀寫execl的類:

'''
    #用于讀取測試數(shù)據(jù)鳖目,寫出測試結(jié)果的類
    from common.regular_pick import Regular
    from openpyxl import load_workbook
    from common.config import Config
    from common.loger import Log
    from conf.projectpath import *
    #實例化正則表達(dá)式提取器
    reg=Regular()
    #日志模塊實例化
    log=Log()
    class DoExcel():
      def __init__(self,workbook_name,sheet_name):
            self.workbook_name=workbook_name
            self.sheet_name=sheet_name
            self.init_sheet_name='init'
            self.ip=Config().configer(http_path,'HTTP','ip')
  #將測試的結(jié)果回寫寫入到excel中
    def write_excel(self,row,result_data):
        #打開工作薄
          wb=load_workbook(self.workbook_name)
        #打開表單
          sh_new=wb[self.sheet_name]
          sh_new.cell(row=row+1,column=10).value=result_data['actually_code']
          sh_new.cell(row=row+1,column=11).value=result_data['sql_result']
          sh_new.cell(row=row+1,column=12).value=result_data['result']
          sh_new.cell(row=row+1,column=13).value=result_data['reson']
          wb.save(self.workbook_name)
   #讀取初始化手機號
    def read_initphone(self):
        wb=load_workbook(self.workbook_name)
        sh_init=wb[self.init_sheet_name]
        init_phone=sh_init.cell(row=2,column=1).value
        return init_phone
    #將注冊成功的手機號單獨記錄
    def re_ok_tel(self,reg_tel):
          wb=load_workbook(self.workbook_name)
        sh_new=wb[self.init_sheet_name]
      #獲取最大行數(shù),每次都追加寫入
        sh_new.cell(row=sh_new.max_row+1,column=2).value=reg_tel
        wb.save(self.workbook_name)
    #在初始化手機號使用后缤弦,對它進(jìn)行自動修改
    def update_initphone(self,new_phone):
        wb=load_workbook(self.workbook_name)
        sh_new=wb[self.init_sheet_name]
        sh_new.cell(row=2,column=1).value=new_phone
        wb.save(self.workbook_name)
    #將測試數(shù)據(jù)讀取出來
     def read_excel(self,mod,case_list):
        init_phone=self.read_initphone()
        #打開工作薄
        wb=load_workbook(self.workbook_name)
        #打開excel表單
        sh_new=wb[self.sheet_name]
        #新建列表,用來存入讀取的字典
        data=[]
        #獲取最大行數(shù)彻磁,并進(jìn)行遍歷表單,先把所有數(shù)據(jù)讀出來碍沐,然后再進(jìn)行判斷是否要讀出所有用例
        for i in range(1,sh_new.max_row):
            #新建字典,用來讀取存入的數(shù)據(jù)
            dict_data={}
            #根據(jù)鍵名來讀取衷蜓,與excel測試數(shù)據(jù)的名稱一致
            dict_data['id']=sh_new.cell(row=i+1,column=1).value
            dict_data['module']=sh_new.cell(row=i+1,column=2).value
            dict_data['method']=sh_new.cell(row=i+1,column=3).value
            dict_data['url']=self.ip+sh_new.cell(row=i+1,column=4).value
            dict_data['param']=sh_new.cell(row=i+1,column=5).value
            #如果號碼為phone則進(jìn)行替換為初始手機號
            phone=dict_data['param']
            dict_data['param']=reg.regular('initphone',str(init_phone),phone)
            dict_data['title']=sh_new.cell(row=i+1,column=6).value
            dict_data['sql_mode']=sh_new.cell(row=i+1,column=7).value
            #if sh_new.cell(row=i+1,column=8).value !=None:
                #將sql中涉及到手機號參數(shù)的進(jìn)行替換
            dict_data['sql']=sh_new.cell(row=i+1,column=8).value
            sql=dict_data['sql']
            dict_data['sql']=reg.regular('initphone',str(init_phone),sql)
            dict_data['excepted_code']=sh_new.cell(row=i+1,column=9).value
            data.append(dict_data)
        #根據(jù)mod來判斷累提,如果mod為1則讀取所有的數(shù)據(jù),不為1則根據(jù)case_list來判斷
        if mod==1:
           final_data=data
            log.info('讀取的測試數(shù)據(jù)為{0}'.format(final_data))
        else:
            final_data=[]
            for item in data:
                if item['id'] in case_list:
                    final_data.append(item)
                    log.info('讀取的測試數(shù)據(jù)為{0}'.format(final_data))
        #self.update_initphone(int(init_phone)+1)
        return final_data
       '''

這個類主要用來讀寫excel中的數(shù)據(jù)磁浇,因為我把測試數(shù)據(jù)都放在excel中的斋陪,然后在發(fā)起http請求之前,就調(diào)用的的方法將測試數(shù)據(jù)讀取出來置吓,進(jìn)行發(fā)起請求无虚,然后在測試完成后將結(jié)果回寫到excel中;這樣做的目的是衍锚,對于想要測試的接口友题,可以直接在excel中進(jìn)行添加和管理,就不用單獨再去改代碼加測試用例了戴质,比較的方便吧度宦。
就是這個樣子的:


image.png

進(jìn)行單元測試的類:

'''
  #寫測試用例的類
  #首先引入單元測試框架,繼承使用TestCase編寫測試用例
  #然后進(jìn)行初始化處理告匠,發(fā)起http請求戈抄,
  #進(jìn)行斷言,并將測試結(jié)果寫入到excel
  import unittest
  #引入ddt裝飾器后专,使用數(shù)據(jù)驅(qū)動模式直接從excel中讀取測試用例
  from ddt import ddt,data
  from common.http_request import Request
  from common.test_case_set import *
  @ddt#引入ddt
  class Test_Case(unittest.TestCase):
    #進(jìn)行初始化處理
    def setUp(self):
        log.info('--------開始測試--------')
    @data(*test_data)
    #@unpack
    def test_cases(self,dict_item):#將讀取的數(shù)據(jù)存為字典
        log.info('***********************')
        log.info('發(fā)起請求的測試數(shù)據(jù)為{0}'.format(dict_item))
        log.info('正在進(jìn)行第{0}用例測試,用例標(biāo)題為:{1}'.format(dict_item['id'],dict_item['title']))
        result_data={}
        #參數(shù)定義
        #定義check_sql
        check_sql=dict_item['sql']
        #進(jìn)行參數(shù)替換
        #將手機號進(jìn)行參數(shù)替換
        if s.MOBILE_PHONE !='':
            params=reg.regular('reg_tel',s.MOBILE_PHONE,dict_item['param'])
            #如果為投資接口則把手機號賦值到期望結(jié)果中
            excepted=reg.regular('reg_tel',s.MOBILE_PHONE,dict_item['excepted_code'])
        #將memberid進(jìn)行參數(shù)替換
            if s.MEMBERID !='' :
                #期望結(jié)果中的手機號
                excepted=reg.regular('member_id',str(s.MEMBERID),excepted)
                #投資的memberid替換
                params=reg.regular('member_id',str(s.MEMBERID),params)
                #投資的sql中的memberid進(jìn)行替換
                check_sql=reg.regular('member_id',str(s.MEMBERID),check_sql)
        #將充值前的余額查出來
                if s.LEVAL_AMOUNT ==None and dict_item['module']=='recharge' :
                    s.LEVAL_AMOUNT=db.db_operation("select leaveamount from member where mobilephone="+s.MOBILE_PHONE)[0]
                    lamount=str('%.2f'%(float(s.LEVAL_AMOUNT)+float(eval(params)['amount'])))
                    excepted=reg.regular('leave_amount',lamount,excepted)
        else:
            params=dict_item['param']
            excepted=dict_item['excepted_code']
            check_sql=dict_item['sql']
        if dict_item['module']=='bidLoan':
            #將投資前的用戶余額查出來
            s.STARTAMOUNT=db.db_operation(eval(check_sql)['my_sql'][0])[0]
            #將投資前標(biāo)的已投總額查出來
            s.STARTINVAMOUNT=db.db_operation(eval(check_sql)['my_sql'][1])[0]
        #發(fā)起http請求
        re=Request().httprequest(dict_item['url'],eval(params),dict_item['method'])
        log.info('請求結(jié)果為{0}'.format(re))
        #注意划鸽,在將字典寫入excel中時,要加format
        result_data['actually_code']=format(re)
        result_data['reson']=re['msg']
        #將需要用到的變量存儲再全局變量中
        if re['msg'] =='注冊成功':
            #將注冊成功的手機號儲存在全局變量中
            s.MOBILE_PHONE=str(eval(params)['mobilephone'])
            #將注冊成功的手機號記錄在excel
            t.re_ok_tel(s.MOBILE_PHONE)
            #將用戶id查詢出來
            member_id=db.db_operation("select Id from member where mobilephone="+s.MOBILE_PHONE)[0]
            s.MEMBERID=member_id
        if re['msg']=='充值成功':
            #將注冊時間進(jìn)行替換
            reg_time=re['data']['regtime']
            excepted=reg.regular('reg_time',reg_time,excepted)
        if re['msg']=='競標(biāo)成功':
            #獲取投資成功后的用戶余額
            s.ENDAMOUNT=db.db_operation(eval(check_sql)['my_sql'][0])[0]
            #獲取投資成功后的該標(biāo)的投資總額
            s.ENDINVAMOUNT=db.db_operation(eval(check_sql)['my_sql'][1])[0]
        #進(jìn)行數(shù)據(jù)庫檢查
        #通過sql_mode來判斷行贪,如果為1則檢查漾稀,
        if dict_item['sql_mode']==1:
            if dict_item['module']=='bidLoan':
                #用戶實際投資金額為
                acct_loan_amount=s.STARTAMOUNT-s.ENDAMOUNT
                log.info('用戶實際投資金額為{0}'.format(acct_loan_amount))
                #用戶實際投資記錄為
                acct_inv_amount=s.ENDINVAMOUNT-s.STARTINVAMOUNT
                log.info('用戶實際投資記錄為{0}'.format(acct_inv_amount))
                #投資后標(biāo)的狀態(tài)為
                loan_status=db.db_operation(eval(check_sql)['my_sql'][2])[0]
                log.info('用戶實際投資記錄為{0}'.format(loan_status))
                #數(shù)據(jù)庫期望結(jié)果
                excepted_sql_result=eval(check_sql)['result']
                try:
                    #用戶的實際投資金額對比
                    self.assertEqual(float(excepted_sql_result[0]),float(acct_loan_amount))
                    #用戶投資后的余額對比
                    self.assertEqual(float(excepted_sql_result[0]),float(acct_inv_amount))
                    #用戶投資后標(biāo)的狀態(tài)對比
                    self.assertEqual(float(excepted_sql_result[1]),float(loan_status))
                    result_data['sql_result']='PASS'
                    log.debug('數(shù)據(jù)庫檢查結(jié)果為:{0}'.format(result_data['sql_result']))
                except AssertionError as a:
                    log.debug('數(shù)據(jù)庫比對報錯:{0}'.format(a))
                    result_data['sql_result']='FAIL'
                    result_data['result']='FAIL'
                    t.write_excel(dict_item['id'],result_data)
                    raise a
            else:
                #查詢數(shù)據(jù)庫,是否存在新增的數(shù)據(jù),讀出來是個元組類型的
                acctually_sql=db.db_operation(eval(check_sql)['my_sql'])[0]
                log.debug('查詢數(shù)據(jù)庫結(jié)果為:{0}'.format(acctually_sql))
                #數(shù)據(jù)庫期望結(jié)果
                excepted_sql_result=eval(check_sql)['result']
                try:
                    self.assertEqual(excepted_sql_result,acctually_sql)
                    result_data['sql_result']='PASS'
                    log.debug('數(shù)據(jù)庫檢查結(jié)果為:{0}'.format(result_data['sql_result']))
                except AssertionError as a:#這里不拋出異常模闲,以便程序繼續(xù)執(zhí)行
                    log.debug('數(shù)據(jù)庫比對報錯:{0}'.format(a))
                    result_data['sql_result']='FAIL'
                    result_data['result']='FAIL'
                    #result_data['sql_result']='FAIL'
                    #這里直接將結(jié)果寫入
                    t.write_excel(dict_item['id'],result_data)
                    raise a
        else:
            result_data['sql_result']='NoNeedCheck'

        #進(jìn)行測試結(jié)果比較斷言
        try :
            self.assertEqual(eval(excepted),eval(result_data['actually_code']))
            log.info('期望結(jié)果為:{0}'.format(excepted))
            result_data['result']='PASS'
            log.debug('斷言檢查結(jié)果為{0}'.format(result_data['result']))
        except Exception as e:
            result_data['result']='FAIL'
            log.info('期望結(jié)果為:{0}'.format(excepted))
            log.debug('返回報錯!{0}'.format(e))
            raise e
        #測試結(jié)果回寫
        finally:#無論結(jié)果怎樣都要將測試結(jié)果寫回到excel中
            log.info('******開始寫入數(shù)據(jù)******')
            t.write_excel(dict_item['id'],result_data)
            log.info('******結(jié)束寫入數(shù)據(jù)******')
    def tearDown(self):
        log.info('--------結(jié)束測試--------')
'''

這個就是繼承了unittest框架的TestCase類來寫的測試用例崭捍,然后還用到了ddt裝飾器尸折,因為之前我使用讀寫excel的類,將測試數(shù)據(jù)都是以列表嵌套字典的方式讀出來的殷蛇,然后ddt的作用就是可以給一個用例傳入不同的參數(shù)实夹,然后每個運行一遍,這樣的話就相當(dāng)于運行了多個測試用例粒梦,但實際上我只寫了一個test_case;這樣就實現(xiàn)了單例模式亮航,這也算是用excel管理測試用例的好處吧。
利用正則表達(dá)式進(jìn)行參數(shù)替換的類


image.png

因為在測試的時候涉及到對測試數(shù)據(jù)的參數(shù)化配置匀们,這就會涉及到參數(shù)的替換缴淋,所以我單獨寫了一個類來進(jìn)行參數(shù)替換,使用正則表達(dá)式的方法泄朴,調(diào)用起來比較的方便重抖,比用replace的方法要方便一點。
最后就是main執(zhí)行文件的類:

'''
#用于加載并執(zhí)行測試用例并生成測試報告
import unittest
import time
import HTMLTestRunnerNew
from common.test_case import Test_Case
from common.sendemail import Sendemail
from common.config import Config
from conf.projectpath import *
#添加測試集
suite=unittest.TestSuite()
#通過指定的測試類祖灰,加載測試用例
loader=unittest.TestLoader()
loader.loadTestsFromTestCase(Test_Case)
#將用例添加到測試集
suite.addTest(loader.loadTestsFromTestCase(Test_Case))
#將測試結(jié)果寫入到文件中
now=time.strftime('%y-%m-%d_%H_%M_%S')
#命名一個html文件钟沛,為了避免名稱不重復(fù)所以加上時間戳
filepath=test_report+'\API_test'+now+'.html'
#利用html模板導(dǎo)出測試報告
with open(filepath,'wb') as file:
    runner=HTMLTestRunnerNew.HTMLTestRunner(stream=file,verbosity=2,title='api_test',description=None,tester='wang')
 #執(zhí)行測試用例
 runner.run(suite)
#將測試報告通過郵件發(fā)送出去
email_to=Config().configer(email_path,'EMAIL','email_to')
sendmail=Sendemail()
#sendmail.send_email(email_to,filepath)
'''

這里我先調(diào)用TestLoader來集成測試用例,然后用TestSuite來集成測試用例局扶,然后在將測試報告用html模板導(dǎo)出恨统,這樣的話運行完成后就可以在web頁面打開測試報告進(jìn)行查看測試結(jié)果,最后還添加了發(fā)送郵件的方法三妈,可以將測試報告發(fā)送到個人郵箱畜埋。
我目前能做的就這些了,總的來說還是比較簡易的沈跨,還有一些東西需要完善由捎,比如部署到j(luò)enkins集成進(jìn)行定時調(diào)度;代碼層面也有很多地方需要優(yōu)化饿凛,希望各位大神提出來狞玛;
第一次寫,排版有點亂涧窒,不太會搞心肪,莫怪莫怪!

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末纠吴,一起剝皮案震驚了整個濱河市硬鞍,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖固该,帶你破解...
    沈念sama閱讀 206,723評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件锅减,死亡現(xiàn)場離奇詭異,居然都是意外死亡伐坏,警方通過查閱死者的電腦和手機怔匣,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,485評論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來桦沉,“玉大人每瞒,你說我怎么就攤上這事〈柯叮” “怎么了剿骨?”我有些...
    開封第一講書人閱讀 152,998評論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長埠褪。 經(jīng)常有香客問我浓利,道長,這世上最難降的妖魔是什么钞速? 我笑而不...
    開封第一講書人閱讀 55,323評論 1 279
  • 正文 為了忘掉前任荞膘,我火速辦了婚禮,結(jié)果婚禮上玉工,老公的妹妹穿的比我還像新娘。我一直安慰自己淘菩,他們只是感情好遵班,可當(dāng)我...
    茶點故事閱讀 64,355評論 5 374
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著潮改,像睡著了一般狭郑。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上汇在,一...
    開封第一講書人閱讀 49,079評論 1 285
  • 那天翰萨,我揣著相機與錄音,去河邊找鬼糕殉。 笑死亩鬼,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的阿蝶。 我是一名探鬼主播雳锋,決...
    沈念sama閱讀 38,389評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼羡洁!你這毒婦竟也來了玷过?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,019評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎辛蚊,沒想到半個月后粤蝎,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,519評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡袋马,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,971評論 2 325
  • 正文 我和宋清朗相戀三年初澎,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片飞蛹。...
    茶點故事閱讀 38,100評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡谤狡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出卧檐,到底是詐尸還是另有隱情墓懂,我是刑警寧澤,帶...
    沈念sama閱讀 33,738評論 4 324
  • 正文 年R本政府宣布霉囚,位于F島的核電站捕仔,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏盈罐。R本人自食惡果不足惜榜跌,卻給世界環(huán)境...
    茶點故事閱讀 39,293評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望盅粪。 院中可真熱鬧钓葫,春花似錦、人聲如沸票顾。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,289評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽奠骄。三九已至豆同,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間含鳞,已是汗流浹背影锈。 一陣腳步聲響...
    開封第一講書人閱讀 31,517評論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留蝉绷,地道東北人鸭廷。 一個月前我還...
    沈念sama閱讀 45,547評論 2 354
  • 正文 我出身青樓,卻偏偏與公主長得像潜必,于是被迫代替她去往敵國和親靴姿。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,834評論 2 345

推薦閱讀更多精彩內(nèi)容

  • 關(guān)于Mongodb的全面總結(jié) MongoDB的內(nèi)部構(gòu)造《MongoDB The Definitive Guide》...
    中v中閱讀 31,898評論 2 89
  • 轉(zhuǎn)自:[http://blog.csdn.net/liu88010988/article/details/5154...
    hackywit閱讀 5,990評論 0 26
  • Swift1> Swift和OC的區(qū)別1.1> Swift沒有地址/指針的概念1.2> 泛型1.3> 類型嚴(yán)謹(jǐn) 對...
    cosWriter閱讀 11,089評論 1 32
  • 小女堪憐父病久磁滚,歸來一任淚泗流佛吓。 不辭灶下仍為婢宵晚,詠絮潑茶亦自由。
    顧天曉閱讀 279評論 7 9
  • 那年大了好大的雪维雇,白小蘭給她兒子打電話淤刃,讓她兒子多穿些衣服。剛掛電話吱型,白小蘭的異國的父母打電話讓她多穿些衣服……是...
    星空下的雪花閱讀 112評論 0 0