『動善時』JMeter基礎 — 31、JMeter中BeanShell斷言詳解
JMeter中的BeanShell斷言,可以使用BeanShell腳本來執行斷言檢查,可以用於更複雜的個性化需求,使用更靈活,功能更強大,但是要能夠熟練使用BeanShell語。
1、BeanShell簡介
Beanshell是一種類似JAVA的腳本語言,通過BeanShell可以對請求數據、響應數據或環境變數進行更加靈活的處理和判斷。還可以直接調用外部的JAR包,例如:可以直接引入現成的第三方JSON解析包來使用。
在JMeter的多種組件中都有BeanShell的身影,如下:
- 定時器:
BeanShell Timer
- 前置處理器:
BeanShell PreProcessor
- 取樣器:
BeanShell Sampler
- 後置處理器:
BeanShell PostProcessor
- 斷言:
BeanShell Assert
- 監聽器:
BeanShell Listener
在JMeter運行的流程式控制制中,BeanShell出現的位置,如下圖所示:
2、Beanshell的內置變數和方法
Beanshell有一些默認的內置變數和方法,用戶可以通過這些變數與JMeter進行交互,
例如:
prInt
:非GUI模式下列印資訊(輸出資訊到stdout
,標準輸出控制台)。log
:輸出資訊到日誌(文件)log.debu("調試資訊")
log.info("響應狀態碼" + ResponseCode)
log.warn("警告資訊")
log.error("出錯資訊")
ResponseCode
:響應狀態碼(String類型)。ResponseHeaders
:響應頭(String類型)。prev
:獲取當前請求結果prew.getResponseDataAsString()
:獲取響應體數據(String類型)。prew.getResponseCode()
:獲取狀態碼(同ResponseCode,String類型)。
vars
:操作JMeter變數String var1 = vars.get("變數名")
:獲取變數的值(假設為String類型)。vars.put("變數名", 變數值)
:設置變數值。
props
:操作JMeter屬性props.get(String,String)
可以獲取JMeter中已經生成的屬性。props.put(String,String)
可以創建和更新JMeter屬性。
ctx
:獲取當前執行緒上下文數據(可獲取所有資訊)ctx.getVariables("變數名")
:獲取變數值(同vars.get()
)。ctx.setVariables("變數名", "變數值")
:設置變數(同vars.put()
)。ctx.getProperties("屬性名")
:獲取屬性值(同props.get()
)。ctx.setProperties("屬性名","屬性值")
:設置屬性(同props.put()
)。ctx.getPreviousResult()
:獲取當前請求結果同(prev
)。ctx.getCurrentSampler()
:獲取當前取樣器。ctx.getPreviousSampler()
:獲取前一取樣器。ctx.getThreadNum()
:獲取執行緒數。ctx.getThreadGroup()
:獲取執行緒組。ctx. getThread()
:獲取當前執行緒。ctx.getEngine()
:獲取引擎。ctx.isSamplingStarted()
:判斷取樣器是否啟動。ctx.isRecording()
:判斷是否開啟錄製。ctx.getSamplerContext()
:獲取取樣器山下文數據。
提示:ctx詳細API可參考:JMeter上下文
3、BeanShell斷言介面詳解
添加BeanShell斷言組件操作:選中「取樣器」右鍵 —> 添加 —> 斷言 —> BeanShell斷言
。
介面如下圖所示:
BeanShell斷言組件的詳細說明:
- 名稱:BeanShell斷言組件的自定義名稱,見名知意最好。
- 注釋:即添加一些備註資訊,對該BeanShell斷言組件的簡短說明,以便後期回顧時查看。
Reset bsh.Interpreter before each call
:每個BeanShell測試元素都有自己的解釋器副本(對於每個執行緒)。如果重複調用測試元素,例如在循環內,除非選擇在每次調用之前重置bsh.Interpreter
選項,否則解釋器將保留在調用之間。一些長時間運行的測試可能會導致解釋器使用大量記憶體。
由於BeanShell的bsh.Interpreter
存在記憶體泄露,常規方法無法支援長時間的壓力測試。JMeter官網推薦,在使用BeanShell進行長時間測試時,打開選項Reset bsh.Interpreterbefore each call
,則在每次調用BeanShell程式前,都把解釋器重置,以釋放解釋器之前佔用的記憶體。參數 (-> String Parameters和String[]bsh.args)
:輸入String參數。String []bsh.args
是主類main函數的形式參數,是一個String 對象數組,可以用來獲取命令行用戶輸入進去的參數。- 腳本文件:腳本文件(可以填入腳本文件路徑),可以點擊後邊的瀏覽選擇腳本文件。
Script (see below for variables that are defined)
:編寫腳本,參照下文定義的變數(使腳本文件參照定義的變數來運行)
4、BeanShell斷言的使用
BeanShell斷言中可以通過ResponseCode
、ResponseHeaders
及pre.getResponseDataAsString()
來分別獲得String格式的響應狀態碼、響應頭、響應體數據。結合if
判斷,通過變數Failure=false
或Failure=true
來設置斷言是否通過。當設置Failure=true
時,還可以設置FailureMessage
來設置失敗原因。
我們以一個登陸介面,來演示BeanShell斷言組件的應用。
(1)測試計劃內包含的元件
添加元件操作步驟:
- 創建測試計劃。
- 創建執行緒組:
選中「測試計劃」右鍵 —> 添加 —> 執行緒(用戶) —> 執行緒組
。 - 在執行緒組裡面,添加取樣器「HTTP請求」組件:
選中「執行緒組」右鍵 —> 添加 —> 取樣器 —> HTTP請求
。 - 在取樣器下,添加斷言「BeanShell斷言」組件:
選中「取樣器」右鍵 —> 添加 —> 斷言 —> BeanShell斷言
。 - 在取樣器下,添加監聽器「斷言結果」組件:
選中「取樣器」右鍵 —> 添加 —> 監聽器 —> 斷言結果
。 - 在執行緒組裡面,添加監聽器「察看結果樹」組件:查看結果,
選中「執行緒組」右鍵 —> 添加 —> 監聽器 —> 察看結果樹
。
最終測試計劃中的元件如下:
點擊運行按鈕,會提示你先保存該腳本,腳本保存完成後會直接自動運行該腳本。
(2)登陸介面請求介面內容
標準的Post請求,填寫請求的基本資訊和參數即可。
編寫內容如下:
(3)BeanShell斷言介面內容
我把只要把自己編寫的BeanShell程式碼,複製到Script (see below for variables that are defined)
下的輸入框即可。
如果需要進行模擬壓力測試的時候,可以勾選上Reset bsh.Interpreter before each call
選項。我們這裡不用。
編輯好的介面,如下圖所示:
說明:
1)狀態碼斷言程式碼:
//狀態碼斷言
log.info("狀態碼:" + ResponseCode);
if(ResponseCode.equals("200")){
Failure=false; // 表示斷言成功
}
else{
Failure=true; // 表示斷言失敗
FailureMessage="響應狀態碼非200"; // 自定義的失敗資訊
}
註:字元串只能使用雙引號,字元串相等要使用
" ".equals(" ")
表達式。
2)響應體包含特定內容斷言程式碼:
//獲取響應數據
String response = prev.getResponseDataAsString();
log.info("響應體:" + response);
//響應數據包含
if(response.contains("登錄成功")){
Failure=false; // 表示斷言成功
}
else{
Failure=true; // 表示斷言失敗
FailureMessage="響應數據不包含登錄成功";
}
(4)查看運行結果
我們在察看結果樹組件中,觀察腳本運行之後的結果。
如果斷言正確,和正常發送請求一樣,如下圖:
如果斷言失敗,則會出現斷言失敗的提示,如下圖所示:
(5)斷言結果組件說明
也添加斷言結果監聽器,通過斷言結果組件來判斷斷言是否通過。
如下圖所示:
說明:
- 已通過的斷言僅顯示取樣器名稱。
- 未通過的,除了顯示取樣器的名稱,還顯示錯誤原因。
5、補充知識點
(1)JSON響應體欄位提取及斷言
將String類型的響應體轉為JSON對象並操作需要額外的JAR包,可以使用org.json.jar
或gson.jar
。
以json.jar
為例,下載後將其放入JMeter/lib
目錄下,重啟JMeter,添加BeanShell斷言程式碼,如下:
//JSON響應斷言
import org.json.*; //導入org.json包
String response = prev.getResponseDataAsString(); //獲取響應數據
JSONObject responseJson = new JSONObject(response); //轉為JSON對象
String message = responseJson.getString("message");
log.info("響應message欄位:" + message);
if(message.equals("成功")){
Failure=false;
}
else{
Failure=true;
FailureMessage="響應message欄位非成功";
}
JSONObject對象除了getString()
方法外,還支援:
getBoolean("欄位名")
:獲取布爾類型欄位值。getInt("欄位名")
:獲取整型欄位值。getLong("欄位名")
:獲取長整型欄位值。getDouble("欄位名")
:獲取雙精型欄位值。getJSONObject("欄位名")
:獲取嵌套Object類型欄位值,JSONObject類型。getJSONArray("欄位名")
:獲取嵌套Array類型,JSONArray類型。
(2)響應頭解析
響應頭原本為String類型,可以通過分割遍歷組裝成Map類型來提取響應頭中的項
Copyimport java.util.HashMap;
import java.util.Map;
//將字元串用換行符 截取為adc數組
String [] headersList = ResponseHeaders.split("\n");
Map headersMap = new HashMap(); //創建HashMap來從新組裝headers
for(int i=1;i<headersList.length;i++){
String [] itemList=headersList[i].split(": "); // 將每一條Headerr項按冒號分割
headersMap.put((itemList[0]), itemList[1]); // 分鍵值放入HashMap
}
String contentType = headersMap.get("Content-Type"); // 提取相應項
log.info("響應Content-Type:" + contentType)