Jmeter 接口自動化-腳本數據分離實例

1、 背景:

 爲了讓你們更加的瞭解Jmeter,而且使用起來遊刃有餘。
這篇咱們主要講一下,如何優雅的使用Jmeter一步步的實現接口自動化,完成腳本與數據分離,把可能對Jmeter腳本的維護轉移到csv文本中,下降接口變動時對腳本的維護,最終目標是實現寫好接口自動化腳本後,接口變動的維護都只要操做csv文件。java

Jmeter腳本,數據和報告地址:
https://github.com/grizz/jmeter-mastergit

testerhome地址:https://testerhome.com/topics/13029github

2、實例

先介紹一個Jmeter的函數-》csvRead函數,後續介紹使用的會比較多,熟悉的夥伴能夠直接跳過。正則表達式

一、csvRead函數使用:

csvRead函數是從外部讀取參數,能夠從一個文件中讀取多個參數。 
使用步驟:
一、先新建一個文件,例如test.csv(或test.txt),裏面的數據存放爲
grizz,qq1111
jiezai,qq1111json

文件爲用戶名和密碼,用逗號隔開,每一列表示一種參數,每一行則表示一組參數。數組

二、選項-》函數助手對話框-》函數助手,打開Jmeter的函數助手,選擇csvRead函數:app

 

 

其中:
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執行的時候,若是有多個線程,順序讀取每行的數據,若是線程組多於文件中的行數,則循環讀取。如線程數爲2,則第2個線程讀取的是第二行的數據,線程數爲3,線程數3大於文件中的行數2,則第3個線程讀取的是第一行的數據。測試

PS:這一函數並不適合於讀取很大的文件,由於整個文件都會被存儲到內存之中。對於較大的文件,請使用配置元件CSV Data Set或者StringFromFile 。可是咱們不是壓測,只是接口自動化,通常沒有太大的數據文件,啊哈哈哈哈。

默認狀況下,函數會在遇到的每個逗號處斷行,須要換一個分隔符(經過設置屬性csvread.delimiter來實現)
修改jmeter.properties文件:

#csvread.delimiter=,
修改成
csvread.delimiter=?

即把分隔符修改成?問號,注意前面的#號表明註釋,要去掉。重啓Jmeter生效。

二、使用的接口:

這裏咱們以兩個接口舉例,其中Content-Type=application/json
1·獲取token的接口/getToken
入參:
{
"flag":"test",
"appId":"001"
}
返回值:
{"returnFlag":"1000","returnMsg":"獲取token成功","token":"19940622"}

2·使用token的接口/useToken,主要是測試useToken接口,useToken接口的token須要從getToken接口的返回值中取,其實就是參數關聯。
入參:
{
"flag":"${token}",
"appId":"001"
}
返回值,以3個場景爲例:
{"returnFlag":"1000","returnMsg":"使用token成功"}
{"returnFlag":"1001","returnMsg":"token爲空"}
{"returnFlag":"1002","returnMsg":"token錯誤"}

對於接口的斷言,咱們默認"returnFlag":"1000"即接口業務正常返回,1001,1002表明接口針對業務的不一樣異常給予的返回,固然也在咱們的接口測試範圍內。

咱們分v1,v2,v3,v4,4個版本按部就班的講:

v1版本:

剛開始入門時,咱們的腳本可能會是這樣的
先請求getToken接口,並獲取token,用正則表達式提取以下

 

 

再請求useToken接口,flag的值輸入${token},使用提取到的token值。
入參:{"flag":"${token}","appId":"001"}
返回值:{"returnFlag":"1000","returnMsg":"使用token成功"}
斷言:"returnFlag":"1000",斷言成功 

 

 

再測試後續兩種場景,入參以下:
sampler-使用tokenv1-爲空:
入參:{"flag":"","appId":"002"}
返回值:{"returnFlag":"1001","returnMsg":"token爲空"}
斷言:"returnFlag":"1000",斷言失敗

