I've been trying to perform a test that uses a mock Http server to respond and a function that returns a Future[String] or an Exception if the Http server response isn't 200.
I'm trying to achieve a test without using Awaits, but instead AsyncFunSuiteLike.
However the following test seems impossible to resolve without doing it synchronously:
test("Error responses") {
Future.sequence {
NanoHTTPD.Response.Status.values().toList.filter(status => status.getRequestStatus >= 400).map {
status => {
httpService.setStatusCode(status)
val responseBody = s"Request failed with status $status"
httpService.setResponseContent(responseBody)
val errorMessage = s"Error response (${status.getRequestStatus}) from http service: $responseBody"
recoverToExceptionIf[ServiceException] {
myObject.httpCall("123456")
}.map {
ex => assert(ex.getMessage === errorMessage)
}
}
}
}.map(assertions => assert(assertions.forall(_ == Succeeded)))
}
Basically the problem is that when the Futures are tested, the NanoHTTPD is set to the last valued set in the map, so all ex.getMessage are the same. If I run those status codes one by one I do get the desired results, but, is there a way to perform all this in one single Async test?
From the looks of it, NanoHTTPD is stateful, so you have a race between the .set... calls and the .httpCall.
If you can spin up a new httpService within each Future, then you should be able to parallelize the tests (unless the state in question would be shared across instances, in which case you're likely out of luck).
So you'd have something like (replace Status with the type of status in your code and HTTPService with the type of httpService):
// following code composed on the fly and not run through the compiler...
def spinUpHTTPService(status: Status, body: String): Future[HTTPService] = {
// insert the code outside of the test which creates httpService
httpService.setStatusCode(status)
httpService.setResponseContent(body)
httpService
}
test("Error responses") {
Future.sequence(
NanoHTTPD.Response.Status.values().toList.filter(status => status.getRequestStatus >= 400).map { status =>
spinUpHTTPService(status, s"Request failed with status $status")
.flatMap { httpService =>
val errorMessage = s"Error response (${status.getRequestStatus}) from http service: $responseBody"
recoverToExceptionIf[ServiceException] {
myObject.httpCall("123456")
} map {
ex => assert(ex.getMessage === errorMessage)
}
} // Future.flatMap
} // List.map
).map { assertions => assertions.forAll(_ == Succeeded) }
}
Related
I´m implementing my test framework using scalatest and I think I made a mistake using this framework instead of Cucumber
I´m trying to use some sort of features as Scenario outline of cucumber to avoid break DRY
here my problem
feature("Features of mus client") {
scenario("GET message with mus client") {
Given("a Musin message")
val config: Properties = new Properties
config.put("method", "POST")
config.put("encoding", "UTF-8")
config.put("uri", "http://localhost:9083/musClient")
When("I make a request to f2e")
val response = HttpClientTest.request(config, createJSON(READ))
Then("The message it´s returned successfully")
assert(response != null)
}
scenario("POST message with mus client") {
Given("a Musin message")
val config: Properties = new Properties
config.put("method", "POST")
config.put("encoding", "UTF-8")
config.put("uri", "http://localhost:9083/musClient")
When("I make a request to f2e")
val response = HttpClientTest.request(config, createJSON(CREATE))
Then("The message it´s returned successfully")
assert(response != null)
}
As you can see I have two scenarios where the 99% it´s the same steps but a variable that change the request.
Any idea how to do this elegant and efficient in scalatest
And I'm one of those too who chose scalatest over cucumber, cucumber was too much for me(ME) to write the feature file, and then come back to scala/java file and change accordingly. Maintain two files. I actually played cucumber java, scala cucumber might be more fluent. Anyway, I am liking scalatest so far for all my unit testing, component testing and the flow testing.
In case like yours if the properties are common for multiple scenarios and you won't mutate inside scenarios then defining as common property would be fine, as below.
class E2E extends FeatureSpec with GivenWhenThen {
feature("Features of mus client") {
Given("http config")
val config: Properties = new Properties(){{
put("method", "POST") //you are doing POST in both case by the way
put("encoding", "UTF-8")
put("uri", "http://localhost:9083/musClient")
}}
scenario("GET message with mus client") {
When("I make a request to f2e")
val response = HttpClientTest.request(config, createJSON(READ))
Then("The message it´s returned successfully")
assert(response != null)
}
scenario("POST message with mus client") {
When("I make a request to f2e")
val response = HttpClientTest.request(config, createJSON(CREATE))
Then("The message it´s returned successfully")
assert(response != null)
}
}
}
But, You might also want to use property based testing for the only part that changes, property based check was very fluent and readable in spock framework.
property based check in scalatest would look like below where I am testing for two different input parameters. (you neeed import org.scalatest.prop.TableDrivenPropertyChecks._)
class TestE2E extends FeatureSpec with GivenWhenThen {
val requestResponse =
Table(
("request", "response"),
( "GET", "GET-something"),
( "POST", "POST-something")
)
feature("testMe") {
forAll (requestResponse) { (givenRequestFromTable: String, expectedResponseFromTable: String) =>
scenario("for input " + givenRequestFromTable) {
When("input is " + givenRequestFromTable)
val output = testMe(input = givenRequestFromTable)
Then("responseFromTable has something appended to it")
assert(output == expectedResponseFromTable)
}
}
}
def testMe(input: String) : String = {
input + "-something"
}
}
There would be two scenarios based on two given properties,
And for you, the tests would be something as below with property based, hope there's no compile error :)
import org.scalatest.prop.TableDrivenPropertyChecks._
import org.scalatest.prop.Tables.Table
import org.scalatest.{FeatureSpec, GivenWhenThen}
class PaulWritesSpecs extends FeatureSpec with GivenWhenThen {
val requestResponse =
Table(
("httpMethod", "requestType"),
("GET", READ),
("POST", CREATE))
feature("Features of mus client") {
forAll(requestResponse) { (httpMethod: String, requestType: String) => {
scenario(s"$httpMethod message with mus client") {
Given("http config")
val config: Properties = new Properties() {{
put("method", httpMethod)
put("encoding", "UTF-8")
put("uri", "http://localhost:9083/musClient")
}}
When("I make a request to f2e")
val response = HttpClientTest.request(config, createJSON(requestType))
Then("The message it´s returned successfully")
assert(response != null)
}
}
}
}
}
In the code below every service, implements check trait which returns the boolean.I am new to scala so wanted to know that Is there a way to add some service in here which returns the boolean immediately (response for this service will not be used so it can return true every time) and works in the background for a populating database, while loop completes and return the result to the requester.
requestedChecks.keys.par.foreach(serviceName => {
if(requestedChecks(serviceName) == 1 && checkList.contains(serviceName)) {
data += (serviceName -> initializeService(serviceName).check(mapForService))
}
})
You can do this it's the proposal of #mrfyda which was also my thought:
import play.api.libs.concurrent.Execution.Implicits._
import scala.concurrent.Future
Future {
requestedChecks.keys.par.foreach(serviceName => {
if(requestedChecks(serviceName) == 1 && checkList.contains(serviceName)) {
data += (serviceName -> initializeService(serviceName).check(mapForService))
}
})
}
Ok(view.html.index()) // or whatever you response looks like
Here is my simple routing application:
object Main extends App with SimpleRoutingApp {
implicit val system = ActorSystem("my-system")
startServer(interface = "0.0.0.0", port = System.getenv("PORT").toInt) {
import format.UsageJsonFormat._
import spray.httpx.SprayJsonSupport._
path("") {
get {
complete("OK")
}
} ~
path("meter" / JavaUUID) {
meterUUID => pathEnd {
post {
entity(as[Usage]) {
usage =>
// execute some logic asynchronously
// do not wait for the result
complete("OK")
}
}
}
}
}
}
What I want to achieve is to execute some logic asynchronously in my path directive, do not wait for the result and return immediately HTTP 200 OK.
I am quite new to Scala and spray and wondering if there is any spray way to solve this specific problem. Otherwise I would go into direction of creating Actor for every request and letting it to do the job. Please advice.
There's no special way of handling this in spray: simply fire your async action (a method returning a Future, a message sent to an actor, whatever) and call complete right after.
def doStuffAsync = Future {
// literally anything
}
path("meter" / JavaUUID) { meterUUID =>
pathEnd {
post {
entity(as[Usage]) { usage =>
doStuffAsync()
complete("OK")
}
}
}
}
Conversely, if you need to wait for an async action to complete before sending the response, you can use spray-specific directives for working with Futures or Actors.
Helo,
at the beginning i wold like to apologize for my english :)
akka=2.3.6
spray=1.3.2
scalatest=2.2.1
I encountered strange behavior of teting routes, which asks actors in handleWith directive,
I've route with handleWith directive
pathPrefix("firstPath") {
pathEnd {
get(complete("Hello from this api")) ~
post(handleWith { (data: Data) =>{ println("receiving data")
(dataCalculator ? data).collect {
case Success(_) =>
Right(Created -> "")
case throwable: MyInternalValidatationException =>
Left(BadRequest -> s"""{"${throwable.subject}" : "${throwable.cause}"}""")
}
}})
}
}
and simple actor wchich always responds when receive object Data and has own receive block wrapped in LoggingReceive, so I should see logs when message is receiving by actor
and i test it using (I think simple code)
class SampleStarngeTest extends WordSpec with ThisAppTestBase with OneInstancePerTest
with routeTestingSugar {
val url = "/firstPath/"
implicit val routeTestTimeout = RouteTestTimeout(5 seconds)
def postTest(data: String) = Post(url).withJson(data) ~> routes
"posting" should {
"pass" when {
"data is valid and comes from the identified user" in {
postTest(correctData.copy(createdAt = System.currentTimeMillis()).asJson) ~> check {
print(entity)
status shouldBe Created
}
}
"report is valid and comes from the anonymous" in {
postTest(correctData.copy(createdAt = System.currentTimeMillis(), adid = "anonymous").asJson) ~> check {
status shouldBe Created
}
}
}
}
}
and behavior:
When I run either all tests in package (using Intellij Idea 14 Ultimate) or sbt test I encounter the same results
one execution -> all tests pass
and next one -> not all pass, this which not pass I can see:
1. fail becouse Request was neither completed nor rejected within X seconds ( X up tp 60)
2. system console output from route from line post(handleWith { (data: Data) =>{ println("receiving data"), so code in handleWith was executed
3. ask timeout exception from route code, but not always (among failed tests)
4. no logs from actor LoggingReceive, so actor hasn't chance to respond
5. when I rerun teststhe results are even different from the previous
Is there problem with threading? or test modules, thread blocking inside libraries? or sth else? I've no idea why it isn't work :(
I'm trying to implement "request based" sessions in scalaquery in the play framework. I create a session using scalaquery and attempt to store it in the current http context, as follows:
def withTransaction[A](bp: BodyParser[A])(f: Request[A] => Result): Action[A] = {
Action(bp) {
request =>
val context = Http.Context.current()
val session = createSession()
session.conn.setAutoCommit(false)
context.args.put("scalaquery.session", session)
try {
val result = f(request)
session.conn.commit()
result
}
catch {
case t: Throwable =>
session.conn.rollback()
throw t
}
finally {
session.close()
context.args.remove("scalaquery.session")
}
}
}
then i wrap my actions in my controllers like:
withTransaction(parse.anyContent) {
Action {
//code that produces a result here
}
}
However, it crashes in the following line saying:
val context = Http.Context.current()
[RuntimeException: There is no HTTP Context available from here.]
So, why is the context not available? This code is called directly by the framework, so shouldn't the context be set by the time this code executes? Or am i using the wrong way for accessing the context?
EDIT: The "session" is of type org.scalaquery.session.Session. The reason why i want to set it in the HttpContext is so that the wrapped actions can access it in an "http scoped" fashion, i.e. That each request stores their session separately, yet all services that need a session can find it in a public scope that is separated per request.
I think the problem is you're using the Java API with the Scala controller. Http.Context is only set if you're using the Java controller. Have you considered using the Scala Session API?
Also, another question is, why do you need to store the session in the context? I see you just remove it at the end anyway. If what you need is for the sub-actions to be able to access the session, you could just pass it in the function.
I'm just going to assume session is of type Session
def withTransaction[A](bp: BodyParser[A])(f: Session => Request[A] => Result): Action[A] = {
Action(bp) {
request =>
val session = createSession()
session.conn.setAutoCommit(false)
try {
val result = f(session)(request)
session.conn.commit()
result
}
catch {
case t: Throwable =>
session.conn.rollback()
throw t
}
finally {
session.close()
}
}
}
and your sub-actions would be
withTransaction(parse.anyContent) { session => request =>
//code that produces a result here
}
you don't need to wrap this in Action anymore since it's already wrapped by withTransaction