Attempting to run a trivial Scalatest on a Spray Route - scala

Ultimately my goal is to write a custom Spray Directive for use by various routes in an existing application. I have so far been unable to write a test for my new directive, so I simplified further until my custom code wasn't even in play. I'm still not passing what I believe to be a trivial test. What am I doing wrong?
import org.scalatest.FlatSpec
import spray.http.StatusCodes
import spray.routing.{HttpService, Directives, Route}
import spray.testkit.ScalatestRouteTest
trait TrivialDirectivesTextFixture extends Directives {
def trivialRoute: Route =
path("test_route") {
get { requestContext =>
println("get")
complete(StatusCodes.OK, "trivial response")
}
}
}
class TrivialDirectivesSpec extends FlatSpec with TrivialDirectivesTextFixture with ScalatestRouteTest with HttpService {
def actorRefFactory = system
"TrivialDirectives" should "trivially match" in {
Get("/test_route") ~> sealRoute(trivialRoute) ~> check {
println("check")
assertResult(StatusCodes.OK) {
println("status " + status)
status
}
assertResult("trivial response") {
println("response " + response)
responseAs[String]
}
}
}
}
The resulting output is:
get
check
[info] TrivialDirectivesSpec:
[info] TrivialDirectives
[info] - should trivially match *** FAILED ***
[info] Request was neither completed nor rejected within 1 second (TrivialDirectivesSpec.scala:30)

I do not have enough point to write a comment
So I'll write it here, the problem is that
get {
complete("OK")
}
is translated to the code below, using some sort of implicit
get { ctx =>
ctx.complete("OK")
}
thus when you do
get { ctx =>
complete("OK")
}
it is not translated properly

Solved by changing complete() to requestContext.complete(). I don't really understand why, so I'd appreciate more comprehensive answers.

Related

ExceptionHandler doesn't work with spray test-kit?