sampler-使用tokenv1-錯誤:
入參:{"flag":"errorToken","appId":"003"}
返回值:{"returnFlag":"1002","returnMsg":"token錯誤"}
斷言:"returnFlag":"1000",斷言失敗
查看結果樹

 

 

這一頓操做下來,沒啥問題,由於useToken接口的3種場景咱們都覆蓋了,只要把異常的場景的斷言對應改一下,咱們的接口腳本就寫好了,能夠交付。可是若是咱們要實現接口自動化,那麼v1版本中sampler的重複率比較高,咱們考慮能不能把useToken的3種場景放到一個sampler中。
因而有了v2版本

v2版本:

因爲聰明的咱們有必定前瞻性,咱們知道,想下降腳本中sampler的重複率,須要藉助數據文件,咱們能夠把接口useToken的請求body直接從文件中讀

入參1{"flag":"${token}","appId":"001"}
入參2{"flag":"","appId":"002"}
入參3{"flag":"errorToken","appId":"003"}

入參2,3咱們能夠從文件中讀取沒問題,可是入參1這樣從文件中讀取,」${token}」讀取出來的值就是字符串」${token}」,而不是咱們想要的」19940622」,怎麼辦呢?
因而咱們思考着,能夠把接口useToken的請求分兩種狀況,須要正確的token和不須要正確的token,若是須要正確的token,咱們就在Jmeter中傳給他,其它參數仍是能夠在文本中讀取;若是不須要正確的token,則請求所有從文件中讀取。什麼意思呢,繼續往下看。

咱們設置存放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":"002"}

若是咱們useToken_v2.csv文件有3行,則設置auto_interface_v2線程數爲3。
由於對於csvRead函數,每個線程都有獨立的內部指針指向文件數組中的當前行。當某個線程第一次引用文件時,函數會爲線程在數組中分配下一個空閒行。如此一來,任何一個線程訪問的文件行,都與其餘線程不一樣(除非線程數大於數組包含的行數)。
當咱們須要正確Token時,咱們利用if控制器,若是文本的第二列(我這裏是以問號分隔的,由於默認是逗號,可是咱們接口json數據自己就有逗號,只能換一個)爲1,則flag從Jmeter中本身讀取;若是文本的第二列爲0則表明不須要正確token,那接口入參咱們就所有從文件中讀取。
If:${__CSVRead(${DATA}/useToken_v2.csv,1)}==1

 

 

sampler-使用tokenv2-haveToken的請求body爲:

 

 

