一、 背景:
為了讓大家更加的了解Jmeter螺戳,并且使用起來游刃有余搁宾。
這篇我們主要講一下,如何優(yōu)雅的使用Jmeter一步步的實現(xiàn)接口自動化倔幼,完成腳本與數(shù)據(jù)分離盖腿,把可能對Jmeter腳本的維護(hù)轉(zhuǎn)移到csv文本中,降低接口變更時對腳本的維護(hù)损同,最終目標(biāo)是實現(xiàn)寫好接口自動化腳本后翩腐,接口變更的維護(hù)都只要操作csv文件。
二膏燃、實例
先介紹一個Jmeter的函數(shù)-》csvRead函數(shù)茂卦,后續(xù)介紹使用的會比較多,熟悉的伙伴可以直接跳過组哩。
1等龙、csvRead函數(shù)使用:
csvRead函數(shù)是從外部讀取參數(shù)处渣,可以從一個文件中讀取多個參數(shù)。
使用步驟:
1蛛砰、先新建一個文件罐栈,例如test.csv(或test.txt),里面的數(shù)據(jù)存放為
grizz,qq1111
jiezai,qq1111
文件為用戶名和密碼泥畅,用逗號隔開荠诬,每一列表示一種參數(shù),每一行則表示一組參數(shù)涯捻。
2浅妆、選項-》函數(shù)助手對話框-》函數(shù)助手,打開Jmeter的函數(shù)助手障癌,選擇csvRead函數(shù):
其中:
CSV file to get values from | *alias:要讀取的文件路徑凌外,為絕對路徑
CSV文件列號| next| *alias:從第幾列開始讀取,注意第一列是0
即${__CSVRead(D:/test.csv,0)}取到的值為grizz
即${__CSVRead(D:/test.csv,1)}取到的值為qq1111
3.Jmeter執(zhí)行的時候涛浙,如果有多個線程康辑,順序讀取每行的數(shù)據(jù),如果線程組多余文件中的行數(shù)轿亮,則循環(huán)讀取疮薇。如線程數(shù)為2,則第2個線程讀取的是第二行的數(shù)據(jù)我注,線程數(shù)為3按咒,線程數(shù)3大于文件中的行數(shù)2,則第3個線程讀取的是第一行的數(shù)據(jù)但骨。
PS:這一函數(shù)并不適合于讀取很大的文件励七,因為整個文件都會被存儲到內(nèi)存之中。對于較大的文件奔缠,請使用配置元件CSV Data Set或者StringFromFile 掠抬。但是我們不是壓測,只是接口自動化校哎,一般沒有太大的數(shù)據(jù)文件两波,啊哈哈哈哈。
默認(rèn)情況下闷哆,函數(shù)會在遇到的每一個逗號處斷行腰奋,需要換一個分隔符(通過設(shè)置屬性csvread.delimiter來實現(xiàn))
修改jmeter.properties文件:
#csvread.delimiter=,
修改為
csvread.delimiter=?
即把分隔符修改為?問號阳准,注意前面的#號代表注釋氛堕,要去掉。重啟Jmeter生效野蝇。
2讼稚、使用的接口:
這里我們以兩個接口舉例括儒,其中Content-Type=application/json:
1·獲取token的接口/getToken
入?yún)ⅲ?/p>
{
"flag":"test",
"appId":"001"
}
返回值:
{"returnFlag":"1000","returnMsg":"獲取token成功","token":"19940622"}
2·使用token的接口/useToken,主要是測試useToken接口锐想,useToken接口的token需要從getToken接口的返回值中取帮寻,其實就是參數(shù)關(guān)聯(lián)。
入?yún)ⅲ?/p>
{
"flag":"${token}",
"appId":"001"
}
返回值赠摇,以3個場景為例:
{"returnFlag":"1000","returnMsg":"使用token成功"}
{"returnFlag":"1001","returnMsg":"token為空"}
{"returnFlag":"1002","returnMsg":"token錯誤"}
對于接口的斷言固逗,我們默認(rèn)"returnFlag":"1000"即接口業(yè)務(wù)正常返回,1001藕帜,1002代表接口針對業(yè)務(wù)的不同異常給予的返回烫罩,當(dāng)然也在我們的接口測試范圍內(nèi)。
我們分v1洽故,v2贝攒,v3,v4时甚,4個版本循序漸進(jìn)的講:
v1版本:
剛開始入門時隘弊,我們的腳本可能會是這樣的
先請求getToken接口,并獲取token荒适,用正則表達(dá)式提取如下
再請求useToken接口梨熙,flag的值輸入${token},使用提取到的token值刀诬。
入?yún)ⅲ簕"flag":"${token}","appId":"001"}
返回值:{"returnFlag":"1000","returnMsg":"使用token成功"}
斷言:"returnFlag":"1000"咽扇,斷言成功
再測試后續(xù)兩種場景,入?yún)⑷缦拢?/p>
sampler-使用tokenv1-為空:
入?yún)ⅲ簕"flag":"","appId":"002"}
返回值:{"returnFlag":"1001","returnMsg":"token為空"}
斷言:"returnFlag":"1000"陕壹,斷言失敗
sampler-使用tokenv1-錯誤:
入?yún)ⅲ簕"flag":"errorToken","appId":"003"}
返回值:{"returnFlag":"1002","returnMsg":"token錯誤"}
斷言:"returnFlag":"1000"肌割,斷言失敗
查看結(jié)果樹
這一頓操作下來,沒啥問題帐要,因為useToken接口的3種場景我們都覆蓋了,只要把異常的場景的斷言對應(yīng)改一下弥奸,我們的接口腳本就寫好了榨惠,可以交付。但是如果我們要實現(xiàn)接口自動化盛霎,那么v1版本中sampler的重復(fù)率比較高赠橙,我們考慮能不能把useToken的3種場景放到一個sampler中。
于是有了v2版本
v2版本:
由于聰明的我們有一定前瞻性愤炸,我們知道期揪,想降低腳本中sampler的重復(fù)率,需要借助數(shù)據(jù)文件规个,我們可以把接口useToken的請求body直接從文件中讀
入?yún)?:{"flag":"${token}","appId":"001"}
入?yún)?:{"flag":"","appId":"002"}
入?yún)?:{"flag":"errorToken","appId":"003"}
入?yún)?凤薛,3我們可以從文件中讀取沒問題姓建,但是入?yún)?這樣從文件中讀取,”${token}”讀取出來的值就是字符串”${token}”缤苫,而不是我們想要的”19940622”速兔,怎么辦呢?
于是我們思考著活玲,可以把接口useToken的請求分兩種情況涣狗,需要正確的token和不需要正確的token,如果需要正確的token舒憾,我們就在Jmeter中傳給他镀钓,其它參數(shù)還是可以在文本中讀取镀迂;如果不需要正確的token丁溅,則請求全部從文件中讀取。什么意思呢招拙,繼續(xù)往下看唧瘾。
我們設(shè)置存放csv文件的目錄DATA=/jmeter/testcase,因為我們可能會多次使用到這個目錄别凤,所以可以用${ DATA }代表我們的文件目錄饰序。
useToken_v2.csv文件:
用例1-token正確?1?"appId":"001"}
用例2-token為空?0?{"flag":"","appId":"002"}
用例3-token錯誤?0?{"flag":"errorToken","appId":"003"}
舉例:
${__CSVRead(${DATA}/useToken_v2.csv,1)}依次取到的是1,0规哪,0
${__CSVRead(${DATA}/useToken_v2.csv,2)}第一次取到的是{"flag":"","appId":"001"}
如果我們useToken_v2.csv文件有3行求豫,則設(shè)置auto_interface_v2線程數(shù)為3。
因為對于csvRead函數(shù)诉稍,每一個線程都有獨立的內(nèi)部指針指向文件數(shù)組中的當(dāng)前行蝠嘉。當(dāng)某個線程第一次引用文件時,函數(shù)會為線程在數(shù)組中分配下一個空閑行杯巨。如此一來蚤告,任何一個線程訪問的文件行,都與其他線程不同(除非線程數(shù)大于數(shù)組包含的行數(shù))服爷。
當(dāng)我們需要正確Token時杜恰,我們利用if控制器,如果文本的第二列(我這里是以問號分隔的仍源,因為默認(rèn)是逗號心褐,但是我們接口json數(shù)據(jù)本身就有逗號,只能換一個)為1笼踩,則flag從Jmeter中自己讀榷旱;如果文本的第二列為0則代表不需要正確token嚎于,那接口入?yún)⑽覀兙腿繌奈募凶x取掘而。
If:${__CSVRead(${DATA}/useToken_v2.csv,1)}==1
sampler-使用tokenv2-haveToken的請求body為:
{"flag":"${token}",${__CSVRead(${DATA}/useToken_v2.csv,2)}
即請求body由兩部分組成挟冠,一部分來自Jmeter內(nèi),主要是獲取正確的token镣屹,另一部分來自useToken_v2.csv文件的第3列圃郊。
我們這里token正確時,另一個參數(shù)好像只有"appId":"001"這一種情況女蜈,顯得好像這樣寫起來更加冗余持舆,其實不然,我們appId也有可能為空伪窖,也可能錯誤逸寓。這時候我們的useToken_v2.csv文件應(yīng)該會是這樣:
用例1-token正確?1?"appId":"001"}
用例2-appId為空?1?"appId":""}
用例3-appId錯誤?1?"appId":"error appId "}
用例4-token為空?0?{"flag":"","appId":"002"}
用例5-token錯誤?0?{"flag":"errorToken","appId":"003"}
而且一個接口的入?yún)⒉豢赡苤挥袃蓚€,但一般token只會有一個覆山,所以當(dāng)接口參數(shù)多時竹伸,這樣寫還是減少了一定量的sampler的重復(fù)率。
If:${__CSVRead(${DATA}/useToken_v2.csv,1)}==0
請求body就為:${__CSVRead(${DATA}/useToken_v2.csv,2)}
再解釋一下這里為什了加了一個計算器簇宽,和接口名稱為什么命名為使用tokenv2-noToken-${_number}勋篓。
首先,當(dāng)我們把線程數(shù)設(shè)置為3時魏割,其實getToken接口也跑了3次譬嚣,但是其實我們只需要它跑一次,取出正確的token就可以了钞它,getToken接口的if控制器跟計算器一起作用拜银,當(dāng)?shù)?個線程啟動時觸發(fā)if控制器的規(guī)則${_number}==1,第2遭垛,第3個線程是就不會觸發(fā)if控制器里面的getToken接口尼桶。
useToken接口命名后面加一個${_number},
使用tokenv2-noToken-${_number}和使用tokenv2-haveToken-${_number}锯仪;主要是當(dāng)接口報錯時泵督,可以根據(jù)接口的名稱(其后面加了${_number},接口每個場景${_number}都是不一樣的)庶喜,判斷其對應(yīng)useToken_v2.csv文件的哪一行導(dǎo)致報錯幌蚊,可快速定位并進(jìn)行報錯修改。
方便理解溃卡,給出運行效果圖如下:
v3版本:
v2版本我們好像把我們能做的都給做了,但是前提是
從文件中讀取蜒简,”${token}”讀取出來的值就是字符串”${token}”瘸羡,而不是我們想要的”19940622”!
隨著時間的推移搓茬,樓主真的前前后后看過網(wǎng)上各種Jmeter教程和使用犹赖,不下40次队他,畢竟自己一直有信念,別人能做的自己也可以峻村,1次看不懂的麸折,那就看5次,5次還不懂粘昨,10次垢啼。不努力,沒有辦法比別人做得更好的张肾。接著說隨著時間的推移芭析,grizz發(fā)現(xiàn)那個前提是可以打破的,因為Jmeter有個eval函數(shù)吞瞪。
函數(shù)__eval可以用來執(zhí)行一個字符串表達(dá)式馁启,并返回執(zhí)行結(jié)果。
舉個栗子:
name=grizz
SQL=select * from able where name='${name}'
${ SQL }=select * from able where name='${name}'
${__eval(${SQL})}= select * from able where name='grizz'
現(xiàn)在我們可以設(shè)置useToken_v3.csv文件如下:
用例1-token正確?{"flag":"${token}","appId":"001"}?"returnFlag":"1000"
用例2-token為空?{"flag":"","appId":"002"}?"returnFlag":"1000"
用例3-token錯誤?{"flag":"errorToken","appId":"003"}?"returnFlag":"1000"
明顯的芍秆,我們不需要if控制器來判斷是否需要正確的Token了惯疙,腳本看起來清新了一些,而且我們把響應(yīng)斷言也從文件中讀取妖啥。這樣開發(fā)修改接口的返回提示時霉颠,我們可以直接通過修改csv文件完成對應(yīng)的修改,不需要去動jmeter腳本迹栓。
入?yún)ⅲ?{__eval(${__CSVRead(${DATA}/useToken_v3.csv,1)})}
斷言${__CSVRead(${DATA}/useToken_v3.csv,2)}
看到這我們可以思考一下掉分,在v4版本還有哪些內(nèi)容可以優(yōu)化?
v4版本:
v1到v2是入門到掌握克伊,v2到v3應(yīng)該是弱雞到熟悉酥郭,v4應(yīng)該到星耀了吧,很想寫個v5版本愿吹,v5(威武)不从,應(yīng)該很強的怕。
我們想想犁跪,我們的最終目標(biāo)是實現(xiàn)寫好接口自動化腳本后椿息,接口變更的維護(hù)都只要操作csv文件。
那么當(dāng)我們的useToken接口新增了一個場景坷衍,token過期
useToken_v4.csv文件
用例個數(shù)?4
用例1-token正確?{"flag":"${token}","appId":"001"}?"returnFlag":"1000"
用例2-token為空?{"flag":"","appId":"002"}?"returnFlag":"1000"
用例3-token錯誤?{"flag":"errorToken","appId":"003"}?"returnFlag":"1000"
用例4-token過期?{"flag":"oldToken","appId":"003"}?"returnFlag":"1000"
設(shè)置線程組auto_interface_v4的線程數(shù)為
${__CSVRead(${DATA}/useToken_v4.csv,1)}寝优,其實就是文件useToken_v4.csv第一行的第二列,就是4
因為我們的線程數(shù)與我們的文件行數(shù)掛鉤枫耳,但這里為什么文件有5行乏矾,而線程數(shù)是4。前面說了,因為對于csvRead函數(shù)钻心,每一個線程都有獨立的內(nèi)部指針指向文件數(shù)組中的當(dāng)前行凄硼。當(dāng)某個線程第一次引用文件時,函數(shù)會為線程在數(shù)組中分配下一個空閑行捷沸。即Jmeter會分配一個線程去保存線程的屬性摊沉,如線程數(shù),啟動時間痒给,循環(huán)次數(shù)等说墨。即當(dāng)線程數(shù)設(shè)置為${__CSVRead(${DATA}/useToken_v4.csv,1)}時,控制線程屬性的線程就讀取了useToken_v4.csv文件的第一行侈玄。
PS:再說一下csvRead函數(shù)
csvRead函數(shù)默認(rèn)從文件第一行開始讀取婉刀,除非你二次開發(fā),不然這個默認(rèn)就一只在(苦笑)序仙,就是說我們不好加文件列的標(biāo)題(當(dāng)然可以利用v4版本的第一行也行)突颊,降低了文件的可讀性。但當(dāng)對某個文件進(jìn)行第一次讀取時潘悼,文件將被打開并讀取到一個內(nèi)部數(shù)組中律秃。如果在讀取過程中找到了空行,函數(shù)就認(rèn)為到達(dá)文件末尾了治唤,即允許拖尾注釋棒动。就是我們可以在文件寫完后回車一下,再寫一些文件的注釋宾添,或者${__CSVRead(D:/test.csv,next)}了解一下船惨,csvRead函數(shù)的第二個值為next可自行進(jìn)行文件行標(biāo)的切換。
其實當(dāng)我們的腳本量化后缕陕,還有很多東西要考慮的粱锐,接口csv文件的命名規(guī)范,線程組和sampler的命名規(guī)范扛邑,因為線程組和sampler的名稱會在報告中體現(xiàn)怜浅,接口入口和出口標(biāo)準(zhǔn),怎么維護(hù)我們的數(shù)據(jù)和腳本更優(yōu)雅蔬崩,怎樣更高效的運行腳本和生成更豐富的報告恶座,更加的節(jié)省測試人力。
下一篇講一下jemter和ant沥阳,jenkins的持續(xù)集成
接口匯總報告:
接口詳細(xì)報告:
歡迎交流指正跨琳,感謝閱讀。更多接口測試文檔歡迎關(guān)注 頭條號:馬蟻蛋桐罕。公眾號:testpu