The scala test code:
import play.api.test._
import scala._
import org.specs2.execute.Result
object ThrowTest extends PlaySpecification {
"throwA" should {
"catch the exception test1" in {
world must throwA[Exception]
}
"catch the exception test2" in {
hello {
world =>
world must throwA[Exception]
}
}
}
def hello(action: (String) => Result) = {
action(world)
}
def world: String = {
throw new Exception("world-exception")
}
}
Why test1 is working as I expected, but test2 is not, which throws the exception to outer and never catch it:
[info] ! catch the exception test2
[error] Exception: world-exception (ThrowTest.scala:26)
[error] database.ThrowTest$.world(ThrowTest.scala:26)
[error] database.ThrowTest$.hello(ThrowTest.scala:22)
[error] database.ThrowTest$$anonfun$1$$anonfun$apply$4.apply(ThrowTest.scala:14)
[error] database.ThrowTest$$anonfun$1$$anonfun$apply$4.apply(ThrowTest.scala:14)
[info] Total for specification ThrowTest
Because for test 2 your exception is thrown from hello before calling action. action is a String => Result and you call it with world which - when evaluated - throws an exception, therefor, all this code:
world =>world must throwA[Exception]
is never executed.
Related
Compiling this code does not result in any warnings about exhaustive pattern matches.
package example
object Hello extends App {
class Problem extends Exception {
override def getCause() : Throwable = new NotImplementedError("I'm not done")
}
handle(new Problem)
def handle(ex: Exception): Unit = {
println("handler called: %s".format(ex))
val result = ex.getCause match {
case null => println("null")
case e : Exception => println("except")
// no warning about the match not being exhaustive
// case t : Throwable => println("throw")
}
}
}
When I run the code I get this stack trace:
scala.MatchError: scala.NotImplementedError: I'm not done (of class scala.NotImplementedError)
at example.Hello$.handle(Hello.scala:13)
at example.Hello$.delayedEndpoint$example$Hello$1(Hello.scala:9)
at example.Hello$delayedInit$body.apply(Hello.scala:3)
at scala.Function0.apply$mcV$sp(Function0.scala:39)
at scala.Function0.apply$mcV$sp$(Function0.scala:39)
at scala.runtime.AbstractFunction0.apply$mcV$sp(AbstractFunction0.scala:17)
at scala.App.$anonfun$main$1(App.scala:76)
at scala.App.$anonfun$main$1$adapted(App.scala:76)
at scala.collection.IterableOnceOps.foreach(IterableOnce.scala:563)
at scala.collection.IterableOnceOps.foreach$(IterableOnce.scala:561)
at scala.collection.AbstractIterable.foreach(Iterable.scala:926)
at scala.App.main(App.scala:76)
at scala.App.main$(App.scala:74)
at example.Hello$.main(Hello.scala:3)
at example.Hello.main(Hello.scala)
If I uncomment out the line case t : Throwable => println("throw"), the program prints throw.
Why isn't a warning printed to the console when I compile this code? Thanks!
I am following this. I have defined JsonFormat for my custom case class as:
object RecipeJsonProtocol extends DefaultJsonProtocol {
implicit val recipeFormat = jsonFormat4(Recipe.apply)
}
...and then used it as following in same file:
import RecipeJsonProtocol._
def postRecipe(recipe: Recipe): Callback = {
val json = recipe.toJson.toString()
Ajax.post(url = "....", data = json).onComplete {
case Success(xhr) => println("....")
case Failure(t) => println("An error has occurred: " + t.getMessage)
}
Callback.empty
}
But this is throwing error as:
[error] Referring to non-existent class spray.json.package$
What's wrong here?
How can I capture the exception stacktrace into a file in Scala? I tried with System.setErr but it didn't work, while Console.withErr can be set only for a code snippet and not for the whole app. Any idea?
I would like to log to a file all the uncaught exceptions of the app
Edit:
A bit more context: I run inside a spark-shell with a jar added in the classpath. What I do is instantiating an object of a class and the first code ran inside the class is the following, which doesn't give me the
expected print
Thread.currentThread().setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
def uncaughtException(t: Thread, e: Throwable): Unit = {
logger.error("exception logged")
println("exception logged")
}
})
throw new Exception("my ex")
but I don't see the print neither in stdout nor in the log file
Just use the Thread.setUncaughtExceptionHandler:
Java uncaught global exception handler
Use Thread.currentThread() if your app is single-threaded to get the current thread.
Edit
object ErrorHandling {
def main(args: Array[String]): Unit = {
Thread.currentThread().setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
def uncaughtException(t: Thread, e: Throwable): Unit = {
// logger.error("exception logged")
println(s"exception logged: $e")
e.printStackTrace()
}
})
throw new Exception(s"boom")
}
}
Should give you something like:
exception logged: java.lang.Exception: boom
java.lang.Exception: boom
at ErrorHandling$.main(ErrorHandling.scala:10)
at ErrorHandling.main(ErrorHandling.scala)
Code below is a simplified version of the real code. We "inherited" the domain model case object FutTest and case class FutTest, which we can't modify. The actual domain models are served from a Database, so I believe the Future approach is valid, but it causes problems which I don't understand.
import org.scalatest.FunSpec
import scala.concurrent.Future
import scala.concurrent.ExecutionContext.Implicits.global
case object FutTest {
def create(sz: Int) = { FutTest(sz) }
}
case class FutTest(size: Int)
class FutureTest extends FunSpec {
def one(v: Int): Future[FutTest] = {
Future { FutTest.create(v) }
}
def two(t: FutTest) = {
Future { FutTest.create(t.size) }
}
def compileError1: Future[FutTest] = {
one(10).map(f => two(f))
}
def compileError2: Future[FutTest] = {
for { o <- one(10) } yield (two(o))
}
}
The error messages:
[INFO] Using incremental compilation
[INFO] Compiling 7 Scala sources and 5 .. target/test-classes...
[ERROR] domain.FutureTest.scala:25: type mismatch;
found : scala.concurrent.Future[domain.FutTest]
required: domain.FutTest
[ERROR] one(10).map(f => two(f))
[ERROR] ^
[ERROR] domain/FutureTest.scala:29: type mismatch;
found : scala.concurrent.Future[domain.FutTest]
required: domain.FutTest
[ERROR] for { o <- one(10) } yield (two(o))
I tried the above code with plain Int instead of FutTest and all is fine. Why is the compiler complaining and how can we solve this without touching the existing domain.
flatMap is what you want.
one(10).flatMap(f => two(f))
or
one(10).flatMap(two)
Using for comprehension,
for { o <- one(10); t <- two(o) } yield t
One() returns a Future and two() also returns a Future so you need to flatMap instead of map. When you map to two(), your result is Future[Future[FutTest]] and needs to be flattened.
Doing
one(10).flatMap(f => two(f))
should do the trick.
I have implemented my own HttpErrorHander in Play Framework 2.4.2 and it functions very well, but now I want to be able to test with "Fake Actions" that intentionally throw Exceptions. I have tried in scalatest and specs2
import play.api.http.HttpErrorHandler
import play.api.mvc._
import play.api.mvc.Results._
import scala.concurrent._
class MyErrorHandler extends HttpErrorHandler {
def onClientError(request: RequestHeader, statusCode: Int, message: String) = {
Future.successful(
Status(statusCode)("A client error occurred: " + message)
)
}
def onServerError(request: RequestHeader, exception: Throwable) = {
Future.successful(
InternalServerError("A server error occurred: " + exception.getMessage)
)
}
}
I tried so far the following tests. I try to debug the code, but I am never entering my methods. The methods of play.api.http.DefaultHttpErrorHandler are neither executed.
object ThrowableControllerSpec extends PlaySpecification with Results {
"Example Page" should {
"throwErrorAction should be valid" in {
val controller = new TestController()
val result: Future[Result] = controller.exceptionAction().apply(FakeRequest())
//val bodyText: String = contentAsString(result)
status(result) mustEqual INTERNAL_SERVER_ERROR
//bodyText must be startingWith "A server error occurred:"
}
}
}
The Action-method in TestController.exceptionAction looks:
def exceptionAction() = Action {
if (true)
throw new Exception("error")
else
Ok("")
}
The second try:
class ApplicationSpec extends Specification {
"Application" should {
"sent 500 on server error" in new WithApplication {
route(FakeRequest(GET, "/exception")) must beSome.which(status(_) == INTERNAL_SERVER_ERROR)
}
}
}
And the route for /exception
GET /exception controllers.TestController.exceptionAction
I also added in application.conf play.http.errorHandler. But as I said, this is working, but I am not able to test it. The test always fails with the Exception given in exceptionAction.
Thank you in advance
If you are using Specs2, try this
await(controller.exceptionAction()(FakeRequest())) must throwA[Throwable] // or any error you want to test against
See these links.
https://twitter.github.io/scala_school/specs.html
http://www.scalatest.org/user_guide/using_assertions (for ScalaTest)
http://www.scalatest.org/getting_started_with_fun_suite
Hope this helps!