ExceptionHandler doesn't work with spray test-kit? - scala

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.

Related

scalatest - test a method of Future[S] with fallbackTo

Premise: When my API responds to a request for the User object, I want to try enriching it with the properties of case class PartnerView(id: String, vipStatus: Option[Boolean], latestSession: Option[Timestamp]. Since the database can be unreliable at times, I use fallbackTo to provide the values as optional, thus not displaying them in the User JSON response.
The following implementation seems to work so far (running the request through Postman returns the User JSON without the optional values) yet my unit test would complain as if I had an uncaught Exception.
The Service class:
class Service(repo: Repository) {
def get(id: String): Future[Partner] = {
val account = repo.getAccount(id)
val getLatestSession = repo.getLatestSession(id)
val partnerView = (for {
account <- getAccount
latestStartTime <- getLatestSession.map {
case Some(x) => x.scheduledStartTime
case _ => None
}
} yield PartnerView(partnerId, account.vipStatus, latestStartTime))
.fallbackTo(Future.successful(PartnerView(id, None, None)))
partnerView
}
}
The Repository class:
class Repository(database: DatabaseDef, logger: LoggingAdapter) {
def getAccount(id: String): Future[Account] = database.run((...).result.head)
.recover {
case e: Exception =>
logger.error(e, "DB Server went down")
throw e
}
def getLatestSession(id: String): Future[Option[Session]] = database.run((...).result.headOption)
.recover {
case e: Exception =>
logger.error(e, "DB Server went down")
throw e
}
}
The Unit Test:
class ServiceSpec extends AsyncFlatSpec with AsyncMockFactory with OneInstancePerTest {
val mockRepo = mock[Repository]
val service = new Service(mockRepo)
behaviour of "Service"
it should "get an empty PartnerView when the repository get an Exception" in {
(mockRepository.getAccount _)
.expects("partner")
.throwing(new Exception)
service.get("partner")
.map(partnerView => assert(partnerView.id == "partner" && partnerView.vipStatus.isEmpty))
}
}
The test would fail with the message
Testing started at 5:15 p.m. ...
java.lang.Exception was thrown.
{stacktrace here}
I'm expecting the Exception to
By changing the mock setup to below, the test ran successfully:
it should "get an empty PartnerView when the repository get an Exception" in {
(mockRepository.getAccount _)
.expects("partner")
.returning(Future.failed(new Exception))
...
}
since the recover method wraps the Exception inside a Future
Sources:
recover vs recoverWith
official scala article

How to return early in a pattern match of akka actor receive

Tried googling variations on this trivial question but didn't get an answer...
Basically I have a pattern match in my receive method.
In some cases I want to break early from the receive handling
override def receive = {
case blah => {
... preflight code
if (preflight failed) {
sender() ! errorMSG
"break" or "return" here // get error "method receive has a return statement ; needs result type -
// I tried adding Unit to the receive and return statements
}
... more code
....
if (something happened) {
sender() ! anotherErrorMSG
"break" or "return" here
}
...
}
case foo => {...}
case bar => {...}
} // end receive
See this discussion of return's semantics and remember that receive returns a PartialFunction[Any, Unit] which is then evaluated after receive has returned. In short, there's no way to return early.
Ömer Erden's solution of throwing an exception and using actor supervision works (indeed, exception throwing with all of its overhead is basically the only way to reliably end a computation early), but if you need any state to carry over from message to message, you'll need Akka persistence.
If you don't want to nest if-elses as in chunjef's solution, you can use context.become and stash to create some spaghetti-ish code.
But the best solution may be to have the things that might fail be their own functions with Either result types. Note that the Either API in scala 2.12 is quite a bit nicer than in previous versions.
import scala.util.{ Either, Left, Right }
type ErrorMsg = ...
type PreflightSuccess = ... // contains anything created in preflight that you need later
type MoreCodeSuccess = ... // contains anything created in preflight or morecode that you need later
def preflight(...): Either[ErrorMsg, PreFlightSuccess] = {
... // preflight
if (preflight failed)
Left(errorMsg)
else
Right(...) // create a PreflightSuccess
}
def moreCode1(pfs: PreFlightSuccess): Either[ErrorMsg, MoreCodeSuccess] = {
... // more code
if (something happened)
Left(anotherErrorMSG)
else
Right(...) // create a MoreCodeSuccess
}
def moreCode2(mcs: MoreCodeSuccess): Either[ErrorMsg, Any] = {
... // more code, presumably never fails
Right(...)
}
override def receive = {
case blah =>
val pf = preflight(...)
val result = pf.map(morecode1).joinRight.map(moreCode2).joinRight // only calls morecode1 if preflight succeeded, and only calls morecode2 if preflight and morecode1 succeeded
result.fold(
{ errorMsg => sender ! errorMsg },
()
)
case foo => ...
case bar => ...
}
Whether this is preferable to nested if-else's is a question of taste...
This may not be your question's exact answer but in your case adding supervisor actor would be the better solution. In Akka Supervision model convince you to handle exceptions on supervisor actor instead of sending error messages back to the sender.
This approach brings you a fault-tolerant model and also you can throw exception at any line you want(which solves your current problem), your supervisor actor will handle the throwable with restarting, resuming or stopping the child actor.
please check the link

Test the message returned by an akka actor containing a Try[T]

I am developing an Akka actor that respond with a message of type PutAck[Try[String]]. The problem is not how to develop the actor itself, but the unit tests.
Given that the following actor code
private def put(store: Map[String, Array[Byte]], key: String, value: Array[Byte]) = {
try {
val newStore = store + (Objects.requireNonNull(key) -> value)
sender ! PutAck(Success(key))
context.become(nonEmptyMap(newStore))
} catch {
case ex: Exception =>
sender ! PutAck(Failure(ex))
}
}
I wish to test it with the following test
"A Storekeeper actor" must {
"receive an error message for a couple (null, value)" in {
val sk = TestActorRef[Storekeeper]
sk ! Put(null, SerializationUtils.serialize(42))
expectMsg(PutAck(Failure(new NullPointerException())))
}
}
Unfortunately, the test fails with message
assertion failed: expected PutAck(Failure(java.lang.NullPointerException)), found PutAck(Failure(java.lang.NullPointerException))
I supposed that the failure is due to the differenthashCode that the two exception have.
How can I test this case?
The reason why it does match is that instances of NullPointerExceptions are only equal to themselves, not other seemingly identical instances.
What you can do instead is to expect the message on type, catch the response as a value and then assert whatever you want about it, something like this:
val ack = expectMsgType[PutAck[Try[String]]]
ack.value.isFailure should === (true)
ack.value.failed.get shouldBe a[NullPointerException]

Attempting to run a trivial Scalatest on a Spray Route

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.

Weird try/catch behaviour with Scala + Akka

I'm trying to use Akka to implement a TCP server for a custom application protocol. I'm trying to follow the example given here: http://doc.akka.io/docs/akka/2.0/scala/io.html to do non-blocking IO inside a for...yield loop.
I find that when I throw an exception from inside the yield block, I can't catch it from outside the block. I think I've got a fundamental misunderstanding of how Akka or Scala is working here and I'd appreciate any tips.
I've boiled down the code to this:
import akka.actor._
import java.net.InetSocketAddress
class EchoServer(port: Int) extends Actor {
val state = IO.IterateeRef.Map.async[IO.Handle]()(context.dispatcher)
override def preStart {
IOManager(context.system) listen new InetSocketAddress(port)
}
def receive = {
case IO.NewClient(server) =>
val socket = server.accept()
state(socket) flatMap (_ => EchoServer.processRequest(socket))
case IO.Read(socket, bytes) =>
state(socket)(IO.Chunk(bytes))
case IO.Closed(socket, cause) =>
state(socket)(IO.EOF(None))
state -= socket
}
}
object EchoServer extends App
{
def processRequest(socket: IO.SocketHandle): IO.Iteratee[Unit] =
{
println( "In process request")
try {
for {
bs <- IO take 1
} yield {
println("I'll get here")
throw new Exception("Hey-o!")
println("But not here ... as expected")
}
} catch {
case e: Exception => println("And not here ... wtf?"); IO.Done() // NEVER GETS HERE
}
}
ActorSystem().actorOf(Props(new EchoServer(8080)))
}
Maybe more convenient to follow the gist here: https://gist.github.com/2296554
Can anybody explain why control does not reach my catch block in this situation?
I noticed that if I turn on debug logging in Akka, I see this message in the output:
[DEBUG] [04/03/2012 22:42:25.106] [EchoServerActorSystem-akka.actor.default-dispatcher-1] [Future] Hey-o!
So I guess the exception is getting handled by the Akka dispatcher? Can anybody explain how that's possible?
The point of non-blocking IO is of course that there is no guarantee when and where it is executed. Remember that one can write the for comprehension as
(IO take 1).map(bs => {
println("I'll get here"); throw // ...
}
What does this code do? IO take 1 returns some non-blocking Future-like thing, which is then appended a transforming function through the map method. I.e. whenever (and wherever) IO take 1 is ready, it will apply the map on the result.
All of this happens in some other thread (or using some other way of implementing the non-blocking semantics), so there is no way for the try–catch to react on any Exceptions being thrown. Nor would the bs => println(…) … method know of your exception handling. All it knows it that it should transform some input bs and have a result when it’s finished.
The lesson to be learned: When using non-blocking code avoid side-effects. Especially so, if the side effects are being used to change the flow of execution.
In order to actually catch the exception, I think you’ll have to structure it as follows (untested; see API):
def processRequest(socket: IO.SocketHandle): IO.Iteratee[Unit] =
{
println( "In process request")
(
for {
bs <- IO take 1
} yield {
println("I'll get here")
throw new Exception("Hey-o!")
println("But not here ... as expected")
}
) recover {
case e: Exception => println("And here ...?"); IO.Done()
}
}