I am using Gatling to measure the performance of a delete API. The url for the delete looks like
https://endpoint.com/rest/<id>/delete
So I basically want to invoke the delete API with different ID's. My scenario looks something like this:
val scenario =
exec(
http("${scenario}")
.post(getUrl())
.headers(getHeaders())
.body(StringBody(body))
.check(status.is(200))
)
.exec(session => {
val response = session("responsePayload").as[String]
logger.info(response)
session;
})
the getUrl() methods returns the endpoint with a unique id each time it's called. However I see that the method gets called only once & the url returned for the first time is being used in all subsequent calls.
What would be the best way to solve my use-case ?
You need to pass a function instead of a hardcoded value so your method is evaluated on each invocation:
.post(session => getUrl())
Related
[EDITED: I realized after reading response that I oversimplified my question.]
I am new to Locust and not sure how to solve this problem.
I have function (call it "get_doc") that is passed a locust.HttpSession() and uses it to issue an HTTP request. It gets the response and parses it, returning it up several layers of call. One of these higher-level calls looks at the returned, parsed document to decide if the response was what was expected or not. If not, I want Locust to mark the request/response as failed. A code sketch would be:
class MyUser (HttpUser):
#task
def mytask(self):
behavior1 (self.client)
def bahavior1(session):
doc = get_doc(session, url1)
if not doc_ok (doc):
??? how to register a failure with Locust here...
doc2 = get_doc(session, url2)
...
def get_doc(http_session, url):
page = http_session.get(url)
doc = parse (page)
return doc
There may be several behavior[n] functions and several Locust users calling them.
A constraint is that I would like to keep Locust-specific stuff out of bahavior1() so that I can call it with an ordinary Requests session. I have tried to do something like this in get_doc() (the catch_response parameter and success/fail stuff is actually conditionalized on 'session' being an HttpSession object):
def get_doc (session, meth, url):
resp = session.request (meth, url, catch_response=True)
doc = parse (resp.content)
doc.logfns = resp.success, resp.failure
return doc
and then in behavior1() or some higher up-chain caller I can
doc.logfns[1]("Document not as expected")
or
doc.logfns[0] # Looks good!
Unfortunately this is not working; the calls to them produce no errors but Locust doesn't seem to record any successes or failures either. I am not sure if it should work or I bungled something in my code. Is this feasible? Is there a better way?
You can make get_doc a context manager, call .get with catch_response=True and yield instead of return inside it. Similar to how it is done here: https://github.com/SvenskaSpel/locust-plugins/blob/2cbbdda9ae37b6cbb0a11cf69aca80b164198aec/locust_plugins/users/rest.py#L22
And then use it like this
def mytask(self):
with get_doc(self.client, url) as doc:
if not doc_ok(doc):
doc.failure(”doc was not ok :(”)
If you want, you can add the parsed doc as a field on the response before yielding in your doc function, or call doc.failure() inside doc_ok.
Using the Play 2.2.3 framework, given I have a route like this one in my routes file :
GET /event controllers.Event.events
How can I programatically get the "/event" knowing the method I am trying to reach, in this case 'controllers.Authentication.authenticate' ?
I need it for test purpose, because I would like to have better test waiting not for the route itself but for the controllers method really called. So maybe there is another solution than getting the route and test it this way like I do for now :
//Login with good password and login
val Some(loginResult) = route(FakeRequest(POST, "/login").withFormUrlEncodedBody(
("email", email)
, ("password", password)))
status(loginResult)(10.seconds) mustBe 303
redirectLocation(loginResult).get mustBe "/event"
You construct the reverse route in this way:
[full package name].routes.[controller].[method]
In your example:
controllers.routes.Authentication.authenticate
controllers.routes.Events.events
But say you broke your packages out like controllers.auth and controllers.content, then they would be:
controllers.auth.routes.Authentication.authenticate
controllers.content.routes.Events.events
The reverse routes return a Call, which contains an HTTP method and URI. In your tests you can construct a FakeRequest with a Call:
FakeRequest(controllers.routes.Authentication.authenticate)
And you can also use it to test the redirect URI:
val call: Call = controllers.routes.Events.events
redirectLocation(loginResult) must beSome(call.url)
My tests run fine but now I need multiple sessions running at once. I've tried getting the cookie value using headerRegex("Set-Cookie", "HOME_SESSID=(.*)").saveAs("homeSessid") but when I print this out its returning a value of com.excilys.ebi.gatling.http.check.HttpMultipleCheckBuilder#6075598
I have no idea where this is coming from. My question is: what is going on?
Thanks.
edit: forgot to mention that the value its returning is not a session id and no matter what I use for the cookie name I get the same value.
edit (solution):
1) In the first .exec: .check( headerRegex("Set-Cookie", """HOME_SESSID=(.*dll/(\d+))""").saveAs("homeSessid") )
2) Then to retrieve homeSessid in later http requests I did, for example:
.post( session=>{session}.getAttribute("homeSessid").toString + "/some/relative/url" )
1) In the first .exec:
.check( headerRegex("Set-Cookie", """HOME_SESSID=(.*dll/(\d+))""").saveAs("homeSessid") )
2) Then to retrieve homeSessid in later http requests I did, for example:
.post( session=>{session}.getAttribute("homeSessid").toString + "/some/relative/url" )
Please properly read the Check documentation. Checks save data into the Session, so that's where you have to read. Here, you're just trying to print the extractor.
For example, after performing your check, you could add a exec(function), like:
.exec(session => {
println(session("homeSessid").as[String]) // Gatling 2 API
session
})
My web application will be triggered from an external system. It will call one request path of my app, but uses different query parameters for different kinds of requests.
One of the parameters is the "action" that defines what is to be done. The rest of the params depend on the "action".
So I can get request params like these:
action=sayHello&user=Joe
action=newUser&name=Joe&address=xxx
action=resetPassword
...
I would like to be able to encode it similarly in the routes file for play so it does the query param based routing and as much of the validation of other parameters as possible.
What I have instead is one routing for all of these possibilities with plenty of optional parameters. The action processing it starts with a big pattern match to do dispatch and parameter validation.
Googling and checking SO just popped up plenty of samples where the params are encoded in the request path somehow, so multiple paths are routed to the same action, but I would like the opposite: one path routed to different actions.
One of my colleagues said we could have one "dispatcher" action that would just redirect based on the "action" parameter. It would be a bit more structured then the current solution, but it would not eliminate the long list of optional parameters which should be selectively passed to the next action, so I hope one knows an even better solution :-)
BTW the external system that calls my app is developed by another company and I have no influence on this design, so it's not an option to change the way how my app is triggered.
The single dispatcher action is probably the way to go, and you don't need to specify all of your optional parameters in the route. If action is always there then that's the only one you really need.
GET /someRoute controller.dispatcher(action: String)
Then in your action method you can access request.queryString to get any of the other optional parameters.
Note: I am NOT experienced Scala developer, so maybe presented snippets can be optimized... What's important for you they are valid and working.
So...
You don't need to declare every optional param in the routes file. It is great shortcut for type param's validation and best choice would be convince 'other company' to use API prepared by you... Anyway if you haven't such possibility you can also handle their requests as required.
In general: the dispatcher approach seems to be right in this place, fortunately you don't need to declare all optional params in the routes and pass it between actions/methods as they can be fetched directly from request. In PHP it can be compared to $_GET['action'] and in Java version of Play 2 controller - DynamicForm class - form().bindFromRequest.get("action").
Let's say that you have a route:
GET /dispatcher controllers.Application.dispatcher
In that case your dispatcher action (and additional methods) can look like:
def dispatcher = Action { implicit request =>
request.queryString.get("action").flatMap(_.headOption).getOrElse("invalid") match {
case "sayHello" => sayHelloMethod
case "newUser" => newUserMethod
case _ => BadRequest("Action not allowed!")
}
}
// http://localhost:9000/dispatcher?action=sayHello&name=John
def sayHelloMethod(implicit request: RequestHeader) = {
val name = request.queryString.get("name").flatMap(_.headOption).getOrElse("")
Ok("Hello " + name )
}
// http://localhost:9000/dispatcher?action=newUser&name=John+Doe&address=john#doe.com
def newUserMethod(implicit request: RequestHeader) = {
val name = request.queryString.get("name").flatMap(_.headOption).getOrElse("")
val address = request.queryString.get("address").flatMap(_.headOption).getOrElse("")
Ok("We are creating new user " + name + " with address " + address)
}
Of course you will need to validate incoming types and values 'manually', especially when actions will be operating on the DataBase, anyway biggest part of your problem you have resolved now.
Action create shows form:
def create = Action {
Ok(html.post.create(postForm))
}
How can i modify this action so that for GET request it would give out form and for the POST request it would process user input data, as if it were a separate action:
def newPost = Action { implicit request =>
postForm.bindFromRequest.fold(
errors => BadRequest(views.html.index(Posts.all(), errors)),
label => {
Posts.create(label)
Redirect(routes.Application.posts)
}
)
}
Wthat i mean is i want to combine this two actions.
UPDATE1: I want a single Action that serves GET and POST requests
It is recommended not to merge both actions, but modify routes to get the behavior you are expecting. For instance:
GET /create controllers.Posts.create
POST /create controllers.Posts.newPost
In case you have several kind of resources (post and comments, for instance), just add
a prefix to the path to disambiguate:
GET /post/create controllers.Posts.create
POST /post/create controllers.Posts.newPost
GET /comment/create controllers.Comments.create
POST /comment/create controllers.Comments.newComment
I tried once to accomplish similar thing, but I realized that I wasn't using framework like it was meant to be used. Use separate GET and POST methods like #paradigmatic showed and in cases like you specified "If we take adding comments to another action, we wouldn't be able to get infomation on post and comments in case an error occured (avoding copy-paste code)." - just render the page at the end of controller method with the view you like? and for errors etc. you can always use flash scope too? http://www.playframework.org/documentation/2.0.2/ScalaSessionFlash you could also render this form page with two or more beans and send them to controller side to catch related error messages and data.?