I'm trying Spray's ExceptionHandler using an example in this guide: http://spray.io/documentation/1.2.2/spray-routing/key-concepts/exception-handling/
class MyServiceActor extends Actor with MyService {
def actorRefFactory = context
def receive = runRoute(handleExceptions(myExceptionHandler)(myRoute))
implicit def myExceptionHandler(implicit log: LoggingContext) =
ExceptionHandler {
case e: ArithmeticException =>
requestUri { uri =>
complete(InternalServerError, "Bad numbers, bad result!!!")
}
}
}
I intentionally throw ArithmeticException in the route like this:
trait MyService extends HttpService {
val myRoute =
path("") {
get {
complete {
throw new ArithmeticException("Oops, I failed!")
"Hello World"
}
}
}
}
If I made a request with curl, it returns the error message Bad numbers, bad result!!! correctly. However when testing with Specs2 + spray testkit, it never returns the correct error message, instead it returns default 500 code error message There was an internal server error. Even using sealRoute doesn't help.
"Test" in {
Get() ~> sealRoute(myRoute) ~> check {
println(responseAs[String]) // Always print `There was an internal server error.`
ok
}
}
And on the console, I would see error trace:
[ERROR] [07/07/2016 00:31:24.661] [specs2.DefaultExecutionStrategy-1] [ActorSystem(com-example-MyServiceSpec)] Error during processing of request HttpRequest(GET,http://example.com/,List(),Empty,HTTP/1.1)
java.lang.ArithmeticException: Oops, I failed!
at com.example.MyService$$anonfun$1.apply(MyService.scala:62)
at com.example.MyService$$anonfun$1.apply(MyService.scala:61)
at spray.routing.directives.RouteDirectives$$anonfun$complete$1$$anon$3.apply(RouteDirectives.scala:49)
at spray.routing.directives.RouteDirectives$$anonfun$complete$1$$anon$3.apply(RouteDirectives.scala:48)
at spray.routing.directives.BasicDirectives$$anonfun$mapRequestContext$1$$anonfun$apply$1.apply(BasicDirectives.scala:30)
...
I put a println command in the myExceptionHandler and found out the myExceptionHandler never get executed.
Anyone know why it doesn't work and the solution?
Apparently sealRoute is not enough, because the exception handler is resolved implicitly, as described here: http://spray.io/documentation/1.2.4/spray-testkit/
In your case, MyServiceActor has an exception handler, but in the test case you use MyService/myRoute directly, so the exception handler is not picked up.
This documentation page was useful: http://spray.io/documentation/1.2.4/spray-routing/key-concepts/exception-handling/
The solution is to bring an implicit ExceptionHandler into scope in the test case. So in this example:
"Test" in {
implicit val testExceptionHandler = ExceptionHandler {
case e: ArithmeticException =>
requestUri { uri =>
complete(InternalServerError, "Bad numbers, bad result!!!")
}
}
Get() ~> sealRoute(myRoute) ~> check {
println(responseAs[String])
ok
}
}
It worked, but of course the duplication is not super elegant. Maybe you can access the exception handler from MyServiceActor in your test and reuse production code. I just put testExceptionHandler into a base class all tests inherit from.

Custom spray rejection handler not working properly in tests

I am building some JSON HTTP services using spray and I am having some problems testing a RejectionHandler. If I start the application running the command sbt run and make the request, the RejectionHandler process the MalformedRequestContentRejection as expected but I am getting an IllegalArgumentException when running the tests even with the route sealed. In the other hand, the MethodRejection works fine. The JSON validation is done using require
The next example is based in the spray-template repository branch on_spray-can_1.3_scala-2.11 with a POST endpoint and the new tests. I've made a fork with the entire example here
Notice the use of a case clase for deserialize JSONs, the use of the require method for validation and the declaration of an implicit RejectionHandler.
package com.example
import akka.actor.Actor
import spray.routing._
import spray.http._
import StatusCodes._
import MediaTypes._
import spray.httpx.SprayJsonSupport._
class MyServiceActor extends Actor with MyService {
def actorRefFactory = context
def receive = runRoute(myRoute)
}
case class SomeReq(field: String) {
require(!field.isEmpty, "field can not be empty")
}
object SomeReq {
import spray.json.DefaultJsonProtocol._
implicit val newUserReqFormat = jsonFormat1(SomeReq.apply)
}
trait MyService extends HttpService {
implicit val myRejectionHandler = RejectionHandler {
case MethodRejection(supported) :: _ => complete(MethodNotAllowed, supported.value)
case MalformedRequestContentRejection(message, cause) :: _ => complete(BadRequest, "requirement failed: field can not be empty")
}
val myRoute =
pathEndOrSingleSlash {
post {
entity(as[SomeReq]) { req =>
{
complete(Created, req)
}
}
}
}
}
This is are the test implemented using spray-testkit. The last one expects a BadRequest but the test fails with an IllegarArgumentException.
package com.example
import org.specs2.mutable.Specification
import spray.testkit.Specs2RouteTest
import spray.http._
import StatusCodes._
import spray.httpx.SprayJsonSupport._
class MyServiceSpec extends Specification with Specs2RouteTest with MyService {
def actorRefFactory = system
"MyService" should {
"leave GET requests to other paths unhandled" in {
Get("/kermit") ~> myRoute ~> check {
handled must beFalse
}
}
"return a MethodNotAllowed error for PUT requests to the root path" in {
Put() ~> sealRoute(myRoute) ~> check {
status should be(MethodNotAllowed)
responseAs[String] === "POST"
}
}
"return Created for POST requests to the root path" in {
Post("/", new SomeReq("text")) ~> myRoute ~> check {
status should be(Created)
responseAs[SomeReq] === new SomeReq("text")
}
}
/* Failed test. Throws IllegalArgumentException */
"return BadRequest for POST requests to the root path without field" in {
Post("/", new SomeReq("")) ~> sealRoute(myRoute) ~> check {
status should be(BadRequest)
responseAs[String] === "requirement failed: field can not be empty"
}
}
}
}
I am missing something?
Thanks in advance!
Your SomeReq class is being eagerly instantiated in the Post("/", new SomeReq("")) request builder and the require method is invoked as soon as the class is instantiated.
To get around this try using the following instead:
import spray.json.DefaultJsonProtocol._
Post("/", JsObject("field" → JsString(""))) ~> sealRoute(myRoute) ~> check {
status should be(BadRequest)
responseAs[String] === "requirement failed: field can not be empty"
}

spray.io debugging directives - converting rejection to StatusCodes

I am using logRequestResponse debugging directive in order to log every request/response failing through whole path tree. Log entries looks as follows:
2015-07-29 14:03:13,643 [INFO ] [DataImportServices-akka.actor.default-dispatcher-6] [akka.actor.ActorSystemImpl]ActorSystem(DataImportServices) - get-userr: Response for
Request : HttpRequest(POST,https://localhost:8080/city/v1/transaction/1234,List(Accept-Language: cs, Accept-Encoding: gzip,...
Response: Rejected(List(MalformedRequestContentRejection(Protocol message tag had invalid wire type.,...
My root route trait which assembles all partial routes to one look as follows:
trait RestAPI extends Directives {
this: ServiceActors with Core =>
private implicit val _ = system.dispatcher
val route: Route =
logRequestResponse("log-activity", Logging.InfoLevel) {
new CountryImportServiceApi().route ~
new CityImportServiceApi().route
}
}
And partial routes are defined as following:
class CinemaImportServiceApi()(implicit executionContext: ExecutionContext) extends Directives {
implicit val timeout = Timeout(15 seconds)
val route: Route = {
pathPrefix("city") {
pathPrefix("v1") {
path("transaction" / Segment ) {
(siteId: String, transactionId: String) =>
post {
authenticate(BasicAuth(cityUserPasswordAuthenticator _, realm = "bd city import api")) {
user =>
entity(as[CityTrans]) { e =>
complete {
StatusCodes.OK
}
}
}
}
}
}
}
}
Assembled routes are run via HttpServiceActor runRoute.
I would like to convert rejection to StatusCode and log that via logRequestResponse. Even though I write a custom function for logging I get rejection. What seems to me fishy is that since it is wrapped the whole route tree rejection is still not converted to HttpResponse. In tests we are sealing the route in order to convert Rejection to HttpResponse. Is there a way how to mark a route as a complete route hence actually seal it? Or am I missing some important concept here?
Thx
I would add something like the following:
} ~ // This is the closing brace of the pathPrefix("city"), just add the ~
pathPrefix("") {
complete {
(StatusCodes.OK, "Invalid Route")
}
}
of course, change the OK and the body to whatever you need. pathPrefix("") will match any path that was rejected by any previous routes.

How to test methods that return Future?

I'd like to test a method that returns a Future. My attempts were as follows:
import org.specs2.mutable.Specification
import scala.concurrent.ExecutionContext.Implicits.global
import scala.util.{Failure, Success}
class AsyncWebClientSpec extends Specification{
"WebClient when downloading images" should {
"for a valid link return non-zero content " in {
val testImage = AsyncWebClient.get("https://www.google.cz/images/srpr/logo11ww.png")
testImage.onComplete { res =>
res match {
case Success(image) => image must not have length(0)
case _ =>
}
AsyncWebClient.shutDown
}
}
}
}
Apart from the fact that I am unable to make this code work I guess that there could be a better way of testing a futures with a Future-oriented matcher.
How to do it properly in specs2?
You can use the Matcher.await method to transform a Matcher[T] into a Matcher[Future[T]]:
val testImage: Future[String] =
AsyncWebClient.get("https://www.google.cz/images/srpr/logo11ww.png")
// you must specify size[String] here to help type inference
testImage must not have size[String](0).await
// you can also specify a number of retries and duration between retries
testImage must not have size[String](0).await(retries = 2, timeout = 2.seconds)
// you might also want to check exceptions in case of a failure
testImage must throwAn[Exception].await
Took me awhile to find this so thought I'd share. I should've read the release notes. In specs2 v3.5, it is required to use implicit ExecutionEnv to use await for future. This can also be used for future transformation (i.e. map) see http://notes.implicit.ly/post/116619383574/specs2-3-5.
excerpt from there for quick reference:
import org.specs2.concurrent.ExecutionEnv
class MySpec extends mutable.Specification {
"test of a Scala Future" >> { implicit ee: ExecutionEnv =>
Future(1) must be_>(0).await
}
}
Await is an anti pattern. Shouldn't ever use it.
You can use traits like ScalaFutures, IntegrationPatience, and Eventually.
whenReady does the magic you are looking for.
Example:
import org.specs2.mutable.Specification
import scala.concurrent.ExecutionContext.Implicits.global
import scala.util.{Failure, Success}
import org.scalatest.concurrent.{IntegrationPatience, ScalaFutures}
import scala.concurrent.Future
class AsyncWebClientSpec extends Specification
with ScalaFutures
with IntegrationPatience {
"WebClient when downloading images" should {
"for a valid link return non-zero content " in {
whenReady(Future.successful("Done")){ testImage =>
testImage must be equalTo "Done"
// Do whatever you need
}
}
}
}
There is a nice thing for that in specs2 - implicit await method for Future[Result]. If you take advantage of future transformations you can write like this:
"save notification" in {
notificationDao.saveNotification(notification) map { writeResult =>
writeResult.ok must be equalTo (true)
} await
}
Future composition comes to the rescue when some data arrangement with async functions is needed:
"get user notifications" in {
{
for {
_ <- notificationDao.saveNotifications(user1Notifications)
_ <- notificationDao.saveNotifications(user2Notifications)
foundUser1Notifications <- notificationDao.getNotifications(user1)
} yield {
foundUser1Notifications must be equalTo (user1Notifications)
}
} await
}
Note how we have to use an additional block around for-comprehension to convince compiler. I think it's noisy, so if we turn await method in a function we come up with a nicer syntax:
def awaiting[T]: Future[MatchResult[T]] => Result = { _.await }
"get user notifications" in awaiting {
for {
_ <- notificationDao.saveNotifications(user1Notifications)
_ <- notificationDao.saveNotifications(user2Notifications)
foundUser1Notifications <- notificationDao.getNotifications(user1)
} yield {
foundUser1Notifications must be equalTo (user1Notifications)
}
}
Wondering why #etorreborre did not mention "eventually"
See https://github.com/etorreborre/specs2/blob/master/tests/src/test/scala/org/specs2/matcher/EventuallyMatchersSpec.scala#L10-L43
class EventuallyMatchersSpec extends Specification with FutureMatchers with ExpectationsDescription { section("travis")
addParagraph { """
`eventually` can be used to retry any matcher until a maximum number of times is reached
or until it succeeds.
""" }
"A matcher can match right away with eventually" in {
1 must eventually(be_==(1))
}
"A matcher can match right away with eventually, even if negated" in {
"1" must not (beNull.eventually)
}
"A matcher will be retried automatically until it matches" in {
val iterator = List(1, 2, 3).iterator
iterator.next must be_==(3).eventually
}
"A matcher can work with eventually and be_== but a type annotation is necessary or a be_=== matcher" in {
val option: Option[Int] = Some(3)
option must be_==(Some(3)).eventually
}
onComplete returns Unit, so that block of code returns immediately and the test ends before being able to do anything. In order to properly test the result of a Future, you need to block until it completes. You can do so using Await, and setting a maximum Duration to wait.
import scala.concurrent._
import scala.concurrent.duration._
Await.result(testImage, Duration("10 seconds")) must not have length(0)

How to use "eventually" with "Specs2RouteTest"

There are two specs here. First is not passing because eventually in check will not cause whole route rerun but this is the way I would prefer to follow. The second spec is the best solution I found (and prove that it is doable ;) ) but it contains some boilerplate like additional function which in real life will have to return rather tuple than single thing and it is inconsistent with spray-tests syntax design to test routs.
So question is:
How to use eventually with spray-tests to be as close as possible to syntax from first spec.
import org.specs2.mutable.Specification
import spray.routing.Directives
import spray.http._
import MediaTypes._
import HttpCharsets._
import spray.testkit.Specs2RouteTest
class EventuallyAndRouts extends Specification with Directives with Specs2RouteTest {
var i = 0
def incAndGet = {
i = i + 1
println(s"This is i = $i")
s"$i"
}
"The testing infrastructure should support an eventually matcher" >> {
"but it is not working inside a check as I need :( (and this will fail)" in {
i = 0
Get() ~> complete(incAndGet) ~> check {
body must eventually(7, 20 millis)(be_===(HttpEntity(ContentType(`text/plain`, `UTF-8`), "5")))
}
}
"so I got workaround :/ (and this is passing)" in {
i = 0
def requestResult = Get() ~> complete(incAndGet) ~> check {
body
}
requestResult must eventually(7, 20 millis)(be_===(HttpEntity(ContentType(`text/plain`, `UTF-8`), "5")))
}
}
}
eventually is used to evaluate repeatedly a value that is changing. So it either has to be a var, a def or a byname parameter.
The best way around that is probably for you to implement a checkEventually(times, delay) method that will incorporate the eventually call. Something like that:
implicit class Checked(request: =>Request) {
def checkEventually[R : AsResult](times: Int, delay: Duration, matcher: Matcher[RequestResult])(body: =>Unit)( = {
val requestResult = result ~> check(body)
requestResult must eventually(times, delay)(matcher)
}
}