快速打造接口自動化測試框架

1 接口測試介紹

接口測試是對系統(tǒng)或組件之間的接口進(jìn)行測試完丽,主要是校驗(yàn)數(shù)據(jù)的交換躲惰,傳遞和控制管理過程,以及相互邏輯依賴關(guān)系智袭。
接口自動化相對于UI自動化來說奔缠,屬于更底層的測試,這樣帶來的好處就是測試收益更大吼野,且維護(hù)成本相對來說較低校哎,是我們進(jìn)行自動化測試的首選;

2 框架選型

目前接口自動化的框架比較多箫锤,比如jmeter贬蛙,就可以集接口自動化和性能測試于一體雨女,該工具編寫用例效率不高谚攒;還有我們常用的postman,結(jié)合newman也可以實(shí)現(xiàn)接口自動化氛堕;Python+unittest+requests+HTMLTestRunner是目前比較主流的測試框架馏臭,對python有一定的編碼要求;

本期我們選擇robotframework(文中后續(xù)統(tǒng)一簡稱為RF)這一個(gè)比較老牌的測試框架進(jìn)行介紹讼稚,RF是一個(gè)完全基于關(guān)鍵字測試驅(qū)動的框架括儒,它即能夠基于它的一定規(guī)則,導(dǎo)入你需要的測試庫(例如:其集成了selenium的測試庫锐想,即可以理解為操作控件的測試底層庫)帮寻,然后基于這些測試庫,你能應(yīng)用TXT形式編寫自己的關(guān)鍵字(支持python和java語言赠摇,這些關(guān)鍵字即你的庫組成)固逗,之后,再編寫(測試用例由測試關(guān)鍵字組成)進(jìn)行測試藕帜;他支持移動端烫罩、UI自動化和接口自動化的測試;

3 環(huán)境搭建

python的安裝:目前選取的python3以上的版本洽故,RF的運(yùn)行依賴python
robotframework:參考http://www.reibang.com/p/9dcb4242b8f2
jenkins:用于調(diào)度RF的用例執(zhí)行環(huán)境
gitlab:代碼倉庫

4 需求

4.1 需求內(nèi)容

接口內(nèi)容:實(shí)現(xiàn)一個(gè)下單贝攒,并檢查訂單狀態(tài)是否正常的場景;該需求涉及到如下三個(gè)接口
下單接口
訂單結(jié)果查詢接口
下單必須帶上認(rèn)證標(biāo)識时甚,生成token的接口
環(huán)境覆蓋:需要支持能在多套環(huán)境運(yùn)行隘弊,比如測試和預(yù)發(fā)布環(huán)境
系統(tǒng)集成:需要能夠集成在CICD中哈踱,實(shí)現(xiàn)版本更新后的自動檢測

4.2 用例設(shè)計(jì)

4.2.1 用例設(shè)計(jì),根據(jù)業(yè)務(wù)場景設(shè)計(jì)測試用例梨熙,方便后續(xù)實(shí)現(xiàn)

image

4.2.2 測試數(shù)據(jù)構(gòu)造嚣鄙,預(yù)置不同環(huán)境的測試數(shù)據(jù),供實(shí)現(xiàn)調(diào)用

image

5 整體實(shí)現(xiàn)架構(gòu)

image

接口測試實(shí)現(xiàn)層:在RF串结,通過引用默認(rèn)關(guān)鍵字RequestsLibrary(實(shí)現(xiàn)http請求)和通過python自定義關(guān)鍵字來完成用例實(shí)現(xiàn)的需求哑子;

jenkins調(diào)度:在jenkins上配置一個(gè)job,設(shè)置好RF用例執(zhí)行的服務(wù)器和發(fā)送給服務(wù)器相關(guān)的RF執(zhí)行的指令肌割,并且在jenkins中配置好測試報(bào)告模板卧蜓,這樣用例便可以通過jenkins完成執(zhí)行并發(fā)送測試結(jié)果給項(xiàng)目干系人;

生成用例執(zhí)行的API:上圖中藍(lán)色部分把敞,就是為了將jenkins的job生成一個(gè)可訪問api接口弥奸,方便被測項(xiàng)目的CICD集成;

集成到被測系統(tǒng)CICD流程:將上面步驟中封裝的API配置在被測應(yīng)用的gitlab-ci.yml中奋早,完成整個(gè)接口自動化的閉環(huán)

6 RF用例實(shí)現(xiàn)

6.1 引用的內(nèi)置關(guān)鍵字

RequestsLibrary 構(gòu)造http的請求盛霎,get|post等請求


getRequests
# get請求的入?yún)?[Arguments]${url_domain}${getbody}${geturl}${getToken}
Create session    postmain${url_domain}

# 定義header的內(nèi)容

${head}createdictionary    content-type=application/json    Authorization=${getToken}MerchantId=${s_merchant_id}

