JMeter內(nèi)置變量大揭秘:含義,用法和實例
在JMeter中,有一些內(nèi)置的變量,可以幫助我們在測試過程中存儲和使用一些數(shù)據(jù)。這些內(nèi)置變量有四種:vars,props,prev和sample。
vars變量
vars變量是JMeterVariables類的一個實例,它是一個Map類型的對象,可以存儲String或Object類型的數(shù)據(jù)。vars變量的作用域是當(dāng)前線程組,也就是說,只有同一個線程組內(nèi)的線程才能訪問和修改同一個vars變量。如果不同的線程組需要共享數(shù)據(jù),就不能使用vars變量。
vars變量的使用方法很簡單,我們可以在BeanShell Sampler或JSR223 Sampler中使用以下語法來獲取和設(shè)置vars變量:
// JSR233 groovy 腳本
//獲取vars變量
String value = vars.get("key");
Object obj = vars.getObject("key");
//設(shè)置vars變量
vars.put("key", "value");
vars.putObject("key", new Object());
直接使用${key}來引用vars變量的值。
vars變量的一個常見用途是保存上一個請求的響應(yīng)數(shù)據(jù),以便后續(xù)請求使用。例如,我們可以在 JSR233PostProcessor中使用以下代碼的一些實例:
又比如從 csv 文件中讀取數(shù)據(jù),并保存到一個list對象中:
然后從其他取樣器中使用這個對象:
props變量
props變量是JMeterProperties類的一個實例,它是一個Hashtable類型的對象,也可以存儲String或Object類型的數(shù)據(jù)。props變量的作用域是全局的,也就是說,所有的線程組都可以訪問和修改同一個props變量。如果不同的線程組需要共享數(shù)據(jù),就可以使用props變量。
props變量的使用方法和vars變量類似,我們可以在BeanShell Sampler或JSR223 Sampler中使用以下語法來獲取和設(shè)置props變量:
//獲取props變量
String value = props.get("key");
Object obj = props.get("key");
//設(shè)置props變量
props.put("key", "value");
props.put("key", new Object());
我們也可以在其他元件中使用${__P(key)}來引用props變量的值。
props變量的一個常見用途是保存一些全局配置參數(shù),例如服務(wù)器地址,端口號等。例如,我們可以在Test Plan中使用User Defined Variables元件來定義一些props變量:
然后,在其他地方,我們可以使用${__P(server)}來引用服務(wù)器地址。
prev變量
prev變量是SampleResult類的一個實例,它是一個對象,可以存儲上一個請求(或者說上一個取樣器)的結(jié)果信息。prev變量的作用域是當(dāng)前線程組,并且只能在后置處理器(PostProcessor)或斷言(Assertion)中使用。
prev變量的使用方法是在BeanShell PostProcessor或JSR223 PostProcessor中使用以下語法來獲取prev變量:
SampleResult prev = ctx.getPreviousResult();
然后,我們可以調(diào)用prev對象的各種方法來獲取結(jié)果信息,例如:
prev變量的一個常見用途是對上一個請求的結(jié)果進(jìn)行處理或判斷。例如,我們可以在BeanShell Assertion中使用以下代碼來判斷響應(yīng)碼是否為200:
SampleResult prev = ctx.getPreviousResult();
if (!"200".equals(prev.getResponseCode())) {
AssertionResult result = new AssertionResult("檢查檢查狀態(tài)碼");
result.setFailure(true);
result.setFailureMessage("響應(yīng)狀態(tài)碼鬼知道是啥,反正不是 200");
prev.addAssertionResult(result);
prev.setSuccessful(false);
}
當(dāng)前了,你要是直接在jsr233或者beanshell中直接如下那樣寫,也不會出現(xiàn)錯誤,也能直接使用。
//jsr233 中的代碼
def responseCode = prev.getResponseCode();
sample變量
sample變量是SampleEvent類的一個實例,它是一個對象,可以存儲當(dāng)前請求(或者說當(dāng)前取樣器)的事件信息。sample變量的作用域是當(dāng)前線程組,能在監(jiān)聽器(Listener)中使用。
sample變量的使用方法是在BeanShell Listener或JSR223 Listener中使用以下語法來獲取sample變量:
SampleEvent sample = ctx.getCurrentSampleEvent();
然后,我們可以調(diào)用sample對象的各種方法來獲取事件信息,例如:
//獲取取樣器結(jié)果
SampleResult result = sample.getResult();
//獲取線程名稱
String threadName = sample.getThreadName();
//獲取線程組名稱
String threadGroupName = sample.getThreadGroup();
//獲取主機名稱
String hostName = sample.getHostname();
sample變量的一個常見用途是對當(dāng)前請求的事件進(jìn)行處理或記錄。例如,我們可以在BeanShell Listener中使用以下代碼來打印事件信息:
SampleEvent sample = ctx.getCurrentSampleEvent();
log.info("Thread name: " + sample.getThreadName());
log.info("Thread group: " + sample.getThreadGroup());
log.info("Host name: " + sample.getHostname());
log.info("Sampler name: " + sample.getResult().getSampleLabel());
log.info("Response time: " + sample.getResult().getTime());
log.info("Response code: " + sample.getResult().getResponseCode());
log.info("Response data: " + new String(sample.getResult().getResponseData()));
內(nèi)置變量的區(qū)別
從上面的介紹可以看出,JMeter內(nèi)置變量有以下幾個區(qū)別:
- vars和props都是Map類型的對象,可以存儲和修改數(shù)據(jù);prev和sample都是普通對象,只能讀取數(shù)據(jù)。
- vars和props都可以在任何地方引用;prev只能在后置處理器或斷言中引用;sample只能在監(jiān)聽器中引用。
- vars和props都需要手動設(shè)置和獲取;prev和sample都由JMeter自動提供。
- vars只能在當(dāng)前線程組內(nèi)共享;props可以跨線程組共享;prev和sample只能在當(dāng)前線程內(nèi)訪問。
- vars和props都只能存儲String或Object類型;prev和sample都包含多種類型的數(shù)據(jù)。
內(nèi)置變量的實際工作場景
來看一些實際工作場景:
場景一:我們需要模擬用戶上傳文件,并且每個用戶都要上傳不同的文件。這時候,我們就可以使用JSR223 PreProcessor元件來動態(tài)生成一個文件名,并將它保存到props變量中。然后,在上傳文件請求中,我們就可以使用${__P(filename)}來引用文件名。
//在JSR223 PreProcessor的代碼如下:
import java.util.UUID
//生成一個隨機的UUID作為文件名
String filename = UUID.randomUUID().toString() + ".txt"
//將文件名保存到props變量中
props.put("filename", filename)
場景二:我們需要對每個請求的響應(yīng)時間進(jìn)行判斷,如果超過了預(yù)期的時間,就要記錄下來。這時候,我們就可以使用JSR223 Assertion元件來獲取prev變量,并調(diào)用getTime()方法來獲取響應(yīng)時間。然后,我們就可以使用if語句來判斷響應(yīng)時間是否超過了預(yù)期,并使用log.info()方法來記錄日志。
//在JSR223 Assertion 中的代碼如下:
def prev = ctx.getPreviousResult()
//獲取響應(yīng)時間
def responseTime = prev.getTime()
//設(shè)置預(yù)期時間為1000毫秒
def expectedTime = 1000
//判斷響應(yīng)時間是否超過預(yù)期
if (responseTime > expectedTime) {
// 這里建議將結(jié)果寫入 csv 以便持久化查看
log.info("響應(yīng)時間是:${responseTime} ms, 預(yù)期時間是:${expectedTime} ms")
}
場景三:我們需要對每個請求的響應(yīng)數(shù)據(jù)進(jìn)行處理,如果包含了某些關(guān)鍵字,就要提取出來,那么則如下:
//在JSR223 PostProcessor 中的代碼如下:
def prev = ctx.getPreviousResult()
def responseData = prev.getResponseDataAsString()
// 使用正則處理數(shù)據(jù)
def regex = /<title>(.*?)<\/title>/
def matcher = regex.matcher(responseData)
if (matcher.find()) {
def keyword = matcher.group(1)
vars.put("keyword", keyword)
}
// 當(dāng)然除了上述代碼外,也可以直接使用正則匹配元件去處理。
場景四:我們需要對每個請求的事件信息進(jìn)行記錄,例如線程名稱,線程組名稱,主機名稱等,那么則如下:
//在JSR223 Listener 中的代碼
def sample = ctx.getCurrentSampleEvent()
def result = sample.getResult()
//打印事件信息到日志文件中,建議處理到csv(不過會有一點性能開銷)
log.info("線程名稱: " + sample.getThreadName())
log.info("線程組名: " + sample.getThreadGroup())
log.info("域名地址: " + sample.getHostname())
log.info("取樣器名: " + result.getSampleLabel())
log.info("響應(yīng)時間: " + result.getTime())
log.info("響應(yīng)編碼: " + result.getResponseCode())
log.info("響應(yīng)數(shù)據(jù): " + new String(result.getResponseData()))