{"flag":"${token}", ${__CSVRead(${DATA}/useToken_v2.csv,2)}
即請求body由兩部分組成,一部分來自Jmeter內,主要是獲取正確的token,另外一部分來自useToken_v2.csv文件的第3列。
咱們這裏token正確時,另外一個參數好像只有"appId":"001"這一種狀況,顯得好像這樣寫起來更加冗餘,其實否則,咱們appId也有可能爲空,也可能錯誤。這時候咱們的useToken_v2.csv文件應該會是這樣:

用例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"}

並且一個接口的入參不可能只有兩個,但通常token只會有一個,因此當接口參數多時,這樣寫仍是減小了必定量的sampler的重複率。

If:${__CSVRead(${DATA}/useToken_v2.csv,1)}==0
請求body就爲:${__CSVRead(${DATA}/useToken_v2.csv,2)}

 

 

再解釋一下這裏爲什了加了一個計算器,和接口名稱爲何命名爲使用tokenv2-noToken-${_number}。
首先,當咱們把線程數設置爲3時,其實getToken接口也跑了3次,可是其實咱們只須要它跑一次,取出正確的token就能夠了,getToken接口的if控制器跟計算器一塊兒做用,當第1個線程啓動時觸發if控制器的規則${_number}==1,第2,第3個線程是就不會觸發if控制器裏面的getToken接口。

 

 

useToken接口命名後面加一個${_number},
使用tokenv2-noToken-${_number}和使用tokenv2-haveToken-${_number};主要是當接口報錯時,能夠根據接口的名稱(其後面加了${_number},接口每一個場景${_number}都是不同的),判斷其對應useToken_v2.csv文件的哪一行致使報錯,可快速定位並進行報錯修改。
方便理解,給出運行效果圖以下:

 

 

v3版本:

v2版本咱們好像把咱們能作的都給作了,可是前提是
從文件中讀取,」${token}」讀取出來的值就是字符串」${token}」,而不是咱們想要的」19940622」!
隨着時間的推移,樓主真的前先後後看過網上各類Jmeter教程和使用,不下40次,畢竟本身一直有信念,別人能作的本身也能夠,1次看不懂的,那就看5次,5次還不懂,10次。不努力,沒有辦法比別人作得更好的。接着說隨着時間的推移,grizz發現那個前提是能夠打破的,由於Jmeter有個eval函數。
函數__eval能夠用來執行一個字符串表達式,並返回執行結果。
舉個栗子:
name=grizz
SQL=select * from able where name='${name}'
${ SQL }=select * from able where name='${name}'
${__eval(${SQL})}= select * from able where name='grizz'

如今咱們能夠設置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了,腳本看起來清新了一些,並且咱們把響應斷言也從文件中讀取。這樣開發修改接口的返回提示時,咱們能夠直接經過修改csv文件完成對應的修改,不須要去動jmeter腳本。
入參:${__eval(${__CSVRead(${DATA}/useToken_v3.csv,1)})}
斷言${__CSVRead(${DATA}/useToken_v3.csv,2)}

 

 

看到這咱們能夠思考一下,在v4版本還有哪些內容能夠優化?

v4版本:

v1到v2是入門到掌握,v2到v3應該是弱雞到熟悉,v4應該到星耀了吧,很想寫個v5版本,v5(威武),應該很強的怕。
咱們想一想,咱們的最終目標是實現寫好接口自動化腳本後,接口變動的維護都只要操做csv文件。
那麼當咱們的useToken接口新增了一個場景,token過時

useToken_v4.csv文件

用例個數?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"

設置線程組auto_interface_v4的線程數爲
${__CSVRead(${DATA}/useToken_v4.csv,1)},其實就是文件useToken_v4.csv第一行的第二列,就是4

 

 

由於咱們的線程數與咱們的文件行數掛鉤,但這裏爲何文件有5行,而線程數是4。前面說了,由於對於csvRead函數,每個線程都有獨立的內部指針指向文件數組中的當前行。當某個線程第一次引用文件時,函數會爲線程在數組中分配下一個空閒行。即Jmeter會分配一個線程去保存線程的屬性,如線程數,啓動時間,循環次數等。即當線程數設置爲${__CSVRead(${DATA}/useToken_v4.csv,1)}時,控制線程屬性的線程就讀取了useToken_v4.csv文件的第一行。

PS:再說一下csvRead函數
csvRead函數默認從文件第一行開始讀取,除非你二次開發,否則這個默認就一隻在(苦笑),就是說咱們很差加文件列的標題(固然能夠利用v4版本的第一行也行),下降了文件的可讀性。但當對某個文件進行第一次讀取時,文件將被打開並讀取到一個內部數組中。若是在讀取過程當中找到了空行,函數就認爲到達文件末尾了,即容許拖尾註釋。就是咱們能夠在文件寫完後回車一下,再寫一些文件的註釋,或者${__CSVRead(D:/test.csv,next)}瞭解一下,csvRead函數的第二個值爲next可自行進行文件行標的切換。

其實當咱們的腳本量化後,還有不少東西要考慮的,接口csv文件的命名規範,線程組和sampler的命名規範,由於線程組和sampler的名稱會在報告中體現,接口入口和出口標準,怎麼維護咱們的數據和腳本更優雅,怎樣更高效的運行腳本和生成更豐富的報告,更加的節省測試人力。

下一篇講一下jemter和ant,jenkins的持續集成
Jmeter+ant+Jenkins 接口自動化框架完整版
接口彙總報告:

 

 

接口詳細報告:

 

 

歡迎交流指正,感謝閱讀。

相關文章
相關標籤/搜索