Jmeter強大之處是其開源性和可擴展性,所以Jmeter擁有大量豐富的插件和元件,還有各種好用的函數,如果能巧妙應用函數助手裏的函數,就能讓性能測試腳本插上翅膀,實現各種複雜的計算和邏輯應用場景。
以下是我整理的各個版本所啓用的函數(可能也會有一點出入,歡迎大家指正),這些函數在不同的jmeter版本中啓用,所以jmx腳本的兼容性一定要考慮到這些因素(如果函數一但不能被解析,就會出問題),以下列表只是說明了函數的作用,具體使用方法可以見Jmeter函數助手(主要是參數及相關格式要搞清楚):
函數類型 | 函數名稱 | 函數作用 | 啓用版本 |
---|---|---|---|
獲取信息函數 | __TestPlanName |
返回當前測試計劃的名稱,調用 ${__TestPlanName} | |
__threadGroupName |
返回當前線程組的名稱 | 4.1 | |
__threadNum |
返回當前正在執行的線程的編號 | 1.X | |
__samplerName |
返回當前請求的名稱 | 2.5 | |
__log |
輸出日誌信息,示例 ${__log(報錯了,ERR,,)},另一個簡化的函數__logn,少了第四個參數[日誌註釋] | 2.2 | |
__machineName |
獲取當前機器名稱,調用${__machineName()} | 2.2 | |
__machineIP |
獲取當前機器IP,調用${__machineIP()} | 2.2 | |
__time |
以多種格式返回當前時間,默認13位時間戳如 ${__time(,)} | 2.2 | |
__timeShift |
可對日期時間進行移位加減操作,包含5個參數(格式,日期,移位,語言環境,存儲變量);舉例 ${__timeShift(dd/MM/yyyy,,P-1D,,)}表示以指定格式創建當前日期,減去一天 | 4.0 | |
數據輸入函數 | __StringFromFile |
從文本文件中讀取字符串,每次調用讀取一行 | 1.9 |
__FileToString |
把文件讀取成一個字符串,每次調用都是讀取整個文件 | 2.4 | |
__CSVRead |
讀取參數文件的值,如讀取第一列的參數 ${__CSVRead(D:\jmeter\login.txt,0)} | 1.9 | |
__XPath |
使用 XPath 語法匹配 XML文件 | 2.0 | |
__evel |
返字符串表達式的結果。可以在一個變量中插入一個有值的字符串表達式或函數或變量,比如變量嵌套:舉例${__evel{你好$id}},其中id來自txt參數文件,文件中的id第一行爲1${__UUID},第二行2${__UUID},以此類推 | 2.0 | |
__evelVar |
作用同evel,只是參數不是表達式,是變量名,這個變量的值允許含表達式或函數或變量,如 ${__evalVar(SQL)} | 2.0 | |
__V |
嵌套函數,作用類似於__evel,支持在變量中嵌套變量,如 ${__V(userName_${no},)} | 2.0 | |
數據計算函數 | __counter |
計數器函數 | 1.9 |
__intSum |
對多個整數求和,如:${__intSum(${year},-1,)} | 1.8.1 | |
__longSum |
長整型求和 | 2.3.2 | |
__Random |
返回指定最大值和最小值之間的隨機整數 | 1.9 | |
__RandomDate |
返回給定開始日期和結束日期值之間的隨機日期 | 3.3 | |
__RandomString |
根據給定的字符生成指定長度的隨機字符串 | 2.6 | |
__RandomFromMultipleVars |
從多個變量中隨機取一個變量值,用|號分隔變量 | 3.2 | |
__dateTimeConvert |
提供兩種時間格式的快速轉換,舉例:${__dateTimeConvert(01 Jan 2017,dd MMM yyyy,dd/MM/yyyy,)} | 4.0 | |
__UUID |
通用唯一標識符函數,如${__UUID} | 2.9 | |
__digest |
加密計算,支持MD5、SHA等;如:${__digest(MD5,Apache JMeter 4.0 rocks !,,,)} | 4.0 | |
__char |
ASCII碼/十進制 轉 字符,如:${__char(97)}輸出a | 4.0 | |
屬性信息函數 | __isPropDefined |
判斷屬性是否存在 | 4.0 |
__setProperty |
用於動態設置JMeter屬性,一般用於不同線程組之間傳遞變量,如將舊變量保存爲全局變量:${__setProperty(new_var,${old_var},false)} | 2.0 | |
__property |
獲取屬性值的函數,支持將結果另存爲變量,如 ${__property(START.MS,新變量,默認值)} | 1.8.1 | |
__P |
簡化的獲取屬性值函數,用於與命令行上定義的屬性一起使用,不支持另存爲變量,如 ${__P(START.MS,默認值)} | 2.0 | |
字符串操作函數 | __split |
根據分隔符拆分字符串爲多個變量,如${__split(1\,2\,3\,4,var,\,)} 逗號分隔符用\轉義,分解完var_1表示第一個值1 | 2.0.2 |
__changeCase |
轉換大小寫,如轉爲小寫${__changeCase(ABC,LOWER,)} | 4.0 | |
__regexFunction |
使用正則表達式解析之前的響應結果(一般不用它,用正則表達式提取器),包含6個參數,具體使用見[這裏] | 1.X | |
__escapeHtml |
轉換爲HTML格式的字符,支持HTML 4.0實體,對應反傳函數爲__unescapeHtml;類似函數還有__escapeXml | 2.0 | |
__urlencode |
將字符串進行url轉碼;對應的解碼函數是__urldecode | 2.0 | |
__unescape |
去除字符串中的轉義符 | 2.0 | |
腳本函數 | __BeanShell |
參數爲beanshell腳本表達式,如${__BeanShell(123*456,)}:返回56088;${__BeanShell(source("function.bsh",))}:執行function.bsh代碼; | 1.X |
__groovy |
參數爲groovy腳本表達式,如${__groovy(123*456,)}:返回56088;${__groovy(${num}%2==1)}:實現if控制; | 1.X | |
__javaScript |
執行 js 腳本,涉及逗號要用\轉義,變量要用""包含,如:${__javaScript('${var}'.slice(2\,4))} | 1.9 | |
__jexl |
使用Jexl表達式引擎解析,包括兩個版本__jexl2和__jexl3,如${__jexl3(${num}<10)}也相當於if控制 | 1.9 | |
驗證信息函數 | __isVarDefined |
測試屬性是否可用,如:${__isPropDefined(START.HMS)}返回true | 4.0 |
__isPropDefined |
測試變量是否可用,如:${__isVarDefined(JMeterThread.last_sample_ok)} 返回true | 4.0 |
除了Jmeter原裝的函數,大家還喜歡用到第三方的JMeterPlugins插件,比如JMeterPlugins-ExtrasLibs,這裏面還帶一些擴展的函數,一般使用率不高,但是其中的__env獲取環境變量,__chooseRandom隨機選取值,我們用的也比較多:
函數類型 | 函數名稱 | 函數作用 | 版本 |
---|---|---|---|
獲取信息函數 | __env |
獲取系統環境變量值,如${__env(JAVA_HOME,newName,C:\jdk1.8)} | 1.2 |
__iterationNum |
獲取循環迭代號,如${__iterationNum} | 1.2 | |
數據計算函數 | __base64Encode |
將字符串進行base64位編碼,對應的解碼函數爲__base64Decode | 1.2 |
__chooseRandom |
枚舉隨機數,從多個數值或字符串中隨機取值,用逗號分隔,最後一個逗號後面爲空或是變量名。舉例:${__chooseRandom(A1,B2,C3,D4,)},隨機4選1返回 | 1.2 | |
__doubleSum |
符點型求和 | 1.2 | |
__MD5 |
MD5串生成,如 ${__MD5(12345,)} 返回12345的md5加密串 | 1.2 | |
字符串操作函數 | __lowercase |
將字符串轉爲小寫字母,如${__lowercase(ABC,)} | 1.2 |
驗證信息函數 | __isDefined |
測試屬性或變量是否可用,如:${__isDefined(START.HMS)}返回1 | 1.2 |
除了用到Jmeter自帶的函數,以及JMeterPlugins插件所帶有的函數,我們還可以進行自定義函數的開發:
在package org.apache.jmeter.functions;中增加自定義函數,代碼模板如下:
package org.apache.jmeter.functions;
import java.util.Collection;
import java.util.LinkedList;
import java.util.List;
import org.apache.jmeter.engine.util.CompoundVariable;
import org.apache.jmeter.samplers.SampleResult;
import org.apache.jmeter.samplers.Sampler;
public class MyFrisFuncion extends AbstractFunction{
private static final List<String> desc = new LinkedList<String>();
public static void main(String[] args) {
// TODO Auto-generated method stub
}
@Override
public List<String> getArgumentDesc() {
/**
* Return a list of strings briefly describing each parameter your function
* takes. Please use JMeterUtils.getResString(resource_name) to grab a
* resource string. Otherwise, your help text will be difficult to
* internationalize.
*
* This list is not optional. If you don't wish to write help, you must at
* least return a List containing the correct number of blank strings, one
* for each argument.
* @return List with brief descriptions for each parameter the function takes
*/
//本方法簡要描述函數的每個參數,使用JMeterUtils.getResString可獲取中文資源字符串
//中文描述資源文件路徑爲 bin/core/org/apache/jmeter/resources/messages_zh_CN.properties
return desc;
}
@Override
public String execute(SampleResult arg0, Sampler arg1) throws InvalidVariableException {
/**
* Given the previous SampleResult and the current Sampler, return a string
* to use as a replacement value for the function call. Assume
* "setParameter" was previously called.
*
* This method must be threadsafe - multiple threads will be using the same
* object.
* @param previousResult The previous {@link SampleResult}
* @param currentSampler The current {@link Sampler}
* @return The replacement value, which was generated by the function
* @throws InvalidVariableException - when the variables for the function call can't be evaluated
*/
//本方法是提供一個在Jmeter調用的函數入口
return "Jmeter函數";
}
@Override
public String getReferenceKey() {
/**
* Return the name of your function. Convention is to prepend "__" to the
* name (ie "__regexFunction")
* @return The name of the function
*/
//本方法是提供一個在Jmeter函數助手顯示的函數名稱
return "__NewFunction";
}
@Override
public void setParameters(Collection<CompoundVariable> arg0) throws InvalidVariableException {
/**
* A collection of the parameters used to configure your function. Each
* parameter is a CompoundVariable and can be resolved by calling the
* execute() method of the CompoundVariable (which should be done at
* execution.)
*
* @param parameters The parameters for the function call
* @throws InvalidVariableException - when the variables for the function call can't be evaluated
*/
//本方法是設置函數參數使用的!checkParameterCount是校驗參數數量
//以下第2個參數爲MIN_PARAM_COUNT,第3個參數爲MAX_PARAM_COUNT,兩者差值就是可選參數數量
checkParameterCount(arg0, 0, 0); // 都是0表示無入參
}
}
2、重構以上代碼並打成jar包
(1)創建自定義函數__RandomEmail
package org.smooth.jmeter.functions;
import org.apache.jmeter.engine.util.CompoundVariable;
import org.apache.jmeter.functions.AbstractFunction;
import org.apache.jmeter.functions.InvalidVariableException;
import org.apache.jmeter.samplers.SampleResult;
import org.apache.jmeter.samplers.Sampler;
import org.smooth.jmeter.functions.core.RandomString;
import java.util.Collection;
import java.util.LinkedList;
import java.util.List;
/**
* 隨機生成電子郵箱
* @author smooth
* @date 2019-12-02 13:15
*/
@SuppressWarnings("unchecked")
public class RandomEmail extends AbstractFunction {
@Override
public String execute(SampleResult sampleResult, Sampler sampler) throws InvalidVariableException {
return RandomString.getEmail();
}
@Override
public void setParameters(Collection<CompoundVariable> collection) throws InvalidVariableException {
checkParameterCount(collection, 0, 0);
}
/**
* 提供jmeter函數助手顯示的下來選項名稱
**/
@Override
public String getReferenceKey() {
return "__RandomEmail";
}
public List<String> getArgumentDesc() {
return new LinkedList();
}
}
其中電子郵箱隨機生成的代碼如下:
package org.smooth.jmeter.functions.core;
/**
* @author smooth00
* @date 2019-12-02 11:21
*/
public class RandomString {
/** 普遍隨機字符串來源 */
private static final String[] SOURCE_STRING = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789".split("|");
/** 常用郵箱後綴 */
private static final String EMAIL_SUFFIX = "@gmail.com, @yahoo.com, @msn.com, @hotmail.com, @aol.com, @ask.com, @live.com, @qq.com, @0355.net, @163.com, @163.net, @263.net, @3721.net, @yeah.net, @googlemail.com, @126.com, @sina.com, @sohu.com, @yahoo.com.cn";
/**
* 獲取0到指定數值間的隨機數,內部使用
* @param max
* @return
*/
private static int getRandomNum(int max) {
if (max < 0) {
throw new RuntimeException("最大值需要大於或等於0");
}
return (int) (Math.random() * max);
}
/**
* 返回[0-9,a-z,A-Z]的隨機字符串
* @param len 字符串長度
* @return
*/
public static String getFixed(int len) {
StringBuilder resBuf = new StringBuilder();
for (int i = 0; i < len; i++) {
int randIndex = getRandomNum(SOURCE_STRING.length);
resBuf.append(SOURCE_STRING[randIndex]);
}
return resBuf.toString();
}
/**
* 生成隨機指定範圍的定長字符串
*
* @param src 產生隨機字符串來源,使用英文逗號分開
* @param len 返回字符串長度
* @return
*/
public static final String getRange(String src, int len) {
if (src == null || src.length() == 0) {
throw new RuntimeException("來源字符串爲空");
}
if (len < 1) {
throw new RuntimeException("返回字符串長度需要大於0");
}
String[] arr = src.split(",");
StringBuffer buf = new StringBuffer();
for (int i = 0; i < len; i++) {
int index = getRandomNum(arr.length);
buf.append(arr[index].trim());
}
return buf.toString();
}
/**
* 生成隨機電子郵箱
* @return
*/
public static String getEmail() {
// 生成用戶名 6-16 位長度
int len = getRandomNum(10) + 6;
String userName = getFixed(len).toLowerCase();
String email = getRange(EMAIL_SUFFIX, 1);
return userName + email;
}
}
(2)Maven Build生成jar包,
(3)將jar包拷貝至$JMETER__HOME/lib/ext目錄下,重啓Jmeter
3、在Jmeter的函數助手中看到新增的函數,並調試通過
4、具體代碼我已發佈,可以參考 https://gitee.com/smooth00/jmeter-ExtraFunc-plugins
代碼結構:
jmeter-ExtraFunc-plugins
├─src
│ └─main
│ ├─java
│ └─resources
├─pom.xml
開發說明:
- 1、Jmeter插件必須在包含
.functions
包下 - 2、創建一個類,繼承
org.apache.jmeter.functions.AbstractFunction
,重寫下面方法:
/** 函數執行邏輯,並返回值 */
public String execute(SampleResult sampleResult, Sampler sampler);
/** 接受處理入參 */
public void setParameters(Collection<CompoundVariable> collection);
/** 定義返回函數名稱 */
public String getReferenceKey();
/** 定義函數入參說明 */
public List<String> getArgumentDesc();