JMeter, postprocessing a sample - java

in my project i would want to implement a plugin for JMeter.
So currently I am stuck at sampler - postprocessing step.
#Override
public void postProcessSampler(HTTPSamplerBase sampler, SampleResult result)
{
super.postProcessSampler(sampler, result);
String postData = sampler.getPropertyAsString(HTTPSamplerBase.ARGUMENTS);
// ...
// apply some operations to postData
// ...
//
// try to write it back to sampler : approach1
// sampler.removeProperty(HTTPSamplerBase.ARGUMENTS);
// sampler.addNonEncodedArgument(HTTPSamplerBase.ARGUMENTS, postData, "");
// Fails
}
So at the postprocessing step i would like to change the request body, whcih is usually stored in HTTPSamplerBase.ARGUMENTS property. However, somehow i cannot set anything to this field. Redefining it with another string gives me a class cast error. If I try operating with strings, then i get invocation exception...
So my question is, what is the correct way to change the sampler's post body?
Regards and thank you

Try out HTTPSamplerBase.getArguments() function instead, example code:
sampler.getArguments().removeAllArguments();
sampler.addNonEncodedArgument("foo","bar","");
sampler.setPostBodyRaw(true);
Also be aware that for this form of post-processing you don't even need to come up with a plugin, all can be done via JSR223 PostProcessor and Groovy language. The above code should work just fine

Related

How can I properly set up this endpoint?

I'm making an URL shortener with the Javalin framework and have this endpoint set up:
app.routes(()->{
path("",()->{
get("/:id", ctx->{
//do stuff
ctx.redirect("somewhere.com");
});
});
});
Problem is when I need to serve a javascript file to load into my html files. It tries to load from http://localhost:7000/qrcode.min.js but ends up going to the endpoint mentioned above. From what I read in the documentation this is normal behaviour, Javalin first runs the endpoint handler and then (if it doesn't find an endpoint) runs the file handler.
So how can I fix this? should I define a GET request at "/qrcode.min.js"?, I dont think the javalin context handler has a function that lets me return a .js file.
As Matt already suggested in a comment, it would be way cleaner if you'd prefix either path. That way, you could have /r/:id (or /u/:id with "u" for "URL") and the static files would not get in your way, or you could prefix your static files with e.g. /static/, or even just /s/ for brevity, and your shortened URLs would not get in your way.
If you, however, prefer to stick with your current scheme, you can simply filter out JavaScript files (or any other non-id request) in the handler and instead provide the file (however, if you previously had auto-generated ETags, you'd lose caching if you don't want to handle that yourself).
The latter solution would look like so:
app.routes (() -> {
path ("", () -> {
get ("/:id", ctx -> {
String id = ctx.pathParam ("id");
if (id.endsWith (".js")) {
String resourcePath = "your/classpath/resources/folder/" + id;
try {
InputStream resultStream = Thread.currentThread ()
.getContextClassLoader ()
.getResourceAsStream (resourcePath);
if (resultStream == null)
throw new NullPointerException ("Script not found");
ctx.contentType ("application/javascript");
ctx.result (resultStream);
} catch (NullPointerException e) { // script does not exist
e.printStackTrace (); // for development only!
ctx.status (404);
}
return;
}
// do stuff
ctx.redirect ("somewhere.com");
});
});
});
Depending on your preference, you can also handle the resultStream == null case where my code is currently throwing an NPE to be caught by the outer try/catch and omit the try/catch completely.
Setting the Content-Type is essential so that the browser knows that you're actually responding with JavaScript code. Also, I'm typically using Thread.currentThread ().getContextClassLoader () because we'd want the resource to be resolved based upon the current HTTP handler thread, which could, in theory, have a different class path/class loader than the class we're currently in.
Please note that, as stated above, this will not support client-side caching as the handler simply ignores all ETag headers sent with the request* and instead respond with the complete file which, with many requests in a short amount of time and large scripts, will certainly put way more stress on your disks and CPUs.
Thus, I'd actually recommend to prefix the static files route and let Javalin/Jetty handle all the caching and files magic.
* Actually, the header sent by the client is If-None-Match most of the time. The server would respond with an ETag to allow for caching in the browser.

How to intercept the return variable using Java Agent

I am intercepting a method and in my application using Java Agent. Through the Javassist bytecode manipulation framework I could be able to
addLocalVariable(..); - add local variable.
insertBefore(..); - insert method at the start of the body.
insertAfter(..); - insert method at the end of the body.
I am using this mechanism and I encounter a problem. say this is the code I am executing
fun(req,res){
interceptstart(req,res)
.....
....
logic
....
....
...
..
if (..){
res.add.....
return res
//then method in the below won't capture anything
}
else if(){
res.add.....
return res
//then method in the below won't capture anything
}
......
interceptend(req,res)
}
Here I wanted to capture the request and the response. But the problem I am facing now is I could be able to capture the request because request contains all the variable but I could not be able to capture the response because response variables are added and returned and there's no way that the interceptend will be executed.
is there any way to capture the return variable by intercepting using JavaAgent and Javassist?