# get請求

${addr}getRequest    postmain${geturl}params=${getbody}headers=${head}

# 請求狀態(tài)碼斷言
Should BeEqualAs Strings${addr.status_code}200
${response_get_data}To Json${addr.content}

# 返回http_get請求結(jié)果

SetTest Variable${response_get_data}
Delete All Sessions

6.2 自定義關(guān)鍵字

getEnvDomain 用于從自定義的configs.ini文件獲取對應(yīng)環(huán)境的微服務(wù)的請求域名
configs.ini的內(nèi)容

image

獲取configs.ini的內(nèi)容


import configparser

def getEnv(path,env):

config = configparser.ConfigParser()

config.read(path)

passport = config[env]['passport']

stock=config[env]['stock']

finance=config[env]['finance']

SUP= config[env]['SUP']

publicApi = config[env]['publicApi']

publicOrder = config[env]['publicOrder']

data_dict={'passport':passport,'stock':stock,'finance':finance,'SUP':SUP,'publicApi':publicApi,'publicOrder':publicOrder}

return data_dict

excelTodict 用戶將excel中的內(nèi)容作為字典返回

import xlrd

'''

通用獲取excel數(shù)據(jù)

@:param path excel文件路徑
@:param sheet_name excel文件里面sheet的名稱 如:Sheet1
@:env 環(huán)境,是IT還是PRE

'''

def getExcelDate(path, sheet_name,env):
bk = xlrd.open_workbook(path)
sh = bk.sheet_by_name(sheet_name)
row_num = sh.nrows
data_list = []
for i in range(1, row_num):
row_data = sh.row_values(i)
data = {}
for index, key in enumerate(sh.row_values(0)):
data[key] = row_data[index]
data_list.append(data)
data_list1 = []
for x in data_list:
#print('這是'+str(x))
if(x.get('env')==env):
data_list1.append(x)
return data_list1

getToken 提供接口下單的授權(quán)token

*** Keywords ***

# 根據(jù)傳入的clientid耽装、secret生成對應(yīng)的token

getToken
[Arguments]    ${client_id}    ${client_secret}    ${url_domain}
Create session    postmain    ${url_domain}
${auth}    createdictionary    grant_type=client_credentials    client_id=${client_id}    client_secret=${client_secret}
${header}    createdictionary    content-type=application/x-www-form-urlencoded
${addr}    postRequest    postmain    /oauth/token    data=${auth}    headers=${header}
Should Be Equal As Strings    ${addr.status_code}    200
${responsedata}    To Json    ${addr.content}
${access}    Get From Dictionary    ${responsedata}    access_token
${token}    set variable    bearer ${access}
Set Test Variable    ${token}
Delete All Sessions

getAllDate 獲取該用例下的所有數(shù)據(jù)


getAllData

[Arguments]    ${row_no}

getEnvDomain
getBalance    ${row_no}
getStockNum    ${row_no}
getSupProPrice    ${row_no}
getProPrice    ${row_no}

Set Test Variable    ${publicOrderUrl}
Set Test Variable    ${FPbalance}
Set Test Variable    ${Pbalance}
Set Test Variable    ${Sbalance}
Set Test Variable    ${Jbalance}
Set Test Variable    ${Cardnum}
Set Test Variable    ${sprice}
Set Test Variable    ${price}
Set Test Variable    ${j_merchant_id}
Set Test Variable    ${s_merchant_id}
Set Test Variable    ${stock_id}
Set Test Variable    ${p_product_id}
Set Test Variable    ${s_product_id}

實(shí)現(xiàn)demo

*** Settings ***
Test Template
Resource          引用所有資源.txt

*** Test Cases ***
*** Settings ***
Test Template
Resource          引用所有資源.txt

*** Test Cases ***
01 下單卡密直儲商品
[Tags]    order
LOG    ---------------------獲取下單前的數(shù)量愤炸、余額------------------------------------------
getAllData    0

${Cardnum1}    set variable    ${Cardnum}
${FPbalance1}    set variable    ${FPbalance}
${Pbalance1}    set variable    ${Pbalance}
${Sbalance1}    set variable    ${Sbalance}
${Jbalance1}    set variable    ${Jbalance}
${CustomerOrderNo1}    Evaluate    random.randint(1000000, 9999999)    random

${Time}    Get Time


log    ------------------------下單操作-------------------------------------------------------
getToken    100xxxx    295dab07a9xxxx9780be0eb95xxxx   ${casUrl}
${input_cs}    create dictionary    memberId=${j_merchant_id}    clientId=1xxx079    userId=string    shopType=string    customerOrderNo=${CustomerOrderNo1}

...    productId=${p_product_id}    buyNum=1    chargeAccount=otest888888    notifyUrl=string    chargeIp=string    chargePassword=string

