The problem
My JMeter test calculates amount of threads that should be allocated to a thread group using JS expressions like this:
threadsGroupA expression: ${__javaScript(${__property(threads)}*${__property(threadPercentageA)})}
threadsGroupB expression: ${__javaScript(${__property(threads)}*${__property(threadPercentageB)})}
The properties passed to script:
threads=100
threadPercentageA=0.3
threadPercentageB=0.7
The expected result is: 30 threads assigned to group A and 70 threads assigned to group B.
This works as expected on my workstation. However, when I try to run it on a server, it does not create threads unless I use comma as a decimal separator in properties file:
threads=100
threadPercentageA=0,3
threadPercentageB=0,7
I am looking for a way to run the test with same property file in any environment.
What I tried
I have tried to force JVM to use locale with a decimal point . by adding -Duser.language=en -Duser.country=US -Duser.region=us (in different permutations) as variablesJMETER_OPTS, JMETER_LANGUAGE and also directly passed to JMeter as command line arguments(JMeter doc reference). None of that seemed to have any effect.
My thoughts
My conclusion at this point is that the problem is happening in JS engine that evaluates __javaScript()function. If there was a way to set locale for JS engine, I would be able to check this, hence the question in subject.
Use __groovy function and cast to integer/double the values before multiply:
${__groovy(props.get("threads").toInteger() * Double.parseDouble(props.get("threadPercentageB")),)}
Try using parseFloat() function for your decimal values like:
${__javaScript(${__property(threads)}*parseFloat(${__property(threadPercentageA))})}
If the output is decimal as well - in JavaScript there is Number.prototype.toLocaleString() function where you can explicitly set the locale for the Number object so you will have the confidence that it will return dot or comma no matter where it is run.
Also be aware that JavaScript engine is removed from the latest JDK/JRE versions so if you have a lot of JavaScript in your JMeter tests it makes sense to consider migrating to GraalVM or switching to __jexl3() or __groovy() functions
JS solution
Use parseFloat(), which always expects ., regardless of locale:
${__javaScript((${__property(threads)}*parseFloat("${__property(threadPercentageA)}")).toFixed())}
Credits for original idea to Dmitri T's answer
Groovy solution
${__groovy((props.get("threads").toInteger() * Double.parseDouble("${props.get('threadPercentageA')}")).round())}
Credits for original idea to user7294900's answer
Note about JMeter thread count handling
Turns out JMeter will not accept number of threads with decimal part, even if it is .0. Therefore, answers above include rounding functions.
Related
I have several processes with almost same flow like "Get some parameters, extract data from database according to them and upload them to target". The parameters vary slightly across processes as well as targets but only a bit. Most of the process is the same. I would like to extract those differences to parameter-context and dynamically load them. My idea is to have parameters defined following way and then using them.
So core of question is:
How to dynamically choose which parameter group load and use?
Having several parameter contexts with same-named/different-valued parameters and dynamically switching them would be probably the best, but it is not possible as far as I know.
Also duplicating flows is out-of-the-table. Any error correction would be spread out over several places and maintenance would be a nightmare.
Moreover, I know I can do it like "In GenetrateFlowFile for process A set value1=#{A_value1} and in GenetrateFlowFile for process B set value1=#{B_value1}. But this is tedious, error-prone and scales kinda bad. Not speaking of situation when I can have dozens of parameters and several processes. Also it is a kind of hardcoding, not configuring...
I was hoping for something like defining group=A and then using it like value1=#{ ${ group:append('_value1') } } but this does not work - it is evaluated as parameter literally named ${ group:append('_value1') }.
TL;DR: Use evaluateELString().
The actual solution is to set in GenetrateFlowFile processor group=A and in next UpdateAttribute processor set the following:
value1=${ group:prepend('hash{ '):append('_value1 }'):replace('hash', '#'):evaluateELString() }
The magic being done here is "Take value of group slap around it #{ and _value1 } to make it valid NiFi Expression Language statement and then evaluate it." (Notice - the word hash and function replace is there since I didnĀ“t manage to escape the # char right before {.)
If you would like to have your value1 at the beginning of the statement then you can use following code. The result is same, it is easier to use (often-changed value value1 is at the beginning of the statement) and is less readable "what is really going on?"-wise.
value1=${ literal('value1'):prepend('_'):prepend(${ group }):prepend('hash{ '):append(' }'):replace('hash', '#'):evaluateELString() }
Today when I was debugging my JMeter script, I found a problem that confused me a lot.
CSV Data Config element: in CSV, I set variable userId to 1001200
Then run script below, and get different value of "userId" when using ${userId} and vars.get("userId"). I think they should be same value, but it seems not. After run vars.put("userId", "-111"), ${userId} and vars.get("userId") get different values:
so it seems ${} and vars.get() have some difference even though their variable is same, does anyone know the reason?
Thanks in advance.
Yes, you need to follow best practices when scripting and avoid using ${value}
When using JSR 223 elements, it is advised to check Cache compiled script if available property to ensure the script compilation is cached if underlying language supports it. In this case, ensure the script does not use any variable using ${varName} as caching would take only first value of ${varName}. Instead use :
vars.get("varName")
Answer provided by #user7294900 refers to the case when Cache compiled script option is checked. But even if it's not checked, your script will resolve variables defined as ${varName} before execution, while vars.get("varName") is resolved during execution.
Before JMeter is about to run any element (sampler, pre- or post-processor), it will take (every) text field and will resolve any variables, functions or inline code, identified by ${...} to their values available at the time of the evaluation. Thus syntax ${...} converts variable into a constant string and your code (for Groovy or any other execution engine) will look like this:
log.info("***" + "1001200" + "***");
log.info("***" + vars.get("userId") + "***");
vars.put("userId", "-111");
log.info("***" + "1001200" + "***");
log.info("***" + vars.get("userId") + "***");
Thus no matter how you change variable during execution, it won't change since it's no longer a variable. But vars.get("userId") on the other hand, is a function call and will check variable value every single time.
I have assigned a variable ${WH} to 22 and now want to assign ${WH} to another variable called ${W_ID}. I have tried a couple of things, but none of them seem to be working.
None of below working (used in User Defined variable page):
W_ID=${__eval(${WH})}
W_ID=${__evalVar(WH)}
W_ID=${__V(${WH})}
I can't figure out why the value doesn't get stored in W_ID. How can this be done?
You can add JSR223 Sampler language can be Javascript and write:
vars.put("WS_ID", vars.get("WH"))
This will move WH value to WS_ID variable.
There's an issue if you are using multiple User Defined Variables, later assignment can override previous assignment, try to avoid it. The reason can be found in manual:
Note that all the UDV elements in a test plan - no matter where they
are - are processed at the start.
You can use the __groovy() function available since JMeter 3.1.
The relevant Groovy expression which will read ${WH} variable value and save the result into ${W_ID} variable will look something like:
${__groovy(vars.get('WH'),W_ID)}
Demo:
You can put the function anywhere in your script. See Functions and Variables JMeter User Manual chapter for more information.
How can I get the value of important id and ValueType?
I have tried using web_save_param_regexp (but unfortunately I don't fully understand how the function works).
I have also tried using web_save_param (with the help of offset and length).
unfortunately once again I cannot get the accurate value some values change in length specially when the total amount values dynamically changes per run.
<important id=\"insertsomevalueshere\" record=\"1\" nucTotal=\"NUC609.40\"><total amount=\"68.75\" currency=\"USD\"/><total amount=\"609.40\" currency=\"USD\"/><out avgsomecost=\"540.65\" ValueType=\"insertsomevalueshere\" containsawesomeness=\"1\" Score=\"-97961\" somedatatype=\"1\" typeofData=\"VAL\" web=\"1\">
Put these lines of code before the line of code which does your web request:
web_reg_save_param_regexp("ParamName=importantid","Regexp=<important id=\\\"(.*?)\\\"",LAST);
web_reg_save_param_regexp("ParamName=ValueType","Regexp= ValueType=\\\"(.*?)\\\"",LAST);
You will then have two stored parameters 'importantid' and 'ValueType'
Dynamic number of elements to correlate? Your path for resubmission is through web_custom_request(). You will need to build the string you need dynamically with the name:value pairs for all of the data which needs to be included.
This path will place a premium on your string manipulation skills in the language of the tool. The default path is through C, but you have other language options if your skills are more refined in another language.
I was reading the JMeter documentation and came across this info box about "If Controllers":
No variables are made available to the script when the condition is interpreted as Javascript. If you need access to such variables, then select "Interpret Condition as Variable Expression?" and use a __javaScript() function call. You can then use the objects "vars", "log", "ctx" etc. in the script.
I don't quite follow this. Does this mean if I want access to a "User Defined Parameter" then I can access it only by writing some JavaScript? The example that follows this box then refers to "${COUNT}"
Could someone clarify the usage of the If Controller, maybe with an example or two?
All these answers are wrong! You need to put the variable reference in quotes, like so:
"${my_variable}"=="foo"
You can simply use something like
${my_variable}=='1'
Sometimes JMeter documentation can be confusing :)
Edit 27 september 2017:
The answer here works but has a very bad performance impact when number of threads exceeds 40.
See below for correct and most performing answer:
https://stackoverflow.com/a/46976447/460802
See:
https://bz.apache.org/bugzilla/show_bug.cgi?id=61675
UNCHECK the CHECKBOX
"Interpret condition as variable expression"
I wasted a couple of hours without unchecking this checkbox. It worked with and without semicolon(;) at the end of the statement. Make sure that you have set the User-Defined Variables before calling the if controller.
All the following variations worked for me in Jakarta Jmeter 1.5
${__javaScript("${HOMEPAGE}"=="Y")}
${__javaScript("${HOMEPAGE}"=="Y")};
"${HOMEPAGE}"=="Y"
"${HOMEPAGE}"=="Y";
If Controller will internally use javascript to evaluate the condition but this can have a performance penalty.
A better option (default one starting from JMeter 4, see https://bz.apache.org/bugzilla/show_bug.cgi?id=61675) is to check "Interpret Condition as Variable Expression?", then in the condition field you have 2 options:
Option 1 : Use a variable that contains true or false. For example If you want to test if last sample was successful, you can use
${JMeterThread.last_sample_ok}
or any variable you want that contains true/false
${myVar}
Option 2 : Use a function (${__jexl3()} is advised) to evaluate an expression that must return true or false.
For example if COUNT is equal to 1:
${__jexl3("${COUNT}"== "1",)}
OR
${__jexl3(${COUNT}== 1,)}
Starting with 4.0, if you don't use the "Interpret Condition as Variable Expression?", a warning in RED will be displayed:
If you'd like to learn more about JMeter and performance testing this book can help you.
God bless the http://habrahabr.ru
Have tried until found these.
Using the quotes was my solution.
As Gerrie said you need to check your variable
${my_var} == 'value'
But be careful with the 'User Defined Variables'
Note that all the UDV elements in a
test plan - no matter where they are -
are processed at the start.
That basically means that you cannot define 'User Defined Variables' inside an 'If Controller'. Take a look to the 'BeanShell' instead.
Replace:
${my_variable}=='1'
with
"${my_variable}" == "1"
if it's string value pass as below and its performance effective
${__groovy("${key}"=="value")}
I have used ${code_g1}== 200 in condition and it worked for me.