How to test a HTTP request

I have set up this simple http request, which simply returns a "hello world" response to my IDE terminal. I have been looking into testing and I am not quite sure how i would test what this method is returning.
Currently i have done my own research into JUnit, but again i am not even sure if this would be the correct tool to use for this problem. I only researched this as it is a Java tool.
public static void newRequest() throws IOException {
URL helloServer = new URL("http://localhost:5050/");
HttpRequestFactory requestFactory = new NetHttpTransport().createRequestFactory();
HttpRequest request = requestFactory.buildGetRequest(new GenericUrl(helloServer));
HttpResponse rawResponse = request.execute();
String responseString = rawResponse.parseAsString();
logger.debug(responseString);
}
Any guidance on this would be greatly appreciated.
Does the function even need to be tested?
Does the function even need to be tested? Well, that is entirely up to you. Does this function contain code that is critical to your application? If so then yes. If the impact of a bug in this function is minimal then probably not.
Assuming that you want to test this, then:
The method in question is not returning anything void before the function name says this. You will need to look at testing the logic of the function. In this case you need to check that the correct response is received. There are two ways that I can think of to do this:
Modify the code to return the response.
You could change the function to return a String and then return rawResponse.parseAsString(); (which is the same thing you are logging.
Then you can call the function from the test and check the String that is returned.
Get the log message from your logger.
Depending on the logging that you are using, you could get the log message that was written by the function. Assuming log4j then there are some posts on how to do this:
log4j: how to get the last inserted log message?
Personally, I prefer the first option as it is less effort. I would also consider returning the body of the response rather than the raw response.

JMeter as code assertions are not being considered in test results

I'm using JMeter as a code (programmatic approach instead of GUI, with a Java Maven project) in order to stress-test an AWS Lambda Serverless API.
I've already developed a test plan, thread group, HTTPSamplerProxy and so on...
The execution of the calls to the API works perfect but is not the case e.g. for the DurationAssertion I've added to the HTTP Sampler..
I've also set a CSV file for the output where after execution I see everything ok (status code 200..), but the test should fail due to it is over the DurationAssertion I've configured (in addition to other assertion test elements).
I thought that perhaps I had to set "enabled" = true in the DurationAssertion object, but no effect.. Also, I've tried to access the JMeter Context in this way:
JMeterContextService.getContext().getPreviousResult()
I expected above code to retrieve a SampleResult (which has an AssertionResult collection), but the SampleResult is null..
A test plan with test elements (DurationAssertion in this case) without its respective analysis of the results of these assertions make no sense.. I want to see a failure message in each call that exceeds a certain threshold.. If I'd be using the JMeter GUI, I would add a ViewResultTree, which shows a Sampler Result view with detail of the request, response, and associated test asserts. And in addition to assertion result (per each request) I wanna see the request payload, full response, headers.. But in programmatic mode (without using the GUI).
So I would highly appreciate if anyone could give me some hint in how to accomplish this goal but by code.
UPDATE 1: I share a github snippet with the entire source code, such as UBIK LOAD PACK user suggested me:
https://gist.github.com/svillarreal/5eb90a66b8972633b95c249abb3566da
UPDATE 2: Inspection of context object (evaluated after JMeter engine finished run) - All null inside
UPDATE 3
i) I've recently found a jmeter.properties file, where I've configured the following properties:
jmeter.save.saveservice.output_format=xml
jmeter.save.saveservice.assertion_results=all
And now the output as XML instead of CSV shows, at least, the sent request payload and the response data, which is VERY useful for analyse error cases.
ii) I did the inspection of JMeterContextService.getContext() inside the JMeterEngine execution instead of after it finishes run and then I could realize that there is one context per thread group, and during its run this object is full so now is clear why in UPDATE 2 all the properties are null..
Best regards and thanks!
I can think about at least one use case when your approach will not work: JMeter didn't receive response from the server at all.
For example if your server gets overloaded it might be the case that JMeter will never get response back therefore your Duration Assertion will simply not be applied as PostProcessors, Listeners and Assertions are not fired given that SampleResult is null.
So in order to be on the safe side I would recommend applying connect and response timeouts to your HTTP Request sampler(s)
HTTPSamplerProxy httpSampler = new HTTPSamplerProxy();
httpSampler.setConnectTimeout("3000");
httpSampler.setResponseTimeout("3000");
//etc.
If you have > 1 HTTP Request sampler in Test Plan it makes sense to go for HTTP Request Defaults instead of setting the timeouts individually.
Finally I could fix this. The issue was that I was managing erroneously the tree that is passed to the StandardJMeterEngine.
In JMeter everything is based on this tree, and like in the GUI, we should take care about how the elements are positioned in its hierarchy.
Analysing the library and debugging it intensely I've realized more in deep how JMeter works and I've understood that everything is managed starting from the HashTree. So the solution was to add the DurationAssertion and ResponseAssertion as HTTPSamplerProxy node's childs instead of putting them as HTTPSamplerProxy's test elements.
In particular, the method that fills the assertions to check after the execution is the following (and that let me know how to manage the hashtree):
// org.apache.jmeter.threads.TestCompiler
private void saveSamplerConfigs(Sampler sam) {
List<ConfigTestElement> configs = new LinkedList<>();
List<Controller> controllers = new LinkedList<>();
List<SampleListener> listeners = new LinkedList<>();
List<Timer> timers = new LinkedList<>();
List<Assertion> assertions = new LinkedList<>();
LinkedList<PostProcessor> posts = new LinkedList<>();
LinkedList<PreProcessor> pres = new LinkedList<>();
for (int i = stack.size(); i > 0; i--) {
addDirectParentControllers(controllers, stack.get(i - 1));
List<PreProcessor> tempPre = new LinkedList<>();
List<PostProcessor> tempPost = new LinkedList<>();
List<Assertion> tempAssertions = new LinkedList<>();
for (Object item : testTree.list(stack.subList(0, i))) {
if (item instanceof ConfigTestElement) {
configs.add((ConfigTestElement) item);
}
if (item instanceof SampleListener) {
listeners.add((SampleListener) item);
}
if (item instanceof Timer) {
timers.add((Timer) item);
}
if (item instanceof Assertion) {
tempAssertions.add((Assertion) item);
}
if (item instanceof PostProcessor) {
tempPost.add((PostProcessor) item);
}
if (item instanceof PreProcessor) {
tempPre.add((PreProcessor) item);
}
}
assertions.addAll(0, tempAssertions);
pres.addAll(0, tempPre);
posts.addAll(0, tempPost);
}
SamplePackage pack = new SamplePackage(configs, listeners, timers, assertions,
posts, pres, controllers);
pack.setSampler(sam);
pack.setRunningVersion(true);
samplerConfigMap.put(sam, pack);
}
Also I had to activate the following property:
jmeter.save.saveservice.assertion_results_failure_message=true
As a consequence now I have my CSV file report with the assertions results messages included in a exclusive column for that.
Well, issue resolved. ** I've updated the github snippet gist with the final solution ** Many thanks to all that read this post and tried to collaborate.
Best regards,