...    chargeGameName=string    chargeGameRole=string    chargeGameRegion=string    chargeGameSrv=string    chargeType=string    remainingNumber=0

...    contactTel=string    contactQQ=string    customerPrice=0    poundage=0    batchNumber=    originalOrderId=string

...    shopName=string    appointSupProductId=0    stemFromSubOrderId=123456    externalBizId=456789

postRequests    ${publicOrderUrl}    ${input_cs}    /api/Order    ${token}

${data}    get from dictionary    ${responsedata}    data

${orderid}    get from dictionary    ${data}    id

sleep    6
${getdata}    create dictionary    Id=${orderid}    PageIndex=1    PageSize=1
getRequests    ${publicOrderUrl}    ${getdata}    /api/Order/GetList    ${token}
${datalist}    get from dictionary    ${response_get_data}    data
${data}    get from dictionary    ${datalist}    list
${dict}    set variable    ${data}[0]
${orderOuterStatus}    get from dictionary    ${dict}    orderOuterStatus

LOG    ---------------------獲取下單后的數(shù)量、余額----------------------------------------------

getAllData    0
${Cardnum2}    set variable    ${Cardnum}
${FPbalance2}    set variable    ${FPbalance}
${Pbalance2}    set variable    ${Pbalance}
${Sbalance2}    set variable    ${Sbalance}
${Jbalance2}    set variable    ${Jbalance}
${sprice}    set variable    ${sprice}
${price}    set variable    ${price}

log    ------------------斷言-----------------------------------------------------------------

${Cardnum3}    Evaluate    ${Cardnum1}
${Jbalance3}    Evaluate    ${Jbalance1}
${Sbalance3}    Evaluate    ${Sbalance1}
${Pbalance3}    Evaluate    ${Pbalance1}
should be true    ${orderOuterStatus}==90
should be true    ${Cardnum3}==${Cardnum2}
should be true    ${Jbalance3}==${Jbalance2}
should be true    ${Sbalance3}==${Sbalance2}
should be true    ${Pbalance3}==${Pbalance2}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末掉奄,一起剝皮案震驚了整個(gè)濱河市规个,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌姓建,老刑警劉巖诞仓,帶你破解...
    沈念sama閱讀 207,113評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異速兔,居然都是意外死亡墅拭,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,644評論 2 381
  • 文/潘曉璐 我一進(jìn)店門涣狗,熙熙樓的掌柜王于貴愁眉苦臉地迎上來谍婉,“玉大人,你說我怎么就攤上這事屑柔÷庞” “怎么了?”我有些...
    開封第一講書人閱讀 153,340評論 0 344
  • 文/不壞的土叔 我叫張陵掸宛,是天一觀的道長死陆。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么措译? 我笑而不...
    開封第一講書人閱讀 55,449評論 1 279
  • 正文 為了忘掉前任别凤,我火速辦了婚禮,結(jié)果婚禮上领虹,老公的妹妹穿的比我還像新娘规哪。我一直安慰自己,他們只是感情好塌衰,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,445評論 5 374
  • 文/花漫 我一把揭開白布诉稍。 她就那樣靜靜地躺著,像睡著了一般最疆。 火紅的嫁衣襯著肌膚如雪杯巨。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,166評論 1 284
  • 那天努酸,我揣著相機(jī)與錄音服爷,去河邊找鬼。 笑死获诈,一個(gè)胖子當(dāng)著我的面吹牛仍源,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播舔涎,決...
    沈念sama閱讀 38,442評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼笼踩,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了终抽?” 一聲冷哼從身側(cè)響起戳表,我...
    開封第一講書人閱讀 37,105評論 0 261
  • 序言:老撾萬榮一對情侶失蹤桶至,失蹤者是張志新(化名)和其女友劉穎昼伴,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體镣屹,經(jīng)...
    沈念sama閱讀 43,601評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡圃郊,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,066評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了女蜈。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片持舆。...
    茶點(diǎn)故事閱讀 38,161評論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖伪窖,靈堂內(nèi)的尸體忽然破棺而出逸寓,到底是詐尸還是另有隱情,我是刑警寧澤覆山,帶...
    沈念sama閱讀 33,792評論 4 323
  • 正文 年R本政府宣布竹伸,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏勋篓。R本人自食惡果不足惜吧享,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,351評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望譬嚣。 院中可真熱鬧钢颂,春花似錦、人聲如沸拜银。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,352評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽尼桶。三九已至钱豁,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間疯汁,已是汗流浹背牲尺。 一陣腳步聲響...
    開封第一講書人閱讀 31,584評論 1 261
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留幌蚊,地道東北人谤碳。 一個(gè)月前我還...
    沈念sama閱讀 45,618評論 2 355
  • 正文 我出身青樓,卻偏偏與公主長得像溢豆,于是被迫代替她去往敵國和親蜒简。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,916評論 2 344