這個接口自動化框架是基于unittest單元測試框架來寫的,簡單偶洋,還算可以用吧幽邓,也有些不合理的地方,歡迎大神指正呀撤蟆!
接口自動化框架布局
這個框架一共分為6個部分:
common:公共代碼模塊奕塑,包括一些公共的代碼和測試用例的代碼模塊
conf:存放配置文件的模塊,包括數(shù)據(jù)庫配置信息家肯,項目ip地址龄砰,框架所在路徑,郵件的配置等
logs:存放用例執(zhí)行過程中所生成的日志信息
test_data:存放測試數(shù)據(jù)(這里用的excel)
test_report:用于存放生成的測試報告
main執(zhí)行文件:用于執(zhí)行測試,并在測試完成后生成測試報告和發(fā)送郵件
公共代碼
先看下公共代碼的構(gòu)成
發(fā)起http請求的類:
這里我調(diào)用的是requests模塊换棚,可發(fā)起post請求或get請求式镐,根據(jù)傳入的method來進(jìn)行判斷,然后我這里直接返回json格式的數(shù)據(jù)固蚤,這個便于之后對數(shù)據(jù)的一些處理
讀取配置文件的類:
這個類我調(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)行添加和管理,就不用單獨再去改代碼加測試用例了戴质,比較的方便吧度宦。
就是這個樣子的:
進(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ù)替換的類
因為在測試的時候涉及到對測試數(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)化饿凛,希望各位大神提出來狞玛;
第一次寫,排版有點亂涧窒,不太會搞心肪,莫怪莫怪!