I need to re-do the setup with every thread(number). Now I have 2 times first the setup thread, and then 2 times the main thread. In setting apply 2 threads in SetUp and Main
The problem is that I end up getting requests with the same JSESSION ID
I want to see like this->
First thread:
CSRF-TOKEN
LOGIN
Test(first csrf login)
Second thread:
CSRF-TOKEN
LOGIN
Test(second csrf-token)
I know that this can be done in one thread, but there can be many such requests. And so we need a single setup, so as not to duplicate
UPDATED:
changed my code like you said and now it doesn't work.
I use in postprocessor this code:
import org.apache.jmeter.protocol.http.control.CookieManager;
import org.apache.jmeter.protocol.http.control.Cookie;
import org.apache.jmeter.testelement.property.PropertyIterator;
import org.apache.jmeter.testelement.property.JMeterProperty;
CookieManager manager = ctx.getCurrentSampler().getCookieManager();
PropertyIterator iter = manager.getCookies().iterator();
while (iter.hasNext()) {
JMeterProperty prop = iter.next();
Cookie cookie = prop.getObjectValue();
if (cookie.getName().equals("JSESSIONID")) {
vars.putObject("JSESSIONID", cookie);
break;
}
}
while (iter.hasNext()) {
JMeterProperty prop = iter.next();
Cookie cookie = prop.getObjectValue();
if (cookie.getName().equals("XSRF-TOKEN")) {
vars.putObject("XSRF-TOKEN", cookie);
break;
}
}
This Preprocessor main thread:
CookieManager manager = sampler.getCookieManager();
manager.add(vars.getObject("JSESSIONID"));
manager.add(vars.getObject("XSRF-TOKEN"));
But now it doesn't work
I used to use:
props.put("JSESSIONID", cookie);
The correctness of your test design is a big question mark, normally you don't need to pass any data between thread groups.
However if you've been told to do so by your not very competent team lead looking at my crystal ball I can see the following possible problems:
If you're using something like props.put("property-name", vars.get("csrf-token")) you're overwriting the previous value with the next value on each occurrence/iteration because properties are global for the whole JVM process. Consider using ctx.getThreadNum() as the property prefix/postfix
If you're using something like props.put("property-name", "${csrf-token}") the first occurrence of variable is being cached and used on subsequent iterations. Consider using vars.get("csrf-token") instead
More information on these ctx, vars and props guys: Top 8 JMeter Java Classes You Should Be Using with Groovy
Related
I am trying to set up a new Cypress framework and I hit a point where I need help.
The scenario I am trying to work out: a page that is calling the same endpoint after every change and I use an interceptor to wait for the call. As we all know you can’t use the same intercept name for the same request multiple times so I did a trick here. I used a dynamically named alias, which works.
var counters = {}
function registerIntercept(method, url, name) {
counters[name] = 1;
cy.intercept(method, url, (req) => {
var currentCounter = counters[name]++;
cy.wrap(counters).as(counters)
req.alias = name + (currentCounter);
})
}
function waitForCall(call) {
waitForCall_(call + (counters[call.substr(1)]));
// HERE counters[any_previously_added_key] is always 1, even though the counters entry in registerIntercept is bigger
// I suspect counters here is not using the same counters value as registerIntercept
}
function waitForCall_(call) {
cy.wait(call)
cy.wait(100)
}
It is supposed to be used by using waitForCall(“#callalias”) and it will be converted to #callalias1, callalias2 and so on.
The problem is that the global counters is working in registerIntercept but I can’t get its values from waitForCall. It will always retrieve value 1 for a specific key, while the counters key in registerIntercept is already at a bigger number.
This code works if you use it as waitForCall_(“#callalias4”) to wait for the 4th request for example. Each intercept will get an alias ending with an incremented number. But I want to not keep track of how many calls were made and let the code retrieve that from counters and build the wait.
Any idea why counters in waitForCall is not having the same values for its keys as it has in registerIntercept?
I know how to prepare a test plan and run it in JMeter using the Java API, there are quite a number of examples on how to do that. What's missing is a way to collect the results directly. I know it's possible to save the results in a .jtl file but that would require me to open the file after saving it and parse it (depending on its format). I have seen the API provides quite a number of Result classes but I wasn't able to figure out how to use them. I have also tried debugging to try to figure out what classes were involved and try to understand an execution model.
Any help would be really appreciated
Right, I am not sure if that is the right answer, I think there's no right answer because it really depends on your needs. At least I managed to understand a bit more debugging a test execution.
Basically what I ended up doing was to extend the ResultCollector and add it to the instance of TestPlan. What the collector does is to store the events once received and print them at the end of the test (but at this point you can do whatever you want with it)
If you have better approaches please let me know (I guess a more generic approach would be to implement SampleListener and TestStateListener without using the specific implementation of the ResultCollector)
import java.util.LinkedList;
import org.apache.jmeter.reporters.ResultCollector;
import org.apache.jmeter.samplers.SampleEvent;
public class RealtimeResultCollector extends ResultCollector{
LinkedList<SampleEvent> collectedEvents = new LinkedList<>();
/**
* When a test result is received, store it internally
*
* #param event
* the sample event that was received
*/
#Override
public void sampleOccurred(SampleEvent event) {
collectedEvents.add(event);
}
/**
* When the test ends print the response code for all the events collected
*
* #param host
* the host where the test was running from
*/
#Override
public void testEnded(String host) {
for(SampleEvent e: collectedEvents){
System.out.println("TEST_RESULT: Response code = " + e.getResult().getResponseCode()); // or do whatever you want ...
}
}
}
And in the code of the main or wherever you have created your test plan
...
HashTree ht = new HashTree();
...
TestPlan tp = new TestPlan("MyPlan");
RealtimeResultCollector rrc = new RealtimeResultCollector();
// after a lot of confugration, before executing the test plan ...
ht.add(tp);
ht.add(ht.getArray()[0], rtc);
For the details about the code above you can find a number of examples on zgrepcode.com
In my frontend I have an input-field that sends an ajax request on every character typed in (using vue.js) to get realtime-filtering (can't use vue filter because of pagination).
Everything works smooth in my test environment, but could this lead to performance issues on (a bigger amount of) real data and if so, what can I do to prevent this?
Is it problematic?
Yes.
The client will send a lot of requests. Depending on the network connection and browser, this could lead to a perceptible feeling of lag by the client.
The server will receive a lot of requests, potentially leading to degraded performance for all clients, and extra usage of resources on the server side.
Responses to requests have a higher chance of arriving out of order. If you send requests very fast, it has increased chances of being apparent (e.g. displaying autocomplete for "ab" when the user has already typed "abc")
Overall, it's bad practice mostly because it's not necessary to do that many requests.
How to fix it?
As J. B. mentioned in his answer, debouncing is the way to go.
The debounce function (copied below) ensures that a certain function doesn't get called more than once every X milliseconds. Concretely, it allows you to send a request as soon as the user hasn't typed anything for, say, 200ms.
Here's a complete example (try typing text very fast in the input):
function debounce(func, wait, immediate) {
var timeout;
return function() {
var context = this, args = arguments;
var later = function() {
timeout = null;
if (!immediate) func.apply(context, args);
};
var callNow = immediate && !timeout;
clearTimeout(timeout);
timeout = setTimeout(later, wait);
if (callNow) func.apply(context, args);
};
}
var sendAjaxRequest = function(inputText) {
// do your ajax request here
console.log("sent via ajax: " + inputText);
};
var sendAjaxRequestDebounced = debounce(sendAjaxRequest, 200, false); // 200ms
var el = document.getElementById("my-input");
el.onkeyup = function(evt) {
// user pressed a key
console.log("typed: " + this.value)
sendAjaxRequestDebounced(this.value);
}
<input type="text" id="my-input">
For more details on how the debounce function works, see this question
I actually discuss this exact scenario in my Vue.js training course. In short, you may want to wait until a user clicks a button or something of that nature to trigger sending the request. Another approach to consider is to use the lazy modifier, which will delay the event until the change event is fired.
It's hard to know the correct approach without knowing more about the goals of the app. Still, the options listed above are two options to consider.
I hope this helps.
The mechanism I was searching for is called debouncing.
I used this approach in the application.
My scenario is that I am sending HTTP requests within a loop, in which the arguments values are based on JMeter variables. When first entering the loop I might have variables which are not set at this point, so they are null, hence I’d like to remove the argument from the HTTP request. I can successfully do so with the code below. However, at a later loop-iteration, this variable could now have a value and I would like to include the argument which I have previously removed. So my question is, how can I temporarily remove the argument from my HTTP request?
I have a JMeter Test Plan extract according to:
While Controller
Some logic
HTTP Request
JSR223 PreProcessor (groovy)
My HTTP Request has the following arguments:
Name Value
inputA ${A}
inputB ${B}
My PreProcessor script looks like:
for (Iterator iterator = sampler.getArguments().iterator();
prop = iterator.next();
String value = prop.getStringValue();
if (value.contains('\${')) {
iterator.remove();
}
}
The fastest, the easiest and imho the correct solution would be using __evalVar() JMeter Function like:
So in case if ${A} variable is not defined - inputA parameter will be sent with an empty value and such parameters are ignored by the well-behaved applications. See series of How to Use JMeter Functions guides to get started with JMeter Functions
I recall answering something similar here, it assumed checking if the variables is null using Beanshell
If for some reason points 1 and 2 are not applicable, here is correct code to completely remove the parameter:
import org.apache.jmeter.config.Arguments;
Arguments args = sampler.getArguments();
Iterator it = args.iterator();
while (it.hasNext()) {
def argument = it.next();
if (argument.getStringValue().contains('${')) {
args.removeArgument(argument.getName());
}
}
A solution to this problem could be to make a clone of the sampler Arguments object and set it to the sampler.
import org.apache.jmeter.config.Arguments;
Arguments arguments = sampler.getArguments().clone();
sampler.setArguments(arguments);
for (Iterator iterator = sampler.getArguments().iterator();
prop = iterator.next();
String value = prop.getStringValue();
if (value.contains('\${')) {
iterator.remove();
}
}
I have two for loops and an HTTP call inside them.
for(i=0;i<m;i++) {
for(j=0;j<n;j++) {
$http call that uses i and j as GET parameters
.success(//something)
.error(//something more)
}
}
The problem with this is it makes around 200-250 AJAX calls based on values of m and n. This is causing problem of browser crash when tried to access from mobile.
I would like to know if there is a way to call HTTP requests in batched form (n requests at a time) and once these calls are finished, move to next batch and so on.
You could always use a proper HTTP batch module like this angular-http-batcher - which will take all of the requests and turn them into a single HTTP POST request before sending it to the server. Therefore it reduces 250 calls into 1! The module is here https://github.com/jonsamwell/angular-http-batcher and a detailed explanation of it is here http://jonsamwell.com/batching-http-requests-in-angular/
Yes, use the async library found here: https://github.com/caolan/async
First, use the loop to create your tasks:
var tasks = []; //array to hold the tasks
for(i=0;i<m;i++) {
for(j=0;j<n;j++) {
//we add a function to the array of "tasks"
//Async will pass that function a standard callback(error, data)
tasks.push(function(cb){
//because of the way closures work, you may not be able to rely on i and j here
//if i/j don't work here, create another closure and store them as params
$http call that uses i and j as GET parameters
.success(function(data){cb(null, data);})
.error(function(err){cb(err);});
});
}
}
Now that you've got an array full of callback-ready functions that can be executed, you must use async to execute them, async has a great feature to "limit" the number of simultaneous requests and therefore "batch".
async.parallelLimit(tasks, 10, function(error, results){
//results is an array with each tasks results.
//Don't forget to use $scope.$apply or $timeout to trigger a digest
});
In the above example you will run 10 tasks at a time in parallel.
Async has a ton of other amazing options as well, you can run things in series, parlallel, map arrays, etc.It's worth noting that you might be able to achieve greater efficiency by using a single function and the "eachLimit" function of async.
The way I did it is as follows (this will help when one wants to call HTTP requests in a batch of n requests at a time )
call batchedHTTP(with i=0);
batchedHTTP = function() {
/* check for terminating condition (in this case, i=m) */
for(j=0;j<n;j++) {
var promise = $http call with i and j GET parameters
.success(// do something)
.error(// do something else)
promisesArray.push(promise);
}
$q.all(promisesArray).then(function() {
call batchedHTTP(with i=i+1)
});
}