Parsing a string with multiple variations

I am curious as to what a better way to deal with this is, I wanted to challenge my self and see if I could break up, in a HashMap of key,value (or String, String), a string that could come back in almost any format.
the string in question is:
/user/2/update?updates=success
Thats right, a url request for a server. The issue - as we all know this could be any thing, it could come back in any form. I wanted to break it up so that it would look like:
Controller => user
action => update
params => ??? (theres a 2, a update=success ... )
Obviously The above is not a real java object.
But you get the idea.
What do you need? what have you done? what are you trying to do?
What I want to do is map this to a controller and action while passing in the parameters along the way. But i need to separate this up making sure to specify each step what is what.
What I have done is:
private Filter parseRoute(String route){
String[] parsedRoute = route.split("[?:/=]");
Filter filter = new Filter(parsedRoute);
return filter;
}
Splits on any thing that is in the url (note, : would be something like /user:id/update
so: user/2/update ... )
I then attempted to do:
public class Filter {
private HashMap<String, String> filterInfo;
public Filter(String[] filteredRoute){
if(filteredRoute.length > 0){
filterInfo.put("Controller", filteredRoute[0]);
}else{
throw new RoutingException("routes must not be empty.");
}
}
}
But this is not going to work as I expected it to...As there are too many variables at play.
including parameters before the action (those would just be used to search for that user), their could be nested routes, so multiple controller/action/controller/action ..
How would you deal with this? What would you suggest? How could you get around this? Should you just do something like:
route(controller, action, params, template); ? (template lets you render a jsp). if so how do you deal with the ?update=success
I am using HttpServer to set up the basics. But I am now lost. I am trying to keep routing as generic and "do what ever you want we will map it to the right controller, action and pass in the parameters" but I think I bit off more then I can chew.
I have looked at both spark and spring framework, and decided that the route you pass, we will map to a xml file to find the controller and action, I just need the data structure in place to do that ...
So I am looking to back up and still go with "pass me something, ill map it out."
I would probably use the URL from apache,
org.apache.tomcat.util.net.URL url = null;
try {
url = new org.apache.tomcat.util.net.URL("/user/2/update?updates=success");
// ... do some stuff with it...
} catch (Exception e) {
e.printStackTrace();
}
java.net.URI may help you.
you can get your path by getPath()
and get all of your query by getQuery(),then you can split the query by = to name value pairs.
URI uri = new URI("/user/2/update?updates=success");
// /user/2/update
System.out.println("path is " + uri.getPath());
// updates=success
System.out.println("query is " + uri.getQuery());

Categories

Resources