Mocking input request objects in scala - scala

Is it possible to mock a request object into a mock server? I.e. I want to make a call multiple times but with just one change, the user id in this case: val request = MyServiceRequest(userId = "123", data = "static data"). I.e.
myService.doSomething(MyServiceRequest("1", any()).returns(MyServiceObject(requestId = "1"))
myService.doSomething(MyServiceRequest("2", any()).returns(MyServiceObject(requestId = "2"))
myService.doSomething(MyServiceRequest("3", any()).returns(MyServiceObject(requestId = "2"))
Right now I have to write out the full request each time, I just want to define one parameter.

Related

How to set different protocol for each user (threads running) in gatling

I am trying to create a performance test suite where i have array of ID's that will be picked in random. For each ID , there is a designated auth_token assigned .
In the protocol , if i pass the method where the random ID's would take , it always sets it to that particular ID for the entire operation .
I am expecting something like , i am defining 10 virtual users and for each user the protocol should change the ID and continue the scenario execution .
Currently Gatling is setting the protocol at the first and uses the same protocol for all the 10 users.
id = random.generate //generate random id
authHeader = Method(id);
def method (id:String) : String{
if(id=="id1")
return token1
else if(id=="id2")
return token2
""
}
val httpProtocol = http.baseUrl(baseURI)
.acceptHeader(header)
.authorizationHeader(authHeader)
.contentTypeHeader(contentType)
.userAgentHeader(agentHeader)
val scn1: ScenarioBuilder = scenario("name")
.exec(http("scenario1")
.post(device_context)
.body(payload)
.check(status.is(202)))
setUp(scn1.inject(atOnceUsers(2)).protocols(httpProtocol))```
In the above code i need the suite to run for 2 different id.
To make this work, you're going to have to use a session variable to hold your authHeader. The DSL methods define builders that are only executed once - which explains what you're seeing.
The best way to do this would be to construct a feeder to hold your ids and auth tokens (I'm assuming you're not using the id anywhere else)
So define a feeder that maps a key (just a string) to a map that contains the id and auth token
//a list of strings to hold the auth tokens
private val ids = List(Map("id" -> "id1", "auth" -> "token1"), Map("id" -> "id2", "auth" -> "token2"), ...)
//the feeder to put a random token into the session
private val idFeeder = Iterator.continually(Map("id" -> Random.shuffle(ids).head))
now when you call .feed on idFeeder you get a random Map that has keys for "id" and "auth" in the session variable "id"
so update your scenario to use the feeder and set an Authorization header (and remove .authorizationHeader from your protocol definition)
val scn1: ScenarioBuilder = scenario("name")
.feed(idFeeder)
.exec(http("scenario1")
.header("Authorization", "${id.auth})"
.post(device_context)
.body(payload)
.check(status.is(202)))
in your body you can access the user id string with "${id.id}"
alternatively you could update your protocol definition to have the ${id} reference, but I find it nicer to have the feeder value used in the same block as the call to .feed

Gatling httpRequest with dynamic method from collection

I want to build a Gatling scenario from a collection of structured data (StructuredDataCollection).
My problem is, that I'm unable to pass in the "method" (as in HTTP method) from an element from the collection into the http call of the actual test.
Here's a code snippet.
def testScenario(duration: Int) = scenario("SO").during(duration) {
exec {
session => {
val test = StructuredDataCollection.next()
val title = test.title
val method = test.method // Not being used, because it does not work like that :(
val endpoint = test.endpoint
val requiredParameters = test.requiredParameters
val code = test.code
session
.set("title", title)
.set("methodFUG", method).set("endpoint", endpoint)
.set("requiredParameters", requiredParameters)
.set("code", code)
}
}
.exec(
http("${title}")
.httpRequest("get", "${endpoint}") // TODO: method can't be passed in as an expression.
.queryParamMap("${requiredParameters}")
.check(status.is("${code}"))
)
}
As you can see, I've hard-coded "get", but I'll need that to be replaced with the actual value from the method property from the current selected item from the collection.
Unfortunately, Gatling's DSL isn't available in all the places where you'd expect it to be, and it's just reading that as a string.
It took me some time to realize, that
http("${title}").httpRequest("${methodFUG}", "${endpoint}") will actually make a HTTP call with the invalid method "${methodFUG}" and not the value from the collection element, which could be "GET", "POST", "PUT", "DELETE", and so on.
httpRequest signature is (method: String, url: Expression[String]), see documentation.
It cannot take an Expression, only a static String.

PlayFramework Testing: Uploading File in Fake Request Errors

I want to test my method, which requires uploading a file. It is initialized like this:
val tempFile = TemporaryFile(new java.io.File("/home/ophelia/Desktop/students"))
val part = FilePart[TemporaryFile](
key = "students",
filename = "students",
contentType = Some("text/plain"),
ref = tempFile)
val files = Seq[FilePart[TemporaryFile]](part)
val formData = MultipartFormData(
dataParts = Map(),
files = Seq(part),
badParts = Seq(),
missingFileParts = Seq())
I pass it into the FakeRequest:
val result = route(
FakeRequest(POST, "/api/courses/"+"4f3c4ec9-46bf-4a05-a0b2-886c2040f2f6"+"/import" )
.withHeaders("Authorization" -> ("Session " + testSessionA.id.string))
.withMultipartFormDataBody(formData)
)
But when I run the test I get the following error:
Cannot write an instance of play.api.mvc.AnyContentAsMultipartFormData to HTTP response. Try to define a Writeable[play.api.mvc.AnyContentAsMultipartFormData]
What am I doing wrong and how to fix it? I looked on the internet, I didnt find any useful way to understand and resolve this problem.
It's important to remember that http requests are entirely text. route() takes an implicit Writeable to convert the body type of the provided request into text. Without the right Writeable, there is no way to know how to turn MultipartFormData into a request body.
There doesn't seem to be a Writeable for MultipartFormData, but you can provide your own. jroper has a great Writeable you could use for reference. (EDIT: That code is buggy, here's a working Writeable for AnyContentAsMultipartFormData)
Once you have your Writeable, you will need to make it accessible to your call to route(). Bear in mind, you currently have a FakeRequest[AnyContentAsMultipartFormData], not a FakeRequest[MultipartFormData]. You can either convert your request first:
val request = FakeRequest(POST,
"/api/courses/"+"4f3c4ec9-46bf-4a05-a0b2-886c2040f2f6"+"/import" )
.withHeaders("Authorization" -> ("Session "))
.withMultipartFormDataBody(formData)
route(request.map(_.mdf).asInstanceOf[FakeRequest[MultipartFormData[TemporaryFile]]])
or make your Writeable a Writeable[AnyContentAsMultipartFormData].
route for a given Request[T] requires an implicit parameter of type Writeable[T] that knows how to serialize the request body, because it will actually call the controller action just like an actual web request would, by pushing bytes onto it.
The problem is that there is no Writeable[MultipartFormData] predefined (you can see which are in play.api.test.Writeables).
This means you basically have two options:
write your own Writeable that serializes a MultipartFormData into bytes
Skip the routing part and call the action directly instead, like in the accepted answer in Play Framework Testing using MultipartFormData in a FakeRequest. This way of testing actions takes a shortcut and does not actually serialize and deserialize the request.
IMHO the first option is way too much pain for the gain, but if you go down that road, maybe contribute it to play when you succeed.
One of the possible solutions is to use wsUrl. For example
"File uploading action" should {
"upload sent file and result in ID" in {
val file = Paths.get(getClass.getResource("/1.txt").toURI)
val action = wsUrl("/upload").post(Source.single(FilePart("file", "hello.txt", Option("text/plain"), FileIO.fromPath(file))))
val res = Await.result(action, timeout)
res.status mustBe OK
res.body contains "123"
}
}

Gatling 2 dynamic queryParam on each request

I am trying to run a load test using Gatling 2. I need to generate one of the query parameters dynamically on each request.
My scenario is defined like this:
val scn = scenario("Load Test Scenario")
.exec(
http("Test API")
.post(url)
.body(StringBody("Some XML"))
.queryParam("x", DigestUtils.md5Hex(generateX().getBytes("UTF-8")))
)
def generateX() : String = {
// generate random string and return
}
This only calls generateX once and uses the result in each request. Is there anyway to have the generateX call on every request?
You have to pass a function, not a value. See Gatling documentation about Expression.
Here, you can just discard the session input parameter as you don't use it, so you can simply write:
.queryParam("x", _ => DigestUtils.md5Hex(generateX().getBytes("UTF-8")))

Create groovy script for run REST request

I have a task to create groovy script which will run REST request and setup property.
I setup property by script:
testRunner.testCase.setPropertyValue( "ScriptProFrom", "BIF" )
testRunner.testCase.setPropertyValue( "ScriptProTo", "STD" )
But I can't find how to run REST request. I tried to do it like this:
myInterface = (RestService) testRunner.testCase.testSuite.project.getInterfaceByName("http://www.webservicex.net")
myOperation = myInterface.getOperationByName("ConversionRate")
myRequest = myOperation.getRequestByName("Request 1")
and get "Script-result: com.eviware.soapui.impl.RestRequest#6a80901" and it cool if it my request, but how to run it?
Please, help...
Normally if you have a testStep you can get it and then simply run it, however your are doing it in another way so you can use the submit method of com.eviware.soapui.impl.rest.RestRequest class. This method has two parameters, the context which is an instance of com.eviware.soapui.model.iface.SubmitContext interface and a boolean which indicates if the operation is asynchronous. In your code this could be:
myInterface = testRunner.testCase.testSuite.project.getInterfaceByName("http://www.webservicex.net")
myOperation = myInterface.getOperationByName("ConversionRate")
myRequest = myOperation.getRequestByName("Request 1")
// get the context
def context = testRunner.getRunContext()
// send the request synchronous
myRequest.submit(context,false)
EDIT BASED ON OP COMMENT:
The submit method returns an object which is instance of com.eviware.soapui.impl.wsdl.WsdlSubmit<T>, then you can invoke getResponse() on this object an get another object which is instance of com.eviware.soapui.model.iface.Response then from this you can use getContentAsString() to check the response content or getContentType() to check the content type and so on. Please note that if you invoke submit in asynchronous way you must validate that getStatus() returns com.eviware.soapui.model.iface.Submit.Status.FINISHED before getResponse(). I give you an example:
myInterface = testRunner.testCase.testSuite.project.getInterfaceByName("http://www.webservicex.net")
myOperation = myInterface.getOperationByName("ConversionRate")
myRequest = myOperation.getRequestByName("Request 1")
// get the context
def context = testRunner.getRunContext()
// send the request synchronous
def submitted = myRequest.submit(context,false)
// get the response
def response = submitted.getResponse()
// get the response content as string
def content = response.getContentAsString()
// i.e check that the response contains literal 'OK'
assert content.contains('OK'),"Response not contains OK literal"
Hope this helps,